Skip to content
Closed
5 changes: 5 additions & 0 deletions .changeset/c4-unified-renderer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mermaid': minor
---

feat(c4): opt-in unified renderer for C4 diagrams via the `c4.useUnifiedRenderer` config flag. C4 diagrams can now be laid out by a real layout algorithm (dagre by default, ELK via `@mermaid-js/layout-elk`) instead of the legacy statement-order grid, with a new `c4-person` shape for Person elements. The legacy renderer remains the default; existing diagrams are unaffected.
5 changes: 5 additions & 0 deletions .changeset/c4-unified-tags.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mermaid': minor
---

feat(c4): style elements and relationships via `AddElementTag` and `AddRelTag` in the C4 unified renderer. Tag styles (`$bgColor`, `$fontColor`, `$borderColor`, `$textColor`, `$lineColor`) are applied after per-type defaults and before `UpdateElementStyle`/`UpdateRelStyle` overrides, and every tag adds a sanitized `c4-tag-<name>` css class for theming.
112 changes: 112 additions & 0 deletions cypress/integration/rendering/c4/c4-unified.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { imgSnapshotTest } from '../../../helpers/util.ts';

const unified = { c4: { useUnifiedRenderer: true } };

describe('C4 diagram (unified renderer)', () => {
it('C4U.1 should render a C4Context diagram with nested boundaries', () => {
imgSnapshotTest(
`
C4Context
title System Context diagram for Internet Banking System

Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")

System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")

Enterprise_Boundary(b1, "BankBoundary") {
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
}
}

BiRel(customerA, SystemAA, "Uses")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
Rel(SystemC, customerA, "Sends e-mails to")
`,
unified
);
});
it('C4U.2 should render element styles applied via UpdateElementStyle and UpdateRelStyle', () => {
imgSnapshotTest(
`
C4Context
Person(customerA, "Banking Customer A", "A customer of the bank.")
System(SystemAA, "Internet Banking System", "Allows customers to make payments.")
Rel(customerA, SystemAA, "Uses")
UpdateElementStyle(customerA, $fontColor="red", $bgColor="grey", $borderColor="red")
UpdateRelStyle(customerA, SystemAA, $textColor="blue", $lineColor="blue")
`,
unified
);
});
it('C4U.3 should render db and queue shape variants', () => {
imgSnapshotTest(
`
C4Container
Person(customer, "Customer", "A customer of the bank.")
Container(spa, "Single-Page App", "JavaScript, Angular", "Provides banking functionality.")
ContainerDb(database, "Database", "Oracle 12c", "Stores user information.")
ContainerQueue(queue, "Queue", "RabbitMQ", "Transaction events.")
Rel(customer, spa, "Uses", "HTTPS")
Rel(spa, database, "Reads from and writes to", "SQL/TCP")
Rel(spa, queue, "Publishes to", "AMQP")
`,
unified
);
});
it('C4U.4 should render a deployment diagram with nested deployment nodes', () => {
imgSnapshotTest(
`
C4Deployment
title Deployment Diagram for Internet Banking System

Deployment_Node(aws, "Amazon Web Services", "Cloud") {
Deployment_Node(ec2, "EC2", "Ubuntu 22.04") {
Container(api, "API Application", "Java, Spring", "Provides banking functionality via API.")
}
Deployment_Node(rds, "RDS", "Oracle 12c") {
ContainerDb(db, "Database", "Oracle", "Stores user information.")
}
}
Rel(api, db, "Reads from and writes to", "SQL/TCP")
`,
unified
);
});
it('C4U.5 should keep the rendered diagram within sane dimensions', () => {
imgSnapshotTest(
`
C4Context
Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
}
BiRel(customerA, SystemAA, "Uses")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
`,
unified
);
cy.get('svg')
.last()
.should((svg) => {
const [, , width, height] = svg.attr('viewBox').split(' ').map(Number);
// Guard against the layout blowup seen in earlier rewrite attempts
expect(width).to.be.lessThan(2200);
expect(height).to.be.lessThan(2200);
});
});
it('C4U.7 should style elements and rels via AddElementTag and AddRelTag', () => {
imgSnapshotTest(
`
C4Context
AddElementTag(deprecated, $bgColor="grey", $fontColor="white", $borderColor="red")
AddRelTag(async, $textColor="green", $lineColor="green")
Person(customerA, "Banking Customer A", "A customer of the bank.")
System(SystemAA, "Internet Banking System", "Allows customers to make payments.", $tags="deprecated")
Rel(customerA, SystemAA, "Uses", $tags="async")
`,
unified
);
});
});
43 changes: 43 additions & 0 deletions docs/syntax/c4.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,49 @@ UpdateRelStyle(customerA, bankA, $offsetY="60")

```

## Unified renderer with automatic layout (v\<MERMAID_RELEASE_VERSION>+)

An experimental opt-in renderer routes C4 diagrams through mermaid's unified
rendering pipeline, replacing the statement-order grid with a real layout
algorithm (dagre by default; ELK via `@mermaid-js/layout-elk` and `layout: elk`).
Person elements are drawn with the classic C4 person notation and nested
boundaries are laid out automatically.

Enable it via configuration:

```yaml
---
config:
c4:
useUnifiedRenderer: true
---
```

When the flag is not set, the legacy renderer is used and existing diagrams
render unchanged. `UpdateLayoutConfig`, `$offsetX` and `$offsetY` only affect
the legacy renderer; element and relationship colors set via
`UpdateElementStyle` and `UpdateRelStyle` are honored by both renderers.

### Tags (v\<MERMAID_RELEASE_VERSION>+)

`AddElementTag(tagName, ?bgColor, ?fontColor, ?borderColor)` and
`AddRelTag(tagName, ?textColor, ?lineColor)` define named styles, following
the C4-PlantUML macros of the same name. Elements and relationships opt in
with `$tags="name"`; combine multiple tags with `+`, e.g.
`$tags="v1.0+deprecated"`. Tag styles override the per-type defaults and are
themselves overridden by `UpdateElementStyle`/`UpdateRelStyle`. Each tag is
also added to the element's CSS classes as `c4-tag-<name>` for custom
styling via `themeCSS` or `classDefs`.

```
C4Context
AddElementTag(deprecated, $bgColor="grey", $borderColor="red")
AddRelTag(async, $textColor="green", $lineColor="green")
Person(customerA, "Banking Customer A")
System(SystemAA, "Internet Banking System", $tags="deprecated")
Rel(customerA, SystemAA, "Uses", $tags="async")
```

## C4 System Context Diagram (C4Context)

```mermaid-example
Expand Down
Loading
Loading