Closed manonmichel closed 1 year ago
Hi! To answer your questions (somewhat out of order):
is there documentation on styling for C4-models using skinparam?
Not really. There is some information scattered here and there in various tickets, but it has not been consolidated into a single source yet.
However I can't wrap my head around how styling works for C4-PlantUML.
I think the main thing to realize is that there is a difference between plain skinparam
entries and using a theme.
Skinparams can be placed in the main diagram or a separate file which is then !include
'd into the main diagram.
Themes are different in how they are build up (for an example, see puml-theme-aws-orange.puml). They should always live in a separate file and are included using and included using: !theme my-theme from /path/or-url/to/my-path
Thus, !include
ing a theme or !theme
ing a skinparams file will not work.
and no styling is applied to it
This can have several causes. First of all, it can be helpful to see what the exact output is, rather than our input.
Using this simplified version:
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/v2.4.0/C4_Container.puml
skinparam {
rectangle<<boundary>> {
BackgroundColor red
}
rectangle<<BOUNDARY_TAG>> {
backgroundcolor blue
}
rectangle<<BOUNDARY_TAG>><<boundary>> {
backgroundcolor lime
}
}
Boundary(ui, "User Interface", $tags='BOUNDARY_TAG') {
Container(dummy, "Dummy Container", "", "desc")
}
@enduml
We get this image:
But if we look at what is actually created[^1], we get this:
This both shows how elements are named (so you can target the correct <<stereotpye>>
) and where styles come from.
As you can see, there is no <<BOUNDARY_TAG>>
(as you might expect) but an <<BOUNDARY_TAG_boundary>>
.
Which leads to my second point: things are not always styled as expected...
As you might ask "If there is no <<BOUNDARY_TAG>>
, then why is the background lime?
This is because PlantUML just sees the rectangle<<boundary>>
and as it is declared last, it wins.
If the existing BOUNDARY_TAG_boundary
were used and the rectangle<<BOUNDARY_TAG_boundary>><<boundary>>
skinparam was placed first it would loose, even though it is more specific / heavily weighed than either rectangle<<BOUNDARY_TAG_boundary>>
or rectangle<<boundary>>
individually.
Also, if there are multiple stereotypes set on an element, the first stereotype declared on the element wins. Not the last declared skinparam!
@startuml
skinparam {
rectangle<<A>> {
backgroundcolor blue
}
rectangle<<B>> {
BackgroundColor red
}
}
rectangle " " <<A>><<B>> as dummy1
rectangle " " <<B>><<A>> as dummy2
@enduml
how would I style a database container?
If we use this:
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/v2.4.0/C4_Container.puml
ContainerDb(db1, "Database")
ContainerDb(db2, "Database", $tags="db-tag")
@enduml
We can see that what is output is:
database "==Database\n//<size:12>[]</size>//" <<container>> as db1
database "==Database\n//<size:12>[]</size>//" <<db-tag>><<container>> as db2
So to style things, you could use either databse<<container>>
(for all database entries), or database<<db-tag>>
(for specific DB types). Obviously, taking the previously mentioned weirdness of order/weight into account.
I'm quite new to UML so maybe I'm missing something obvious.
I don't think any of this can be classified as "obvious". I just hope gives you more understanding of how things work :+1:
[^1]: This information is saved to the meta-data of a generated PNG or as a comment to a generated SVG. The easiest way to see what it is, is by viewing the source of an SVG
Hi @manonmichel,
instead of themes you can define the default and tag styles via the UpdateElementStyle()
, UpdateRelStyle()
, AddElementTag()
, AddRelTag()
... calls.
This has the advantage a) that the legend text is updated too and b) you can ignore some PlantUML specific internals.
And if you combine all style/tag related calls in a separate file than you can include it and use it like a theme, meta model, ... .
e.g. create a DummyDomainModel.puml
, it contains all your style and tag definitions
@startuml
!define osaPuml https://raw.githubusercontent.com/Crashedmind/PlantUML-opensecurityarchitecture2-icons/master
!include osaPuml/Common.puml
!include osaPuml/User/all.puml
!include <office/Servers/database_server>
!include <office/Servers/file_server>
!include <office/Servers/application_server>
!include <office/Concepts/service_application>
!include <office/Concepts/firewall>
AddExternalPersonTag("anonymous_ext", $sprite="osa_user_black_hat", $legendText="anonymous user")
AddPersonTag("customer", $sprite="osa_user_large_group", $legendText="aggregated user")
AddPersonTag("admin", $sprite="osa_user_audit,color=red", $legendSprite="osa_user_audit,scale=0.25,color=red", $legendText="administration user")
AddContainerTag("webApp", $sprite="application_server", $legendText="web app container")
AddContainerTag("db", $sprite="database_server", $legendText="database container")
AddContainerTag("files", $sprite="file_server", $legendText="file server container")
AddContainerTag("conApp", $sprite="service_application", $legendText="console app container")
AddRelTag("firewall", $textColor="$ARROW_COLOR", $lineColor="$ARROW_COLOR", $sprite="firewall,scale=0.3,color=red", $legendText="firewall")
@enduml
This can be included in the diagram itsef
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
' or you could use a file include,...
!include https://.........../DummyDomainModel.puml
Person_Ext(anonymous_user, "Bob", $tags="anonymous_ext")
Person(aggregated_user, "Sam, Ivone", $tags="customer")
Person(administration_user, "Bernd", $tags="admin")
System_Boundary(c1, "techtribes.js"){
Container(web_app, "Web Application", "Java, Spring MVC, Tomcat 7.x", $tags="webApp")
ContainerDb(rel_db, "Relational Database", "MySQL 5.5.x", $tags="db")
Container(filesystem, "File System", "FAT32", $tags="files")
ContainerDb(nosql, "NoSQL Data Store", "MongoDB 2.2.x", $tags="db")
Container(updater, "Updater", "Java 7 Console App", $tags="conApp")
}
Rel(anonymous_user, web_app, "Uses", "HTTPS", $tags="firewall")
Rel(aggregated_user, web_app, "Uses", "HTTPS", $tags="firewall")
Rel(administration_user, web_app, "Uses", "HTTPS", $tags="firewall")
Rel(web_app, rel_db, "Reads from and writes to", "SQL/JDBC, port 3306")
Rel(web_app, filesystem, "Reads from")
Rel(web_app, nosql, "Reads from", "MongoDB wire protocol, port 27017")
Rel_U(updater, rel_db, "Reads from and writes data to", "SQL/JDBC, port 3306")
Rel_U(updater, filesystem, "Writes to")
Rel_U(updater, nosql, "Reads from and writes to", "MongoDB wire protocol, port 27017")
Lay_R(rel_db, filesystem)
SHOW_LEGEND()
@enduml
Would this be an option?
BR Helmut
Hi @manonmichel,
the "Custom schema definition" section in README.md could be the better sample
Instead of a themes file you could define a MyDesign.puml
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
!$COLOR_A_5 = "#7f3b08"
!$COLOR_A_4 = "#b35806"
!$COLOR_A_3 = "#e08214"
!$COLOR_A_2 = "#fdb863"
!$COLOR_A_1 = "#fee0b6"
!$COLOR_NEUTRAL = "#f7f7f7"
!$COLOR_B_1 = "#d8daeb"
!$COLOR_B_2 = "#b2abd2"
!$COLOR_B_3 = "#8073ac"
!$COLOR_B_4 = "#542788"
!$COLOR_B_5 = "#2d004b"
!$COLOR_REL_LINE = "#8073ac"
!$COLOR_REL_TEXT = "#8073ac"
UpdateElementStyle("person", $bgColor=$COLOR_A_5, $fontColor=$COLOR_NEUTRAL, $borderColor=$COLOR_A_1, $shadowing="true", $sprite="person2")
UpdateElementStyle("external_person", $bgColor=$COLOR_B_5, $fontColor=$COLOR_NEUTRAL, $borderColor=$COLOR_B_1, $sprite="person2")
UpdateElementStyle("system", $bgColor=$COLOR_A_4, $fontColor=$COLOR_NEUTRAL, $borderColor=$COLOR_A_2)
UpdateElementStyle("external_system", $bgColor=$COLOR_B_4, $fontColor=$COLOR_NEUTRAL, $borderColor=$COLOR_B_2)
UpdateRelStyle($lineColor=$COLOR_REL_LINE, $textColor=$COLOR_REL_TEXT)
@enduml
and include it in the diagram
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
' or you could use a file include,...
!include https://.........../MyDesign.puml
Person(customer, "Personal Banking Customer")
System(banking_system, "Internet Banking System")
System_Ext(mail_system, "E-mail system")
System_Ext(mainframe, "Mainframe Banking System")
Rel(customer, banking_system, "Uses")
Rel_Back(customer, mail_system, "Sends e-mails to")
Rel_Neighbor(banking_system, mail_system, "Sends e-mails")
Rel(banking_system, mainframe, "Uses")
SHOW_LEGEND()
@enduml
BR Helmut
@kirchsth We really need to get this into a single documentation section somewhere. (Probably together with #221)
With the TOC level 3 PR, we could extend the header Custom schema definition
to something like Custom schema definition - themes "support" in C4-PlantUML
and add the my last issue comment to the readme section (until we have #221)
If the new documentation is written we could also add "how to define a new Element" (maybe in context of a more generic tags based T4-Model) (the topic was discussed in https://github.com/plantuml-stdlib/C4-PlantUML/issues/244#issuecomment-1286713145 but there is a simpler solution for it)
eg. if a new robot system should be defined then it can be done like this. I think it should support all scenarios. (the sample is not useful but I is based on the https://github.com/plantuml-stdlib/C4-PlantUML/issues/127#issuecomment-791380046 and I didn't want to define something new)
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
' ========================
' define a new tag for the new element
' unchanged font color has to defined too that the internal used System base implementation is not displayed in legend
AddElementTag("robot_external", $bgColor=darkred, $fontColor=$ELEMENT_FONT_COLOR, $borderColor=red, $sprite="robot")
' define a new procedure for the new element (new defined tag has to be additional merged)
!unquoted procedure ExternalRobotSystem($alias, $label, $descr="", $sprite="", $tags="", $link="")
!if (%strlen($tags) > 0)
!$combinedTags=$tags+"+robot_external"
!else
!$combinedTags="robot_external"
!endif
System($alias=$alias, $label=$label, $descr=$descr, $sprite=$sprite, $tags=$combinedTags, $link=$link)
!endprocedure
' ========================
' another tag which should be combined
AddElementTag("anotherTag", $bgColor=darkblue)
Person(customer, "Personal Banking Customer")
System(banking_system, "Internet Banking System")
ExternalRobotSystem(mail_system, "E-mail system", $link="https://github.com/plantuml-stdlib/C4-PlantUML/issues/221")
ExternalRobotSystem(mainframe, "Mainframe Banking System", $tags="anotherTag")
Rel(customer, banking_system, "Uses")
Rel_Back(customer, mail_system, "Sends e-mails to")
Rel_Neighbor(banking_system, mail_system, "Sends e-mails")
Rel(banking_system, mainframe, "Uses")
SHOW_LEGEND()
@enduml
This issue has been automatically marked as stale because it has not had activity in the past 60 days. It will be closed in seven days if no further activity occurs. Thank you for your contributions.
HI @manonmichel
I created a PR #295 that you can define themes and change a lot of display related settings (like in the https://github.com/kirchsth/C4-PlantUML/blob/extended/themes/puml-theme-C4_FirstTest.puml).
Can you please see/check there if it works for you.
Thank you and best regards Helmut
I would like to create a PlantUML theme for my C4 model in order to have a seperate file for my design choices and for my diagrams (and also avoid repeating the styling at the beginning of each file). However I can't wrap my head around how styling works for C4-PlantUML.
From this comment I gather that we can also use skinparam (as with regular .puml files) such as
However this doesn't seem to work for me. In
diagram.puml
I set!theme mytheme from path/to/mytheme
and I set the background colour inpuml-theme-mytheme.puml
to check that the theme is well connected to the diagram and it indeed changes the background colour. However I have the following boundary:and no styling is applied to it (I included the above styling in
mytheme
).Furthermore, is there documentation on styling for C4-models using
skinparam
? For example how would I style a database container? Would it beskinparam database<<containerdb>>
?I'm quite new to UML so maybe I'm missing something obvious.