PlantUML layout and styles tutorial

With PlantUML you can create diagrams as plain text code. PlantUML takes off the burden so that you don’t need to worry about the looks and layout of your diagram’s elements, by applying opinionated defaults. This article is both a PlantUML layout tutorial, which explains how you can customize the layout, and also explains how you can customize the styles, influencing how your elements look.

Introduction

PlantUML is a Java-based tool that lets you express diagrams as code. As my introduction article explains, you can use PlantUML for any kind of UML diagram and beyond, including mind maps or Gantt charts.

While PlantUML does the “right thing” most of the time, sometimes you will need to tweak a few settings. Finding the right keywords is a chore at times, which is why I wrote this article to explain tricks for two of the most often needed customization options: layouting and customizing colors and other styling elements.

PlantUML layout tricks

PlantUML takes care of your diagram layout, so that you don’t have to. Unfortunately, the layout produced by PlantUML is (very) sub-optimal in specific cases, or the arrangement of elements is simply not to your taste. This section presents several tricks you can use to tweak the layout. However, the authors of PlantUML advice you to use those sparingly.

Flow and arrow direction

Your diagram consists of one or more sets of connected elements (that is, the different sets themselves are not connected). For instance, imagine a diagram of six nodes (A, B, C, D, E, F) with two connected sets, e.g. set #1 somehow connects nodes A, B, and C with each other, and set #2 somehow connects D, E, and F.

For nodes within such a set, there is a default layout direction PlantUML uses, which is configurable. If you don’t configure anything, this is the top to bottom direction. This is why a statement such as A --> B produces an arrow going from top to bottom. You can change this default by adding the left to right direction statement near the top of your file.

Considering the different, disconnected sets, PlantUML will layout them in the direction perpendicular to the default flow direction (so, by default, from left to right), as illustrated by the following example:


@startuml ' The following statement is the default, you could also ' comment it out: top to bottom direction ' The two following disconnected sets (realized as packages) are arranged left to right package "Disconnected set #1" {   [A]   [B] } package "Disconnected set #2" {   [C]   [D] } package "Disconnected set #3" {   [E]   [F] } A --> B C --> D E --> F @enduml
Code language: plaintext (plaintext)

There are a few subtle notions about defining connections that you should be aware of, so let’s take a look at the syntax:

  • A -> B or A - B (or any other combination where you use a single character to describe the type of line, not counting the arrow tip character) produces an arrow that goes in the direction perpendicular to the default-configured flow. So by default (top to bottom direction), the arrows that you get for something like A - B go from left to right. If you had used left to right direction, the arrows would go from top to bottom.
  • A --> B or A -- B (etc.), i.e. using two or more characters that describe the line type, produces arrows that follow the direction of your main flow exactly. The more characters you add, the longer the arrow will become.
    • Unfortunately, it is not possible to create longer arrows that go in the perpendicular direction of the main flow.
  • A notation such as A -u- B enforces the direction of the arrow (u could be replaced with d, l, or r, or its corresponding long versions: up, down, left or right). The direction-letter (u, …) denotes the direction of the arrow itself (in other words: its target)! For instance, A -up- B means that A is below B!

Flipped directions

If you use the left to right direction command, then explicitly specified directions (such as A -up- B) don’t do what you would expect. PlantUML applies such explicitly specified directions in a first stage, where they are still interpreted in the context of the default top to bottom direction. Only afterwards is the layout (mentally) rotated by 90°. Thus, if you used the left to right direction, and wanted A to be below B, you need to write A -left- B. If you wanted A to be left of B, instead of A -right- B you’d write A -down- B.

Invisible links

To force PlantUML to place a specific element above/below/left of/right of some other element, you can use invisible links. These are like normal connections, but are not drawn. The syntax is A -[hidden]left- B where left can be exchanged with any other direction, or its shortcut (e.g. l for left). The following example illustrates this:

@startuml [a] [b] [c] [d] a - b c - d a -[hidden]d- c @enduml
Code language: plaintext (plaintext)
Without hidden link
With hidden link

As long as the connection’s direction matches the default layout direction (which is the case in the above example, because d=down matches the default top to bottom direction), additional dashes before [hidden] or after the direction identifier, d, will also increase the horizontal/vertical distance.

Together constraints

By wrapping two or more elements in a together {...} block, you basically create an invisible box around those elements. In other words, no other element will come between those elements.

@startuml together { [a] [b] } [c] a -- b a -- c @enduml
Code language: plaintext (plaintext)
With together constraint
Without together constraint

You should also try the example with single dash connectors (-) instead of -- (open example in editor). Observe what happens if you remove the together constraint.

Ratio pragma

Using !pragma ratio x where x is a floating point number, you can influence the width-to-height ratio.
Roughly, x = height/width.
For instance, !pragma ratio 2.0 will produce an image that is roughly twice as high as it is wide. There are two caveats you should be aware of:

  1. The ratio is not exact. For instance, the pixel dimensions of a generated image for !pragma ratio 2.0 will not be exactly twice as high as it is wide – only approximately.
  2. The layout engine will not intelligently rearrange diagram elements to fill up space – it just stretches out your elements, increasing the margin between the elements.

Margins and paddings

For most diagram types (such as deployment, class, …) you can change the margin between elements using skinparam settings:

  • skinparam nodesep x (where x is an integer > 0) will increase the horizontal margin
  • skinparam ranksep x affects the vertical margin

For sequence diagrams, however, the above definitions do not work. Here a skinparam ParticipantPadding x will increase the horizontal distance between actors/participants (your life lines). I am not aware of any mechanism to increase the vertical distance between messages.

With skinparam padding x (x also being an integer > 0) you will increase the padding of every kind of element, so use it carefully, with small values.

Customizing colors and PlantUML styles

PlantUML offers three mechanisms to customize colors and other styling-related things, such as margins, sizes, etc. These are:

  • Creole/HTML (docs): lets you build tables, bullet lists and trees, and style text placed inside your boxes, e.g. affecting their color, size, style (underline), etc. The docs give a good overview of how this mechanism works.
  • Skin parameters (docs): lets you define styling-related settings, most notably font name/color/type/size, background/border color, and a few other settings.
  • Style sheets (docs): a new, still work-in-progress approach for achieving the same effects as the skin parameters.

The authors of PlantUML plan to replace skin parameters with the style sheet mechanism in the future. I personally like the style sheet approach better anyway. Let’s take a closer look at these three approaches!

Creole/HTML

You can create (blocks of) text that is part of an element (or connection) which is styled in various ways, influencing its color, size, font, etc. This can be done by using specific HTML-like tags (such as <color:red> or <b>) or the so-called Creole syntax, which is a light-weight markup language also used in other systems, slightly extended by PlantUML-specific syntax. You can of course also mix Creole and HTML. The official docs will serve you fine to understand the basics.

Skin parameters

The official docs already hint at the capabilities of skin parameters. Unfortunately, those docs are quite incomplete. There is another external page that lists all skin parameters and provides example images to clarify what each setting means. However, not only is that latter page also outdated (some skin parameters are missing – the list of really all skin parameters is available here), but it misses many example images.

Ultimately, you have to go through the list of skin parameters and see which ones you need, digging through the different sources of documentation. Here are two interesting ones that caught my eye.

First, there is a Handwritten or monochrome look, which helps you achieve an “unfinished” kind of look, which is useful for preliminary, work-in-progress diagrams.

@startuml ' Uncomment one (or both) of the following lines ' skinparam handwritten true ' skinparam monochrome true class Object class String extends Object class Date extends Object @enduml
Code language: plaintext (plaintext)
Handwritten look
Monochrome look

If you don’t like the default curved routing of connections in more complex diagrams, you can customize the linetype skinparam with values polyline or ortho, to customize how connections are routed:

@startuml 'skinparam linetype polyline 'skinparam linetype ortho [a] [b] [c] [d] [e] a --> e b --> c c --> d c -l-> e [rdf] [qq] b -> rdf rdf -d-> a c - qq qq -d- rdf @enduml
Code language: plaintext (plaintext)
Polyline line type
Ortho line type

Style sheets

Style sheets are extremely poorly documented in the official docs. I recommend looking at the plantuml.skin file and the post written on May 5, 2020 in this forum thread, to get started via examples.

Style sheets have a number of advantages over skin parameters:

  • Style definition can be placed in a separate css file, referenced via <style file=MyOwnFile.css>
  • Improved readability (over skin parameters), especially for people already familiar with CSS
  • There are dynamic selectors (such as the depth selector described in the docs)
  • Easier scoping as shown in the code below:
<style> ' scope to sequenceDiagram elements sequenceDiagram { ' scope to actor elements actor { FontColor Blue } } </style>
Code language: plaintext (plaintext)

Coloring only specific objects

Sometimes you want to apply a certain style only to a specific instance of an element, but not to other instances. For example, you might want a very specific arrow to be bold or have a different color, or want to highlight a few specific classes of your class diagram with a different background color.

To achieve this, you should first skim over the docs of the specific diagram type (e.g. class) that you are building, to find out what kinds of inline styling options there are. For instance, the class diagram docs mention that you can use a notation such as A -- B #line:red;text:red : "link text" to connect class A with B via a red line with red label text. As you can see, such a notation lets you modify a specific element of the diagram by putting the concrete styling inline, right next to it.

However, often the aspect you want to style is not available in such an inline way. For instance, how would you set the background color of a specific class? The trick is to use custom stereotypes for this, which are similar to stereotypes you might know from, say, class diagrams (e.g. <<uses>>). The general idea works like this:

  1. You create a style that only applies to a specific stereotype (whose name you choose). You can use either the skin parameter or the style sheets mechanism
  2. You assign the stereotype to one or more of your elements in your diagram
  3. (Optional) you tell PlantUML to hide that stereotype’s label, because you basically “abused” the stereotype mechanism (which assigns semantics to an element) for visual purposes.

The following concrete example illustrates how to perform these steps:

@startuml ' 1. Create the style for our self-named stereotype, "myStyle" ' Either using skin parameters (note there is no space before <<) ... skinparam ClassBackgroundColor<<myStyle>> LightGreen ' ... or using style sheets (commented out) /'<style> .myStyle { BackgroundColor LightGreen } </style>'/ class A {     +int foo } ' 2. Assign the stereotype to one of your elements class B <<myStyle>> {     +int bar } A - B  ' 3. Optionally hide the stereotype hide <<myStyle>> stereotype @enduml
Code language: plaintext (plaintext)

Conclusion

PlantUML is a tool that has grown over time and now has a huge amount of features. Not all of them are easy to understand, which is also due to the lacking documentation. While this sounds dire, it is not really a problem in practice. This article provided a lot of hints. With practice these thing will become effortless. I recommend that you build your own cheat sheet for syntax that solves specific problems (e.g. for layouting or colorization), to make you more effective in the long run. I’ve discussed cheat sheets in detail in this article.

2 thoughts on “PlantUML layout and styles tutorial”

  1. Is there a way to connect some structured documentation e.g. json and components in one diagram?
    And BTW, any hints on line breaks in the json?

    Reply
    • I suppose this highly depends on the generator you would normally apply to that structured documentation (which generates human-readable formats, like HTML). After all, structured documentation is not really consumable for humans, as it is not visually appealing. This kind of generator could be modified to also render PlantUML files.
      Line breaks in JSON string fields are just represented with \n.

      Reply

Leave a Comment