Style Sheets#

Since Gaphor 2.0, diagrams can have a different look by means of style sheets. Style sheets use the Cascading Style Sheets (CSS) syntax. CSS is used to describe the presentation of a document written in a markup language, and is most commonly used with HTML for web pages.

On the W3C CSS home page, CSS is described as:

Cascading Style Sheets (CSS) is a simple mechanism for adding style (e.g., fonts, colors, spacing) to Web documents.

Its application goes well beyond web documents, though. Gaphor uses CSS to provide style elements to items in diagrams. CSS allows us, users of Gaphor, to change the visual appearance of our diagrams. Color and line styles can be changed to make it easier to read the diagrams.

Since we’re dealing with a diagram, and not a HTML document, some CSS features have been left out.

The style is part of the model, so everyone working on a model will have the same style. To edit the style press the tools page button at the top right corner in gaphor:

Button to access style code

Here is a simple example of how to change the background color of a class:

class {
  background-color: beige;
}
background beige

Or change the color of a component, only when it’s nested in a node:

node component {
  background-color: skyblue;
}
nested component

The diagram itself is also expressed as a CSS node. It’s pretty easy to define a “dark” style:

diagram {
  background-color: #343131;
}

* {
  color: white;
  text-color: white;
}
dark-style

Here you already see the first custom attribute: text-color. This property allows you to control the color of the text drawn in an item. color is used for the lines (strokes) that make the layout of a diagram item.

Supported selectors#

Since we are dealing with diagrams and models, we do not need all the features of CSS. Below you’ll find a summary of all CSS features supported by Gaphor.

*

All items on the diagram, including the diagram itself.

node component

Any component item which is a descendant of a node.

node > component

A component item which is a child of a node.

generalization[subject]

A generalization item with a subject present.

class[name=Foo]

A class with name “Foo”.

diagram[name^=draft]

A diagram with a name starting with “draft”.

diagram[name$=draft]

A diagram with a name ends with “draft”.

diagram[name*=draft]

A diagram with a name containing the text “draft”.

diagram[name~=draft item]

A diagram with a name of “draft” or “item”.

diagram[name|=draft]

A diagram with a name is “draft” or starts with “draft-“.

:focus

The focused item. Other pseudo classes are:

  • :active selected items

  • :hover for the item under the mouse

  • :drop if an item is dragged and can be dropped on this item

  • :disabled if an element is grayed out during handle movement

:empty

A node containing no child nodes in the diagram.

:root

Refers to the diagram itself.

This is only applicable for the diagram

:first-child

A node is the first element among a group of sibling.

:has()

The item contains any of the provided selectors.

E.g. node:has(component): a node containing a component item.

:is()

Match any of the provided selectors.

E.g. :is(node, subsystem) > component: a node or subsystem.

:not()

Negate the selector.

E.g. :not([subject]): Any item that has no “subject”.

::after

Provide extra content after a text. Only the content property is supported.

  • The official specification of CSS3 attribute selectors.

  • Gaphor provides the |= attribute selector for the sake of completeness. It’s probably not very useful in this context, though.

  • Please note that Gaphor CSS does not support IDs for diagram items, so the CSS syntax for IDs (#some-id) is not used. Also, class syntax (.some-class) is not supported currently.

Style properties#

Gaphor supports a subset of CSS properties and some Gaphor specific properties. The style sheet interpreter is relatively straight forward. All widths, heights, and sizes are measured in pixels. You can’t use complex style declarations, like the font property in HTML/CSS which can contain font family, size, weight.

Some properties are inherited from the parent style. The parent often is a diagram. When you set a color`` or a font-familyondiagram`, it will propagate down to the items contained in the diagram.

Colors#

background-color

Examples:

background-color: azure;

background-color: rgb(255, 255, 255);

background-color: hsl(130, 95%, 10%);

color

Color used for lines. (inherited)

text-color

Color for text. (inherited)

Deprecated since version 2.23.0: Use color if possible.

opacity

Color opacity factor (0.0 - 1.0), applied to all colors.

  • A color can be any CSS3 color code, as described in the CSS documentation. Gaphor supports all color notations: rgb(), rgba(), hsl(), hsla(), Hex code (#ffffff) and color names.

Text and fonts#

font-family

A single font name (e.g. sans, serif, courier). (inherited)

font-size

An absolute size (e.g. 14) or a size value (e.g. small). (inherited)

font-style

Either normal or italic. (inherited)

font-weight

Either normal or bold. (inherited)

text-align

Either left, center, right. (inherited)

text-decoration

Either none or underline.

vertical-align

Vertical alignment for text.

Either top, middle or bottom.

vertical-spacing

Set vertical spacing for icon-like items (actors, start state).

Example: vertical-spacing: 4.

white-space

Change the line wrapping behavior for text. (inherited)

  • font-family can be only one font name, not a list of (fallback) names, as is used for HTML.

  • font-size can be a number or CSS absolute-size values. Only the values x-small, small, medium, large and x-large are supported.

Drawing and spacing#

border-radius

Radius for rectangles: border-radius: 4.

dash-style

Style for dashed lines: dash-style: 7 5.

justify-content

Content alignment for boxes.

Either start, end, center or stretch.

line-style

Either normal or sloppy [factor].

line-width

Set the width for lines: line-width: 2. (inherited)

min-height

Set minimal height for an item: min-height: 50.

min-width

Set minimal width for an item: min-width: 100.

padding

CSS style padding (top, right, bottom, left).

Example: padding: 3 4.

  • padding is defined by integers in the range of 1 to 4. No unit (px, pt, em) needs to be used. All values are in pixel distance.

  • dash-style is a list of numbers (line, gap, line, gap, …)

  • line-style only has an effect when defined on a diagram. A sloppiness factor can be provided in the range of -2 to 2.

Pseudo elements#

Currently, only the ::after pseudo element is supported.

content

Extra content to be shown after a text.

Variables#

Since Gaphor 2.16.0 you can use CSS variables in your style sheets.

This allows you to define often used values in a more generic way. Think of things like line dash style and colors.

The var() function has some limitations:

  • Values can’t have a default value.

  • Variables can’t have a variable as their value.

Example:

diagram {
  --bg-color: whitesmoke;
  background-color: var(--bg-color);
}

diagram[diagramType=sd] {
  --bg-color: rgb(200, 200, 255);
}

All diagrams have a white background. Sequence diagrams get a blue-ish background.

Media queries#

Gaphor supports dark and light mode since 2.16.0. Dark and light color schemes are exclusively used for on-screen editing. When exporting images, only the default color scheme is applied. Color schemes can be defined with @media queries. The official prefers-color-scheme = dark query is supported, as well as a more convenient dark-mode.

/* The background you see in exported diagrams: */
diagram {
  background-color: transparent;
}

/* Use a slightly grey background in the editor: */
@media light-mode {
  diagram {
    background-color: #e1e1e1;
  }
}

/* And anthracite a slightly grey background in the editor: */
@media dark-mode {
  diagram {
    background-color: #393D47;
  }
}

Diagram styles#

Only a few properties can be defined on a diagram, namely background-color and line-style. You define the diagram style separately from the diagram item styles. That way it’s possible to set the background color for diagrams specifically. The line style can be the normal straight lines, or a more playful “sloppy” style. For the sloppy style an optional wobliness factor can be provided to set the level of line wobbliness. 0.5 is default, 0.0 is a straight line. The value should be between -2.0 and 2.0. Values between 0.0 and 0.5 make for a subtle effect.

CSS model elements#

Gaphor has many model elements. How can you find out which item should be styled?

Gaphor only styles the elements that are in the model, so you should be explicit on their names. For example: Component inherits from Class in the UML model, but changing a color for Class does not change it for Component.

If you hover over a button the toolbox (bottom-left section), a popup will appear with the item’s name and a shortcut. As a general rule, you can use the component name, glued together as the name in the stylesheet. A Component can be addressed as component, Use Case as usecase. The name matching is case insensitive. CSS names are written in lower case by default.

However, since the CSS element names are derived from names used within Gaphor, there are a few exceptions.

Profile

Group

Element

CSS element

*

*

element name

element name without spaces

E.g. class, usecase.

UML

Classes

all Association’s

association

UML

Components

Device/Node

node

UML

Actions

Decision/Merge Node

decisionnode

UML

Actions

Fork/Join Node

forknode

UML

Actions

Swimlane

partition

UML

Interactions

Reflexive message

message

UML

States

Initial Pseudostate

pseudostate

UML

States

History Pseudostate

pseudostate

UML

Profiles

Metaclass

class

C4 Model

C4 Model

Person

c4person

C4 Model

C4 Model

Software System

c4container[type="Software System"]

C4 Model

C4 Model

Component

c4container[type="Component"]

C4 Model

C4 Model

Container

c4container[type="Container"]

C4 Model

C4 Model

Container: Database

c4database

SysML

Blocks

ValueType

datatype

SysML

Blocks

Primitive

datatype

SysML

Requirements

Derive Requirement

derivedreq

RAAML

FTA

any AND/OR/… Gate

and, or, etc.

Ideas#

Here are some ideas that go just beyond changing a color or a font. With the following examples we dig in to Gaphor’s model structure to reveal more information to the users.

To create your own expression you may want to use the Console (open menu → Tools → Console). Drop us a line on Gitter and we would be happy to help you.

The drafts package#

All diagrams in the package “Drafts” should be drawn using sloppy lines:

diagram[owner.name=drafts] {
  line-style: sloppy 0.3;
}

diagram[owner.name=drafts] * {
  font-family: Purisa; /* Or use some other font that's installed on your system */
}
draft style

Unconnected relationships#

All items on a diagram that are not backed by a model element, should be drawn in a dark red color. This can be used to spot not-so-well connected relationships, such as Generalization, Implementation, and Dependency. These items will only be backed by a model element once you connect both line ends. This rule will exclude simple elements, like lines and boxes, which will never have a backing model element.

:not(:is(:root, line, box, ellipse, commentline))[subject=""] {
  color: firebrick;
}
unconnected relationship

Solid Control Flow lines#

In Gaphor, Control Flow lines follow the SysML styling: dashed. If you want, or need to strictly follow the official UML specifications, you can simply make those solid lines.

controlflow {
  dash-style: 0;
}
control flow

Todo note highlight#

All comments beginning with the phrase “todo” can be highlighted in a different user-specific colour. This can be used to make yourself aware that you have to do some additional work to finalize the diagram.

comment[body^="TODO"] {
  background-color: skyblue;
}
highlighted todo note

Emphesize abstract classes and operations#

It may be that the italic font used is not distinguishable enough to differentiate between concrete and abstract classes or operations. To make this work we check if the isAbstract attribute is set on the element:

:is(name, operation)[isabstract]::after {
  content: " {abstract}"
}
emphasize abstract elements

System Style Sheet#

/* Gaphor diagram style sheet */

* {
  --opaque-background-color: white;
  background-color: transparent;
}

:drop {
  color: #1a5fb4;
  line-width: 3;
}

:disabled {
  opacity: 0.5;
}

@media light-mode {
  * {
    --opaque-background-color: #fafafa;
  }
}

@media dark-mode {
  * {
    --opaque-background-color: #242424;
    color: white;
  }

  :drop {
    color: #62a0ea;
  }
}

:root {
  color: black;
  font-family: sans;
  font-size: 14 ;
  line-width: 2;
  padding: 0;
}

:root > pentagon {
  line-width: 1;
  background-color: var(--opaque-background-color);
}

:root > pentagon > diagramtype {
  font-weight: bold;
  padding: 4 0 4 4;
}

:root > pentagon > name {
  padding: 4;
}

/* Relationships */

commentline,
dependency,
interfacerealization,
include,
extend,
packageimport,
lifetime {
  dash-style: 7 5;
}

dependency[on_folded_interface = true],
interfacerealization[on_folded_interface = true] {
  dash-style: 0;
}

/* General */

comment {
  text-align: left;
  vertical-align: top;
  padding: 4 16 4 4;
}

comment body {
  padding: 0;
}

diagram > icon {
  padding: 4;
  border-radius: 4;
}

diagram > type {
  font-weight: bold;
}

metadata {
  justify-content: stretch;
  text-align: left;
}

metadata cell {
  padding: 4;
}

metadata heading {
  font-weight: bold;
  font-style: normal;
  font-size: small;
}

pentagon {
  padding: 4;
  justify-content: start;
}

/* UML */

controlflow {
  dash-style: 9 3;
}

objectnode > icon {
  padding: 4 12;
}

decisionnode > type {
  font-size: small;
}

proxyport > icon,
activityparameternode,
executionspecification {
  background-color: var(--opaque-background-color);
}

partition {
  padding: 4 12 4 12;
  justify-content: stretch;
}

package {
  padding: 24 12 4 12;
}

interaction {
  justify-content: start;
}

activity {
  padding: 4 12;
  border-radius: 20;
  justify-content: start;
}

activityparameternode {
  padding: 4 12;
  min-width: 120;
  text-align: center;
}

action,
valuespecificationaction {
  padding: 4 12;
  border-radius: 15;
}

callbehavioraction {
  padding: 4 24 4 12;
  border-radius: 15;
}

sendsignalaction {
  padding: 4 24 4 12;
}

accepteventaction {
  padding: 4 12 4 24;
}

usecase {
  padding: 4;
}

swimlane {
  min-width: 150;
  padding: 4 12 4 12;
  justify-content: start;
  white-space: normal;
}

association > end {
  font-size: x-small;
  padding: 4;
}

/* SysML */

requirement {
  justify-content: start;
}

requirement text {
  white-space: normal;
}

directedrelationshippropertypath {
  dash-style: 7 5;
}

/* Classifiers */

compartment:first-child {
  padding: 12 4;
}

compartment + compartment {
  padding: 4;
  min-height: 8;
  text-align: left;
  justify-content: start;
  white-space: nowrap;
}

artifact compartment:first-child,
component compartment:first-child {
  padding: 12 24 12 4;
}

state compartment:first-child {
  padding: 4;
}

:has(compartment + compartment),
:has(regions),
:not([children=""]):has(compartment),
:not([children=""]) > compartment {
  justify-content: start;
}

regions {
  justify-content: stretch;
}

region {
  padding: 4;
  min-height: 100;
  justify-content: start;
  text-align: left;
}

region + region {
  dash-style: 7 3;
}

and name,
xor name,
intermediateevent name,
dormantevent name,
basicevent name,
houseevent name,
topevent name,
inhibit name,
conditionalevent name,
zeroevent name,
or name,
not name,
transferin name,
transferout name,
undevelopedevent name,
seq name,
majorityvote name,
unsafecontrolaction name,
operationalsituation name,
controlaction name,
interfaceblock name,
block name,
property name,
requirement name,
c4person name,
c4database name,
c4container name,
package name,
enumeration name,
interface name,
class name,
datatype name,
component name,
statemachine name,
usecase name,
actor name,
artifact name,
node name {
  font-weight: bold;
}

name[isabstract] {
  font-style: italic;
}

from {
  font-size: x-small;
}

interaction > pentagon,
activity > :is(name, stereotypes) {
  text-align: left;
}

compartment heading {
  padding: 0 0 4 0;
  font-size: x-small;
  font-style: italic;
  text-align: center;
}

operation[isabstract] {
  font-style: italic;
}

attribute[isstatic],
operation[isstatic] {
  text-decoration: underline;
}

property:not([aggregation="composite"]) {
  dash-style: 7 5;
}

/* Attached */

:has(icon)[connected_side] {
  text-align: right;
  vertical-align: top;
}

:has(icon)[connected_side="left"] {
  text-align: left;
}

:has(icon)[connected_side="bottom"] {
  vertical-align: bottom;
}

/* C4 model */

c4container, c4person {
  padding: 4 4 4 4;
}

c4database {
  padding: 20 4 4 4;
}

:is(c4container, c4database, c4person):not([children=""]) {
  justify-content: end;
}

:is(c4container, c4database, c4person):not([children=""]) > :is(name, technology) {
  text-align: left;
}

:is(c4container, c4database, c4person) technology {
  font-size: x-small;
}

:is(c4container, c4database, c4person) description {
  padding: 4 4 0 4;
}