Skip to content

feat(c4): Structurizr-style visuals for the unified renderer - #7865

Closed
filipsajdak wants to merge 26 commits into
mermaid-js:developfrom
filipsajdak:feature/7492_c4-structurizr-visuals
Closed

filipsajdak wants to merge 26 commits into
mermaid-js:developfrom
filipsajdak:feature/7492_c4-structurizr-visuals

Conversation

@filipsajdak

Copy link
Copy Markdown
Collaborator

Summary

Brings the unified-renderer C4 output in line with the official c4model.com / Structurizr look:

  • Shape vocabulary beyond rectangles: a c4-person figure (head + body + arms, matched to the c4model.com reference proportions), plus folder, bucket, terminal and browser shape handlers, resolved from $shape/$sprite.
  • Outline style: white fill, coloured border + text, refined geometry and rounder corners (the c4model.com outline look), with drop shadows.
  • Typography: Structurizr stereotype/name/technology/description layout.
  • Boundaries & relationships: boundary styling, redundant-stereotype hiding on system/container boundaries, and straight relationship lines with the label centred on the line (labelpos: 'c').

All behind c4.useUnifiedRenderer; legacy renderer unchanged.

Stacked on #7842 (opt-in unified renderer) - draft until #7842 merges; the diff carries #7842's commits until then. Tracked in #7849.

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 45ee69d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
mermaid Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for mermaid-js ready!

Name Link
🔨 Latest commit 45ee69d
🔍 Latest deploy log https://app.netlify.com/projects/mermaid-js/deploys/6a34ee94a2c22e0008bdf21e
😎 Deploy Preview https://deploy-preview-7865--mermaid-js.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added the Type: Enhancement New feature or request label Jun 16, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@mermaid-js/examples

npm i https://pkg.pr.new/@mermaid-js/examples@7865

mermaid

npm i https://pkg.pr.new/mermaid@7865

@mermaid-js/layout-elk

npm i https://pkg.pr.new/@mermaid-js/layout-elk@7865

@mermaid-js/layout-tidy-tree

npm i https://pkg.pr.new/@mermaid-js/layout-tidy-tree@7865

@mermaid-js/mermaid-zenuml

npm i https://pkg.pr.new/@mermaid-js/mermaid-zenuml@7865

@mermaid-js/parser

npm i https://pkg.pr.new/@mermaid-js/parser@7865

@mermaid-js/tiny

npm i https://pkg.pr.new/@mermaid-js/tiny@7865

commit: 45ee69d

@argos-ci

argos-ci Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ⚠️ Changes detected (Review) 91 changed, 75 added Jun 19, 2026, 8:58 AM

@codecov

codecov Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.61088% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.88%. Comparing base (625ab8b) to head (45ee69d).
⚠️ Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
packages/mermaid/src/diagrams/c4/c4LayoutData.ts 86.53% 9 Missing and 5 partials ⚠️
...ages/mermaid/src/diagrams/c4/c4Renderer-unified.ts 77.77% 0 Missing and 4 partials ⚠️
...dering-util/rendering-elements/shapes/c4Browser.ts 87.50% 0 Missing and 3 partials ⚠️
...ndering-util/rendering-elements/shapes/c4Folder.ts 85.00% 1 Missing and 2 partials ⚠️
...ndering-util/rendering-elements/shapes/c4Person.ts 89.28% 0 Missing and 3 partials ⚠️
...ering-util/rendering-elements/shapes/c4Terminal.ts 85.00% 0 Missing and 3 partials ⚠️
...ndering-util/rendering-elements/shapes/c4Bucket.ts 90.47% 0 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #7865      +/-   ##
===========================================
+ Coverage    77.79%   77.88%   +0.08%     
===========================================
  Files          409      416       +7     
  Lines        30328    30567     +239     
  Branches      5175     5207      +32     
===========================================
+ Hits         23595    23806     +211     
- Misses        5410     5418       +8     
- Partials      1323     1343      +20     
Flag Coverage Δ
e2e 82.01% <86.61%> (+0.05%) ⬆️
unit 24.36% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/mermaid/src/diagrams/c4/c4Diagram.ts 100.00% <100.00%> (ø)
packages/mermaid/src/diagrams/c4/styles.js 100.00% <ø> (ø)
...id/src/rendering-util/rendering-elements/shapes.ts 100.00% <ø> (ø)
...ndering-util/rendering-elements/shapes/c4Bucket.ts 90.47% <90.47%> (ø)
...dering-util/rendering-elements/shapes/c4Browser.ts 87.50% <87.50%> (ø)
...ndering-util/rendering-elements/shapes/c4Folder.ts 85.00% <85.00%> (ø)
...ndering-util/rendering-elements/shapes/c4Person.ts 89.28% <89.28%> (ø)
...ering-util/rendering-elements/shapes/c4Terminal.ts 85.00% <85.00%> (ø)
...ages/mermaid/src/diagrams/c4/c4Renderer-unified.ts 77.77% <77.77%> (ø)
packages/mermaid/src/diagrams/c4/c4LayoutData.ts 86.53% <86.53%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Heads-up: following @aloisklink's review on #7842, Track 1 has switched from the opt-in-flag approach to an in-place migration sliced by rendering layer (shapes -> edges -> layout) - see the updated plan in #7849.

This slice is paused for now. Its branch is preserved as an archive tag (archive/2026-06-17/... on the fork) and its work is reused: it will be re-sequenced as a feature slice on top of the migrated (now-default) unified renderer rather than stacked on an opt-in flag. Keeping it open as a draft for reference; not abandoned.

@filipsajdak
filipsajdak force-pushed the feature/7492_c4-structurizr-visuals branch from 4c86941 to faaaf20 Compare June 18, 2026 22:42
filipsajdak and others added 19 commits June 19, 2026 09:22
Converts the legacy C4 db state (c4ShapeArray / boundaries / rels) into
the unified renderer LayoutData format: elements become nodes (db
variants use cylinder, queue variants use h-cyl), boundaries become
isGroup clusters with parentId nesting, and rels become edges.
UpdateElementStyle and UpdateRelStyle map onto cssStyles/style/labelStyle
preserving legacy semantics. Default fills come from the existing c4
config color keys so the palette matches the legacy renderer.

Kept in a separate file so the legacy c4Db keeps its exact shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds an opt-in renderer for C4 diagrams that goes through the unified
rendering pipeline (dagre by default, other algorithms such as ELK via
registerLayoutLoaders), replacing the legacy statement-order grid with a
real layout algorithm. The legacy row-based renderer stays the default,
so existing diagrams render byte-identical unless the flag is set.

- c4Renderer-unified.ts: renders getData() output via render()
- c4Diagram.ts: dispatches between legacy and unified renderer
- styles.js: CSS for the unified path (nodes, dashed boundary
  clusters, edges, edge labels, title), theme-variable driven
- config.schema.yaml: new c4.useUnifiedRenderer flag (default false)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Renders C4 Person and Person_Ext elements with the classic C4 notation
(circular head over a rounded box body) instead of a plain rectangle.
The shape is registered in the shared shape registry as c4-person so
other diagrams can reuse it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers nested boundaries, UpdateElementStyle/UpdateRelStyle, db and
queue shape variants, nested deployment nodes, and guards the rendered
viewBox dimensions against layout blowups.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rowser)

Adds four custom shape handlers for the unified C4 renderer, modeled on
the existing c4Person shape: a tabbed folder, an open-top object-storage
bucket, a server-side terminal box with a console glyph, and a browser
window with a chrome bar. Registers them in the shapes registry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the type-only shape mapping with a resolver that honors an
explicit $shape (via UpdateElementStyle) or $sprite keyword, then a
recognised $tags token, before falling back to the element type. The
default element box is now a rounded rectangle to match the Structurizr
look.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Applies the diagram drop-shadow filter to C4 element shapes for the
Structurizr look. The shadow is skipped for the hand-drawn look and for
elements that opt out with $shadowing="false" (rendered as a
c4-no-shadow class). Rounded corners come from the rounded default box
and the new shape handlers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Renders the element name in bold, then a muted [Stereotype] or
[Stereotype: Technology] line (Software System, Container, Component,
Person), then a smaller description, matching the c4model.com layout.
Boundary type lines reuse the same muted styling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Relationships are dashed with opaque rounded label backgrounds, and
boundaries use a lighter dashed stroke with a start-anchored label, to
match the c4model.com look.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
System_Boundary/Container_Boundary store an uppercase type (SYSTEM,
CONTAINER); compare case-insensitively so the implicit boundary type is
not shown as a redundant [SYSTEM] label.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a cypress rendering case exercising the Structurizr shape variants
(browser, terminal, bucket, folder) and documents the new shapes,
styling, and the $shape/$sprite attributes. Includes a changeset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render elements as outlines (light fill, colored border and matching
colored text) instead of saturated fills with white text, and drop the
shadow, to match c4model.com. The palette color becomes the element's
border+text identity, clamped dark enough to stay legible on the light
fill. Relationship labels sit on a clean white background.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Person now has a circular head above a rounded body; the folder uses a
clean rectangular tab; the terminal glyph and browser chrome dots take
the element's identity color so they are visible on the light fill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Draw the person's body first and the full head circle on top of it, so
the circle reads as resting on the rectangle. Apply the configured
c4ShapePadding (default 20) to every element so labels keep clear of the
borders.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Size the person head to 60% of the body width (as on c4model.com) and
round all C4 element corners more generously (12px), keeping the browser
address-bar hint square.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Head radius 0.23x the body width, overlapping the body by 0.27x the head
radius, with body corners rounded to 0.177x the width (set inline so it
overrides the shared corner radius).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
filipsajdak and others added 7 commits June 19, 2026 09:23
Draw two vertical arms inside the lower body (at +/-0.29x the width,
spanning 0.45-0.97x the body height) in a lighter tint of the element
color, matching the c4model.com person.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render the person's arms as the text color at 40% opacity and move them
to the lower body (mid-height to the bottom edge), matching the
c4model.com person.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render relationship labels smaller (0.85em, as on c4model.com) on an
opaque background, and add rank spacing, so neighbouring relationship
labels stay separate and legible instead of overlapping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use a linear edge curve for C4 relationships so the line passes through
the layout's label point (placing the label on the line instead of
beside it), and to match the straight relationships on c4model.com.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Labels were anchored on dagre's label-node position, which can sit beside
the drawn line. Add an opt-in edge property centerLabelOnLine that makes
positionEdgeLabel anchor on the line's true midpoint (calcLabelPosition),
and set it on C4 relationships so the label centers on the line. Other
diagrams are unaffected (the flag defaults off).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous centerLabelOnLine edge flag had no effect: dagre's
buildLayoutGraph only keeps whitelisted edge attributes, so the custom
property was stripped before reaching positionEdgeLabel. dagre's default
labelpos 'r' is what offsets the label to the side of the edge. Set
labelpos 'c' on C4 edges (a whitelisted attribute) so dagre places the
label node on the edge and the label centers on the line. Reverts the
ineffective flag, keeping the shared edge type and renderer untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The shape handlers (c4-folder/bucket/terminal/browser) register new entries in shapeDefs, so the generated shapes table in the docs snapshot now lists them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@filipsajdak
filipsajdak force-pushed the feature/7492_c4-structurizr-visuals branch from faaaf20 to 45ee69d Compare June 19, 2026 07:24
@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Closing this PR. It was part of the original opt-in feature-slice approach for the C4 unified renderer. Following @aloisklink's review on #7842, that approach was superseded by an in-place migration sliced by rendering layer (shapes -> edges -> layout), tracked in #7849 (#7842 shapes, #7883 edges, layout PR to follow). This feature will be re-introduced on top of the migrated renderer once the layout slice lands.

The branch is preserved as an archive tag (archive/2026-06-17/*) for reference, so nothing is lost. Closing to keep the open-PR set small and avoid unnecessary CI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant