Elements that can be styled
Here is a simplified list of some of the element types, especially those that have built-in "default" styles.
This will need to be expanded to show the proper hierarchy, and which elements are supported for styling.
Element | in which diagram type ? | Hardcoded styles used |
---|---|---|
Actor |
SequenceDiagram |
|
Stereotype of Actor |
SequenceDiagram |
|
Boundary |
SequenceDiagram |
|
Stereotype of Boundary |
SequenceDiagram |
|
messages |
SequenceDiagram |
|
Actor |
ComponentDiagram |
|
Stereotype of Actor |
ComponentDiagram |
|
Boundary |
ComponentDiagram |
|
Stereotype of Boundary |
ComponentDiagram |
|
Title |
SequenceDiagram |
|
Title |
ComponentDiagram |
|
Title |
ClassDiagram |
|
Footer |
SequenceDiagram |
|
Footer |
ComponentDiagram |
|
Footer |
ClassDiagram |
|
(We use classDiagram
because later we will need class
which will only apply to actual UML class
entities.)
Note that Title, Footer, and Caption are not real UML elements, and will be dealt with differently.
Targeting Specific Diagram Element
We have to define how we apply style so a specific element of a UML diagram (for example, only for participant Bob or for a specific message between Alice and Bob).
To me, some style can be defined, but the style definition should not mention the specific element (otherwise it means that the style definition is specific to one diagram).
I know that this is different from what CSS allows.
Option 3
In Option 3 proposal, styles cannot inherit from each other. However, each element can have several styles (like in CSS) and there are a mecanism based on priority to solve multiple inheritance issues in property value.
Let’s first focus on some elements (actor, boundary which can be used in several diagram type).
If you want use blue color for text everywhere:
Internally, there is a "plantuml.skin" file stored in the plantuml.jar library that store all settings (so no more hardcoded value in the code). The previous example only overrides the fontcolor of the root style.
So now, imagine that you want all UML elements (so not for title, footer…) in green:
The order of declaration is important: since "element" is defined after "root", the priority of green setting is higher than the priority of blue setting.
Now, you want also all actor printed in red:
@startuml
skinparam useBetaStyle true
title Styling example
actor Alice
Alice -> Bob : hello
@enduml
@startuml
skinparam useBetaStyle true
title Styling example
actor Alice
Alice -> Bob : hello
@enduml
Now, you want also sequence diagrams printed in purple:
@startuml
skinparam useBetaStyle true
title Styling example
' In that case, actor from sequence diagrams will be in purple, because purple is defined after red.
actor Alice
Alice -> Bob : hello
@enduml
@startuml
skinparam useBetaStyle true
title Styling example
' In that case, actor from sequence diagrams will be in purple, because purple is defined after red.
actor Alice
Alice -> Bob : hello
@enduml
@startuml
skinparam useBetaStyle true
title Styling example
actor Alice
Alice -> Bob : hello
@enduml
Changing the order definition will only change the color of actor in sequence diagram:
@startuml
skinparam useBetaStyle true
title Styling example
' In that case, actor from sequence diagrams will be in red, because red is defined after purple.
actor Alice
Alice -> Bob : hello
@enduml
@startuml
skinparam useBetaStyle true
title Styling example
' In that case, actor from sequence diagrams will be in red, because red is defined after purple.
actor Alice
Alice -> Bob : hello
@enduml
Finally, there is also a way to change a setting for a element which have two or more precise styles:
@startuml
skinparam useBetaStyle true
title Styling example
' In that case, only actor from sequence diagram are printed in blue.
' Actors in other diagram and other sequence element are unchanged.
actor Alice
Alice -> Bob : hello
@enduml
@startuml
skinparam useBetaStyle true
title Styling example
' In that case, only actor from sequence diagram are printed in blue.
' Actors in other diagram and other sequence element are unchanged.
actor Alice
Alice -> Bob : hello
@enduml
You can also defined your own style. In that case, they could be used when needed with the stererotype notation :
@startuml <style> actor { Padding 10 } MorePadding { Image <&check> ImagePosition BeforeText ' or AfterText ' <img:> convention also supported fontSize 24 Size 32 Padding 20 } message { fontSize 24 } SmallFont { fontSize 10 } </style> participant Bob actor Alice <<MorePadding>> 'In that case Alice with use the style "root, element, sequence, actor, MorePadding" participant Sally Bob->Alice: Hello Alice->Sally: Also Hello! <<SmallFont>> ' This is new: you can use stereotype on message @enduml
Here is another example:
@startuml
participant Bob
actor Alice
participant Sally
Bob->Alice: Hello <>
Alice->Sally: Also Hello!
@enduml
@startuml
participant Bob
actor Alice
participant Sally
Bob->Alice: Hello <>
Alice->Sally: Also Hello!
@enduml
Unfortunately here, you have to manually set <<BobAndAliceMessage>>
.
Varying style
There is another limitation of current skinparam features: the parameter are defined once along the diagram and you cannot change values across the diagram.
The idea here is to allow a style to be different in some context (for example a package) or to change over the execution of the diagram.
For example:
@startuml
skinparam useBetaStyle true
style message {
FontColor blue
}
Alice -> Bob : this is printed in blue
style message {
FontColor red
}
Alice -> Bob : this is printed in red
@enduml
Or in some usecase diagram:
@startuml style actor { FontColor blue } 'foo1 is printed in blue actor foo1 package myPackage { ' Style modification in this package are local style actor { FontColor red } 'foo2 is printed in red actor foo2 } ' We left the package, so we're back to previous style definition 'foo3 is also printed in blue actor foo3 @enduml
Mixing style and stereotype
Styles and stereotypes are going to be very close notions.
Stereotypes could be defined as style to change colors, font… The only difference is that a stereotype is printed on diagrams using standard UML notation while a style is never printed on diagrams. So styles and stereotypes affect rendering in the same way.
For example, you can have:
@startuml
skinparam useBetaStyle true
stereotype foo1 {
FontColor green
}
style dummy1 {
FontColor red
}
participant Alice <>
participant Bob <>
@enduml
Alice is going to be printed in green and Bob in red. However, <<foo1>>
is going to be printed on the diagram while you won’t see any dummy1
string.
Now, since stereotype are printed, you can change stereotype colors using a style named stereotype
@startuml
skinparam useBetaStyle true
stereotype foo1 {
FontColor red
}
style stereotype {
FontColor green
}
participant Bob <>
@enduml
So Bob
is going to be printed in red and <<dummy1>>
is going to be printed in green.
Now you can also do complex stuff:
@startuml
skinparam useBetaStyle true
style dummy1 {
FontColor purple
FontStyle bold
BackgroundColor white
}
stereotype foo3 {
FontColor blue
FontStyle bold
}
style stereotype {
FontColor green
FontSize 8
}
style stereotype+foo3 {
FontSize 24
FontColor red
}
participant Bob <>
actor Alice <>
actor Charlie <>
actor David <>
@enduml
Potential Use Extensions
[KJW]Have style Parameters equivalent to Skin Parameters applied conditionally
!function formatConnection($sequenceArrowThickness, $from, $to, $protocol) <style:sequenceArrowThickness $sequenceArrowThickness> $from<<->>$to : $protocol <style:sequenceArrowThickness 1> !endfunction
<code>box "AWS " #LightBlue
participant "aPlatform" as ap
end box</code>
<code>box "External" #Lightgreen
participant "EXternal API" as extapi participant ""EXternal Service" as exts
end box</code>
<code>formatConnection("2", "ap", "extapi","<< MA-TLS >>")</code>
OR
<code>ap<<→>extapi : <style:sequenceArrowThickness 2> << MA-TLS >></code>
Result is only this sequence line is 2 the rest are the default 1
plantuml.skin file
This is the default file used by PlantUML. Users will be able to modify this file or to create their own foo.skin file.
style root { FontName SansSerif HyperLinkColor red FontColor black FontSize 14 FontStyle plain HorizontalAlignment left RoundCorner 0 DiagonalCorner 0 LineThickness 1.0 LineColor #A80036 BackGroundColor #FEFECE Shadowing 0.0 } style stereotype { FontStyle italic } style title { HorizontalAlignment center FontSize 14 FontStyle bold Padding 0 Margin 4 LineColor none BackGroundColor none } style header { HorizontalAlignment center FontSize 10 FontColor #888888 } style footer { HorizontalAlignment left FontSize 10 FontColor #888888 } style legend { LineColor black BackGroundColor #DDDDDD FontSize 14 RoundCorner 15 Padding 6 Margin 8 } style caption { HorizontalAlignment center FontSize 14 Padding 0 Margin 1 LineColor none BackGroundColor none } style element { Shadowing 4.0 } style sequenceDiagram { } style classDiagram { } style activityDiagram { } style group { BackGroundColor none LineColor black LineThickness 2.0 FontSize 11 FontStyle bold } style groupHeader { BackGroundColor #EEEEEE LineColor black FontSize 13 FontStyle bold } style lifeLine { BackGroundColor none } style destroy { } style reference { LineColor red FontSize 10 FontStyle bold FontColor blue BackGroundColor gold HorizontalAlignment right } style box { BackGroundColor #DDDDDD FontSize 13 FontStyle bold } style separator { LineColor black LineThickness 2.0 BackGroundColor #EEEEEE FontSize 13 FontStyle bold } style delay { FontSize 22 FontStyle italic } style participant { LineThickness 1.5 } style actor { LineThickness 2.0 } style boundary { } style control { } style entity { } style queue { } style database { } style collections { } style swimlane { } style diamond { } style arrow { FontSize 13 } style note { FontSize 13 BackGroundColor #FBFB77 } style partition { } style circle { }
debug.skin file
This file is used as an alternative possible skin file for debugging purpose. It is integrated into beta version.
style root { FontName SansSerif HyperLinkColor red FontColor green FontSize 19 FontStyle plain HorizontalAlignment left RoundCorner 15 DiagonalCorner 0 LineColor #3600A8 LineThickness 1.0 BackGroundColor #AAA Shadowing 0.0 } style stereotype { FontColor blue FontSize 8 FontStyle bold } style title { HorizontalAlignment right FontSize 24 FontColor blue } style header { HorizontalAlignment center FontSize 26 FontColor purple } style footer { HorizontalAlignment left FontSize 28 FontColor red } style legend { FontSize 30 BackGroundColor yellow Margin 30 Padding 50 } style caption { FontSize 32 } style element { BackGroundColor #CEFEFE } style sequenceDiagram { } style classDiagram { } style activityDiagram { } style group { LineThickness 3.5 BackGroundColor MistyRose LineColor DarkOrange FontSize 12 FontStyle italic FontColor red } style groupHeader { BackGroundColor tan LineThickness 0.5 LineColor yellow FontSize 18 FontStyle bold FontColor blue } style lifeLine { BackGroundColor gold } style destroy { LineColor red } style reference { LineColor red FontSize 10 FontStyle bold FontColor blue BackGroundColor gold HorizontalAlignment right } style box { LineThickness 0.1 LineColor FireBrick BackGroundColor PowderBlue FontSize 12 FontStyle italic FontColor Maroon } style separator { LineColor red BackGroundColor green FontSize 16 FontStyle bold FontColor white } style delay { FontSize 22 FontStyle italic } style participant { LineThickness 2.5 } style actor { LineThickness 0.5 } style boundary { LineThickness 1.5 } style control { LineThickness 1.5 } style entity { LineThickness 1.5 } style queue { LineThickness 1.5 } style database { LineThickness 1.5 } style collections { LineThickness 1.5 } style arrow { FontSize 13 LineColor Lime } style note { BackGroundColor GoldenRod } style diamond { } style swimlane { } style activity { BackgroundColor #33668E BorderColor #33668E FontColor #888 FontName arial } style activityDiagram && diamond { BackgroundColor #dae4f1 BorderColor #33668E FontColor red FontName arial FontSize 5 } style activityDiagram && arrow { FontColor gold FontName arial FontSize 15 } style activityDiagram && partition { LineColor red FontColor green RoundCorner 30 } style activityDiagram && note { FontColor Blue LineColor yellow } style circle { BackgroundColor yellow }
Legacy discussions
We keep here some previous discussions. We do not delete them because they contain some interesting ideas. Maybe we are going to reuse them.
First style proposal
A first idea is to use the same notion as CSS (cascading style sheet). The cascading feature may be useful to avoid duplication in skinparam/style feature. All elements will have one default (predefined) style, which may inherit for another default style. Users will be able either:
-
to create a new style and to apply it to some element
-
to modify any predefined style.
Let’s start by an simple example:
@startuml actor Alice boundary Bob Alice -> Bob : hello @enduml
By default, all elements uses the root
style, so you can change the font name of all text of this diagram with:
Let’s say we have the following style hierarchy:
@startuml
title Style hierarchy cascade
skinparam ranksep 30
hide circle
hide empty members
class root
class element extends root
class actor extends element
class boundary extends element
class message extends root
@enduml
@startuml style root { fontName Arial } style element { fontName Courier fontColor Red } syle actor { fontSize 14 } style boundary { fontColor Blue } actor Alice boundary Bob Alice -> Bob : hello @enduml
[RG] — If the format of CSS is followed, the above example could be extended in a similar fashion for targeting specific elements perhaps:
@startuml style root { fontName Arial } style element { fontName Courier fontColor Red } style actor.Bob, boundary.Alice { fontSize 14 } style message { fontSize 24 } style message.Bob:Alice { BackgroundColor Black } actor Alice boundary Bob Alice -> Bob : hello @enduml
The syntax for selecting the entity could take many forms. I’ve used :
for simplicity. Implementing a full descendant selection matching like CSS is more powerful, but also likely more complex, i.e. ancestor > descendant
. It seems most scenarios involve two entities at most, so a simple matching pair may be sufficient, with some minor additional flexibility for directional allowances. To continue the example above, it might look like:
@startuml style message { fontSize 24 } style message.Bob:Alice { BackgroundColor Black } style message.Bob>Alice { BackgroundColor Blue } actor Alice boundary Bob Alice -> Bob : hello 'fontSize 24 && BackgroundColor Black Bob o-> Alice : hi 'fontSize 24 && BackgroundColor Black && BackgroundColor Blue 'BackgroundColor would resolve as standard cascade to last defined value, "Blue" 'or precedence possibly, i.e. a specific relationship ">" is higher order than '"any" relationship ":" @enduml
It would nice to be mindful of some other outstanding requests that would like to see some form of metadata follow through into SVG output, for postprocessing with other tools. For example, I have opened the SVG in Sketch and used a tool to locate elements of a given name or type to make changes, which currently is quite hard. To incorporate that in a basic fashion:
[AR] Agreed on the need for SVG ouput. I propose to use exportedName
property for this purpose
@startuml style message { styleName "DefaultMessage" 'default value, not required but processed by system if no styleName found? See example below for 'counter argument fontSize 24 } style message.Bob:Alice { styleName "BobAndAliceMessage" ' provided by user. If not provided by user, perhaps system generate in output with basic ' convention like CamelCase to hyphens: Bob-Alice-Message ' But I think it's acceptable to simply state "if user wants a value here, they should supply it" fontSize 10 } participant Bob actor Alice participant Sally Bob->Alice: Hello Alice->Sally: Also Hello! @enduml
I think the main drawback of doing the selectors in the style
section is the dynamic nature of PlantUML, where new entities may be defined on the fly. But in many ways this is no different than HTML/CSS. To take a slightly different tact, focusing on style application happening in the UML itself:
[AR] Here, I don’t like the idea of style
targeting individual element of a specific diagram. According to me, it prevent the reuse of some existing style
file several diagrams.
@startuml style actor { Padding 10 } style actor { styleName "MorePadding" Image <&check> ImagePosition BeforeText ' or AfterText ' <img:> convention also supported fontSize 24 Size 32 Padding 20 } style message { styleName "DefaultMessage" fontSize 24 } style message { styleName "SmallFont" fontSize 10 } participant Bob actor Alice <style:"MorePadding"> ' or actor <style:"MorePadding"> Alice ' or !style "MorePadding" participant Sally Bob->Alice: Hello Alice->Sally: <style:"SmallFont"> Also Hello! @enduml
It may be a personal choice, but I feel the cleaner the UML markup itself, the better. For example, to prevent having something like a Swimlane title, that must be repeated such as <&check><size:24><c:blue> \nSome\nName
. With Macros or Variables this can be easier, but still is another layer of mental abstraction. None of this prevents doing that of course, to keep compatibility as is mentioned.
[AR] agreed on the cleaner the UML markup itseft, the better.
Targeting Specific Diagram types
It is important meet the goal of reusing a "style set", i.e. putting many definitions in one file for many diagram types. The common example of this is a "Design System", so that a common style may be followed across different diagrams.
Following the initial example, a convention could be followed such as:
would result in hierarchy:
@startuml
title Style hierarchy cascade per type Option1
skinparam ranksep 30
hide circle
hide empty members
class root
class message extends root
class element extends root
class actor extends element
class "SequenceDiagram.message" extends message
class "SequenceDiagram.actor" extends actor
@enduml
or alternately:
@startuml
title Style hierarchy cascade per type Option2
skinparam ranksep 30
hide circle
hide empty members
class root
class message extends root
class actor extends root
class SequenceDiagram extends root
class SequenceDiagram.message extends SequenceDiagram
class SequenceDiagram.actor extends SequenceDiagram
@enduml
It would seem Option1 makes more sense in an inheritance/parsing sense. Option2 would likely result in challenges in traversing/cascading the styles. I also note that this conflicts with my syntax above for style assignment (reusing the .
operator), so perhaps one or the other may need to change if it was difficult to manage. Perhaps: style message.Bob:Alice in SequenceDiagram
or style SequenceDiagram.message(Bob:Alice)
or even style SequenceDiagram.message.Bob:Alice
.
[AR] I fully agree on the need (except for targeting indivual element), and I will propose an Option3.