Skip to content

fix(c4): allow boundaries as relationship endpoints (#4864) - #7874

Merged
pbrolin47 merged 1 commit into
mermaid-js:developfrom
filipsajdak:bug/4864_c4-boundary-rel
Aug 18, 2026
Merged

pbrolin47 merged 1 commit into
mermaid-js:developfrom
filipsajdak:bug/4864_c4-boundary-rel

Conversation

@filipsajdak

@filipsajdak filipsajdak commented Jun 17, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A Rel (or BiRel/Rel_Back etc.) that referenced a boundary alias threw
C4 rel "<from>" -> "<to>" references an unknown shape, because the renderer's endpoint
lookup (getC4Shape) only searched the shape array and never the boundaries array.

Relating to/from a boundary is valid C4 (e.g. a system inside a boundary talking to an
external boundary), so this widens getC4Shape to fall back to boundaries. Boundaries already
carry the x/y/width/height geometry the renderer uses to compute relationship intersection
points (they are populated by drawInsideBoundary/drawBoundary before drawRels runs), so
no further changes to the geometry math are needed.

Resolves #4864.

Changes

  • c4Db.ts: getC4Shape resolves boundary aliases as a fallback after shapes.
  • c4Types.ts: new C4Element = C4Shape | C4Boundary alias (shapes and boundaries share the
    relationship-endpoint geometry) - avoids any type assertions.
  • c4Renderer.ts: widen getIntersectPoint/getIntersectPoints/drawRels endpoint types to
    C4Element.
  • Regression test in parser/c4Boundary.spec.ts.

Test

  • pnpm vitest run packages/mermaid/src/diagrams/c4 - 127 passing (incl. the new test).
  • Verified the previous behaviour threw references an unknown shape and now renders.

🎉 Fixes C4 relationship endpoints so boundary aliases resolve correctly. This removes the “references an unknown shape” error for Rel, BiRel, and Rel_* aliases.

🎉 Updates getC4Shape to resolve both shapes and boundaries. Generalizes renderer intersection logic with the shared C4Element type and adds boundary geometry support.

🎉 Adds database and Cypress regression coverage for relationships that target boundary aliases. Includes a changeset entry.

@changeset-bot

changeset-bot Bot commented Jun 17, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b79518

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

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

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 17, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for mermaid-js ready!

Name Link
🔨 Latest commit 6b79518
🔍 Latest deploy log https://app.netlify.com/projects/mermaid-js/deploys/6a84130f41cd9022f293c7e3
😎 Deploy Preview https://deploy-preview-7874--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: Bug / Error Something isn't working or is incorrect label Jun 17, 2026
@pkg-pr-new

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

Copy link
Copy Markdown

Open in StackBlitz

@mermaid-js/examples

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

mermaid

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

@mermaid-js/layout-elk

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

@mermaid-js/layout-tidy-tree

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

@mermaid-js/mermaid-zenuml

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

@mermaid-js/parser

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

@mermaid-js/tiny

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

commit: 6b79518

@codecov

codecov Bot commented Jun 17, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 44.44444% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.56%. Comparing base (3d521b1) to head (6b79518).
⚠️ Report is 54 commits behind head on develop.

Files with missing lines Patch % Lines
packages/mermaid/src/diagrams/c4/c4Renderer.ts 33.33% 10 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #7874      +/-   ##
===========================================
- Coverage    77.61%   77.56%   -0.05%     
===========================================
  Files          568      572       +4     
  Lines        74992    75290     +298     
  Branches     12690    12721      +31     
===========================================
+ Hits         58202    58402     +200     
- Misses       15796    15884      +88     
- Partials       994     1004      +10     
Flag Coverage Δ
e2e 70.71% <83.33%> (+0.15%) ⬆️
unit 74.79% <38.88%> (-0.26%) ⬇️

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/c4Db.ts 63.40% <100.00%> (+0.10%) ⬆️
packages/mermaid/src/diagrams/c4/c4Renderer.ts 80.73% <33.33%> (-1.35%) ⬇️

... and 13 files 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.

@argos-ci

argos-ci Bot commented Jun 17, 2026 •

Copy link
Copy Markdown

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

Build Status Details Updated (UTC)
default (Inspect) 👍 Approved by pbrolin47 1 changed Aug 11, 2026, 2:41 PM

@filipsajdak
filipsajdak force-pushed the bug/4864_c4-boundary-rel branch 2 times, most recently from 7877c20 to a332d00 Compare June 19, 2026 07:23
@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR enables C4 relationships to target boundary aliases. Alias lookup resolves shapes or boundaries, renderer endpoint geometry supports both types, and regression coverage validates parsing and rendering.

Changes

C4 boundary relation endpoint fix

Layer / File(s) Summary
Boundary alias resolution and type contract
packages/mermaid/src/diagrams/c4/c4Db.ts, packages/mermaid/src/diagrams/c4/c4Types.ts
getC4Shape falls back to boundaries, and C4Element represents shape or boundary endpoints.
Renderer intersection helpers use C4Element
packages/mermaid/src/diagrams/c4/c4Renderer.ts
Boundary intersection geometry is assigned, relationship helpers accept both endpoint types, and unresolved-endpoint errors mention boundaries.
Regression coverage and release entry
packages/mermaid/src/diagrams/c4/parser/c4Boundary.spec.ts, cypress/integration/rendering/c4/c4.spec.js, .changeset/c4-boundary-relation-endpoint.md
Tests boundary alias lookup and boundary-to-system rendering, while documenting the patch release.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related PRs

Suggested labels: Graph: C4

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main fix: allowing C4 boundaries as relationship endpoints.
Description check ✅ Passed The description explains the bug, implementation, tests, issue reference, and behavior change; missing template checklist items are non-critical.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/mermaid/src/diagrams/c4/c4Renderer.ts (1)

535-539: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Error message no longer accurate.

Now that endpoints can resolve to boundaries too, the thrown message still says references an unknown shape even when a boundary alias is simply missing/misspelled. Consider wording it generically (e.g., "unknown shape or boundary") for clearer diagnostics.

✏️ Suggested tweak
-      throw new Error(`C4 rel "${rel.from}" -> "${rel.to}" references an unknown shape`);
+      throw new Error(`C4 rel "${rel.from}" -> "${rel.to}" references an unknown shape or boundary`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mermaid/src/diagrams/c4/c4Renderer.ts` around lines 535 - 539, The
error text in c4Renderer’s relationship endpoint validation is now too specific
because rel.from and rel.to can resolve to either shapes or boundaries. Update
the throw in the getC4ShapeObj-based check so the message in c4Renderer refers
generically to an “unknown shape or boundary” instead of only an unknown shape,
while keeping the same validation logic for missing endpoints.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/mermaid/src/diagrams/c4/c4Renderer.ts`:
- Around line 535-539: The error text in c4Renderer’s relationship endpoint
validation is now too specific because rel.from and rel.to can resolve to either
shapes or boundaries. Update the throw in the getC4ShapeObj-based check so the
message in c4Renderer refers generically to an “unknown shape or boundary”
instead of only an unknown shape, while keeping the same validation logic for
missing endpoints.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0dd6831f-37a8-47bf-b0a1-24d727f30659

📥 Commits

Reviewing files that changed from the base of the PR and between 0d1ee0a and af09382.

📒 Files selected for processing (5)
  • .changeset/c4-boundary-relation-endpoint.md
  • packages/mermaid/src/diagrams/c4/c4Db.ts
  • packages/mermaid/src/diagrams/c4/c4Renderer.ts
  • packages/mermaid/src/diagrams/c4/c4Types.ts
  • packages/mermaid/src/diagrams/c4/parser/c4Boundary.spec.ts

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Hi @knsv @sidharthv96 - friendly nudge on this one. CI is green; the only outstanding check is argos (visual-diff), which is still queued rather than failed. No merge conflicts, no open threads. Whenever you have a moment, a review would be very welcome.

The full staged plan for this C4 modernization effort - what's landed, what's open, and the recommended merge order - is kept up to date in #7849 if you'd like the full picture.

Thanks for your time!

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Re CodeRabbit's wording nitpick on the unresolved-endpoint error message ("unknown shape" -> "unknown shape or boundary"): already addressed, in a927a4309 (fix(c4): mention boundaries in the unknown-endpoint error message), which landed shortly after the review. No other feedback outstanding on this PR.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

The Argos build for this PR is stuck in "Build is queued" - and it is reproducible, not transient. The previous head's build sat queued for 9 days, and after refreshing the branch from develop today the new build (#7557) is stuck the same way, so I dug into the e2e logs.

Root cause (a detect-scope + parallel-upload interaction in .github/workflows/e2e.yml):

  1. detect-scope correctly narrows this C4-only PR to the C4 specs, so the run produces just 30 screenshots -> 3 composite sheets ([argos-batch] 30 screenshots -> 3 sheets), which all land in a single leaf directory.
  2. The upload step round-robins leaf directories into K=5 buckets and skips empty ones ([ ${#globs[@]} -eq 0 ] && continue), but every upload still declares --parallel-total "$K". This run performed exactly one upload ("Build created" appears once in the log) while promising Argos 5 shards, so the build waits forever for the other 4. A full-suite run (e.g. on feat(c4): migrate C4 element shapes to the unified shapes #7842: 3063 screenshots -> 272 sheets) fills all 5 buckets and finalizes fine, which is why only scope-reduced PRs hang.

Suggested fix: partition the globs into buckets first, drop the empty ones, and pass the actual bucket count as --parallel-total (equivalently K = min(5, <number of leaf dirs>)). Happy to send that as a small PR if you'd like - otherwise flagging it for you since this bites any PR whose scoped run has fewer than 5 screenshot directories.

cc @aloisklink @sidharthv96 (authors of the recent upload-batching changes)

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

This just reproduced on #7972 (a cypress-spec-only PR): its scoped run produced 356 screenshots -> 30 sheets in a single leaf directory, so again exactly one upload ran against --parallel-total 5, and Argos build 7561 is now stuck in "Waiting for screenshots" the same way as this PR's build. Second confirmed case, so any scope-narrowed PR is affected - the offer to send the small workflow fix still stands.

@aloisklink

Copy link
Copy Markdown
Member

Hi @filipsajdak, I've also got a fix for the stalling @argos-ci issue (see #7948), but I'm still waiting for another maintainer to review and merge that PR.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Thanks @aloisklink - #7948 is a cleaner fix than my suggestion (reverting the on-by-default e2e scoping rather than patching --parallel-total), so I'll drop my offer and defer to it. Once #7948 lands it should unblock the stuck argos builds here and on #7970/#7972 too. Appreciate you picking it up!

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Merged current develop in to pick up @aloisklink's #7948, which landed on 28 July.

That should clear the Argos build that has been stuck at "Waiting for status to be reported - Build is queued" on this PR. The cause was the diagram-scoped e2e run: the upload step round-robins leaf screenshot directories into 5 buckets, skips the empty ones, but still declares --parallel-total 5, so a scope-reduced run (this PR narrowed to a single C4 shard) left Argos waiting forever for shards that were never coming. #7948 reverts scoping to off-by-default, so this run should fill the matrix and finalize normally.

No code change in this push - just the merge. The fix itself is unchanged: boundary aliases are accepted as Rel endpoints, with the unknown-endpoint error message mentioning boundaries.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/mermaid/src/diagrams/c4/c4Renderer.ts (1)

254-265: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a renderer-level regression assertion.

The supplied test only checks yy.getC4Shape(...); it does not exercise drawBoundary/drawRels, where boundary.intersect is installed and consumed. Add a render test for Rel(b1, SystemAA, ...) so geometry or coordinate regressions fail in CI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mermaid/src/diagrams/c4/c4Renderer.ts` around lines 254 - 265, Add a
renderer-level regression test that renders a C4 relationship using Rel(b1,
SystemAA, ...), exercising the drawBoundary/drawRels path where
boundary.intersect is installed and consumed. Assert the rendered relationship
geometry or output so coordinate regressions fail, rather than only testing
yy.getC4Shape(...).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/mermaid/src/diagrams/c4/c4Renderer.ts`:
- Line 365: Update the missing-intersection error in getIntersectPoint to refer
to a “shape or boundary” or “C4 element” instead of “C4 shape,” matching the
C4Element parameter and preserving the existing error behavior.

---

Nitpick comments:
In `@packages/mermaid/src/diagrams/c4/c4Renderer.ts`:
- Around line 254-265: Add a renderer-level regression test that renders a C4
relationship using Rel(b1, SystemAA, ...), exercising the drawBoundary/drawRels
path where boundary.intersect is installed and consumed. Assert the rendered
relationship geometry or output so coordinate regressions fail, rather than only
testing yy.getC4Shape(...).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9dee5bc4-0b84-43f2-b633-fe4b4f67a73c

📥 Commits

Reviewing files that changed from the base of the PR and between a927a43 and de5c859.

📒 Files selected for processing (2)
  • packages/mermaid/src/diagrams/c4/c4Renderer.ts
  • packages/mermaid/src/diagrams/c4/c4Types.ts

Comment thread packages/mermaid/src/diagrams/c4/c4Renderer.ts
@pbrolin47

Copy link
Copy Markdown
Collaborator

Hi @filipsajdak ,
Thanks for the recent work in this PR. Tried the local branch and rendered the digram in #4864 and it rendered as expected, which is all good:

image

After running a review, some feedback:

[sisyphos-bot]


What's Working Well

🎉 Root cause identified and addressed precisely. The fix is exactly where it needs to be: getC4Shape falls back to boundaries via ?? (not ||, which would miss falsy-but-valid boundary objects).
Clean and minimal.

🎉 C4Element type alias avoids type assertions. Using C4Shape | C4Boundary as a named type alias for the union is much cleaner than sprinkling as unknown as C4Shape casts. The renderer functions now
express their actual contract in the type signature.

🎉 boundary.intersect closure is placed correctly. It's assigned after boundary.x/y/width/height are set and before any subsequent code that might call drawRels. The closure captures the mutable
object by reference, so it reads the final geometry at call time — correct.

🎉 Center-point math is correct. intersect.rect expects center coordinates + dimensions; the conversion from C4's top-left origin is right: x: boundary.x + boundary.width / 2.

🎉 Excellent PR description. The summary explains the root cause, the fix, and the geometry reasoning clearly. The changeset is present, properly marked patch, and linked to the issue.

🎉 Regression test captures the fix. c4Boundary.spec.ts verifies that getC4Shape('b1') resolves a boundary alias after parsing, SystemAA still resolves as a shape, and missing returns undefined. All
three assertions are meaningful.


Things to Address

🟡 [important] Missing Cypress E2E test for the renderer path — c4Boundary.spec.ts covers the DB layer (getC4Shape lookup), but c4Renderer.ts now adds new rendering logic in drawBoundary (the
boundary.intersect closure). Per the project convention (CLAUDE.md (https://github.com/mermaid-js/mermaid/blob/develop/CLAUDE.md): "Any change that affects rendered SVG output. This is the primary
verification method for visual correctness"), a Cypress test is needed.

The test doesn't need to be elaborate — a minimal diagram with a boundary-to-shape Rel would be sufficient:

// cypress/integration/rendering/c4.spec.js
it('renders a Rel targeting a boundary alias', () => {
imgSnapshotTest(C4Context Container_Boundary(banking, "Internet Banking") { Container(spa, "SPA", "JavaScript", "Front-end") } System_Ext(email, "Email System", "External mail") Rel(banking, email, "Forwards alerts") );
});

This gives visual proof the relationship line renders and prevents future regressions — before this fix, that diagram would throw instead of rendering.


Minor Notes (non-blocking)

🟢 Unit test assertion could confirm boundary type. expect(yy.getC4Shape('b1')).toMatchObject({ alias: 'b1' }) verifies the alias is found, but doesn't prove it came from the boundaries array rather
than a shape. Adding type: { text: 'Container_Boundary' } (or similar boundary-specific field) would make the test intent explicit and prevent getC4Shape from silently finding a shape with the same
alias in future refactors.

🟢 C4Shape lacks a typed intersect? declaration. C4Boundary now has intersect?: (point: C4Point) => C4Point as a proper named field, but C4Shape only has it accessible through the [key: string]:
unknown index signature. For C4Element = C4Shape | C4Boundary, TypeScript resolves .intersect as unknown (since unknown from the shape side absorbs the union). Adding intersect?: (point: C4Point) =>
C4Point to C4Shape would make both sides of the union fully typed. (This is a pre-existing gap, not introduced by this PR — but since you're already touching these types, it would be a natural time
to close it.)


Security

No security concerns. The fix widens a lookup function and adds a geometry closure — no user-supplied text flows to new unsanitized paths. The alias field being found in boundaries vs. shapes
doesn't open any new injection surface.


@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Thanks @pbrolin47 - and thanks especially for rendering the actual #4864 diagram rather than taking the unit tests' word for it.

🟡 Missing Cypress E2E test - added. C4.9 should render a relationship whose endpoint is a boundary, essentially your diagram:

C4Context
title Relationship to a boundary (#4864)

Container_Boundary(banking, "Internet Banking") {
  Container(spa, "SPA", "JavaScript", "Front-end")
}
System_Ext(email, "Email System", "External mail")

Rel(banking, email, "Forwards alerts")

You are right that this needed renderer-level coverage, and more so than the review knew: the boundary.intersect closure it exercises is not part of the original fix at all. It came in when I merged develop after #7842 landed - that PR made getIntersectPoint delegate to each shape's own intersect and throw when there is none, which is safe only while every endpoint is a unified-drawn shape. Boundaries are drawn by the legacy path and had none, so the two changes together would have thrown for exactly the case this PR adds. Before this test, nothing in CI would have caught that.

🟢 Unit assertion - strengthened, but a different way than suggested. type: { text: 'Container_Boundary' } would not actually discriminate: a boundary's type.text comes out as 'system', which is the same value a System(...) shape carries, and there is a standing TODO: Why is this 'system' instead of 'boundary'? about it a few lines up in that same spec. So it would have passed even if getC4Shape returned a shape.

Asserting object identity gets at what you actually wanted:

const boundary = yy.getBoundaries().find((b) => b.alias === 'b1');
expect(yy.getC4Shape('b1')).toBe(boundary);
expect(yy.getC4Shape('SystemAA')).toMatchObject({
  alias: 'SystemAA',
  typeC4Shape: { text: 'system' },
});

That proves the endpoint came from the boundaries array specifically, and the typeC4Shape assertion pins the shape lookup to the shape path.

🟢 C4Shape lacks a typed intersect? - this one is already the case, so nothing to change: C4Shape declares intersect?: (point: C4Point) => C4Point at c4Types.ts:59 (added by #7842), and I added the matching declaration to C4Boundary, now at line 91. Both sides of C4Element are fully typed - .intersect resolves to the function type, not unknown. I suspect the review was reading the pre-#7842 file.

127 c4 unit tests pass, tsc clean.

@filipsajdak
filipsajdak force-pushed the bug/4864_c4-boundary-rel branch from 1b07012 to cdcb095 Compare August 1, 2026 20:10
@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Rebased onto current develop and tidied the history: 9 commits -> 1. The change itself is unchanged, and your review comments are untouched, @pbrolin47.

This one needed it more than the others. Five of those nine were Merge remote-tracking branch 'upstream/develop' commits, and - worse - the boundary intersect implementation was living inside one of those merge commits, where it was effectively invisible to review. It is now part of the single commit, described in the message.

The tree is byte-identical to the previous head: the commit was rebuilt from that exact tree rather than replayed, and git diff <old head> <new head> was empty before pushing. 127 c4 unit tests pass, tsc clean.

Still 6 files, +63/-6 - the db fallback, the C4Element union, the boundary rectangle intersect, and both tests.

A Rel naming a boundary alias threw "references an unknown shape", because
getC4Shape only looked at the element array. It now falls back to the
boundaries, so a relationship can start or end at a boundary as reported in
 mermaid-js#4864. C4Element names the shape-or-boundary union that relationship drawing
actually accepts, which keeps the renderer signatures honest without casts.

A boundary also needs its own intersect function. Element shapes get one from
the unified shape they are drawn with, and the renderer throws when an
endpoint has none; boundaries are drawn by the legacy path, so they are given
a rectangle intersect over their own box once its geometry is known. The
shared intersect.rect helper measures from the centre while the C4 grid stores
the top-left corner, so the centre is passed explicitly.

Covered both ways: the db test asserts the endpoint resolves to the object in
the boundaries array, and a rendering test draws a relationship targeting a
boundary.
@filipsajdak

Copy link
Copy Markdown
Collaborator Author

The Cypress test I added on 2026-08-01 was failing, and I should have caught that before saying it was done - I ran the unit suites but never checked the e2e result, so e2e (8) has been red since. Sorry for the noise, @pbrolin47. Fixed now, and rebased onto current develop.

It was my test, not the fix - but the reason is worth knowing, because it is a real bug in develop:

Lexical error on line 2. Unrecognized text.
...ship to a boundary (#4864)Container_Bo
-----------------------^

I had written title Relationship to a boundary (#4864). A # anywhere in a C4 title breaks parsing - it swallows the newline and the statements that follow. Verified through mermaid.parse(), which is the same path the rendering test uses:

title result
title Relationship to a boundary parses
title Relationship to a boundary (4864) parses
title Relationship to a boundary #4864 lexical error
title Relationship to a boundary (#4864) lexical error
title issue#4864 lexical error

Parentheses are fine; # is not. The cause is c4Diagram.jison:87, where the title token is "title"\s[^#\n;]+ - the character class excludes #, so the token stops there and the lexer is left on text that matches nothing. C4's comment syntax is %%, and this grammar has no # comment rule at all, so the exclusion looks vestigial.

That affects anyone writing title Sprint #3, so I filed it separately as #8030 rather than bundling a grammar change into this PR - it is unrelated to boundaries and predates this branch. Worth noting sequenceDiagram and journey carry the same character-class exclusion yet still parse a # title, so C4 is specifically the one that hard-fails; details and a per-diagram matrix are in the issue.

Here the #4864 was only decoration in a test title, so it is simply dropped. The test still does exactly what you asked for: renders a relationship whose endpoint is a boundary, which is the path that would have thrown before this fix.

Still one commit, 6 files, +87/-7. c4 unit suite 127/127, rendering-util 515 passing, tsc clean, now on top of develop@3d521b1ee (which includes the fastdom label-measurement change - no effect on the c4 suites).

@pbrolin47

Copy link
Copy Markdown
Collaborator

Hi @filipsajdak
Thanks for fixing the failing e2e, and I think it's a good praxis to handle found issues not directly related to a PR in a separate issue as you did with #8030.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Thanks @pbrolin47.

For the record, all three points from your 2026-07-31 review are now addressed:

  1. Cypress E2E for the renderer path: C4.9 in cypress/integration/rendering/c4/c4.spec.js, a Rel whose source is a boundary alias. Green.

  2. Unit test proving the lookup really hit a boundary: done via object identity rather than a type field.

    const boundary = yy.getBoundaries().find((b) => b.alias === 'b1');
    expect(yy.getC4Shape('b1')).toBe(boundary);

    A same-alias shape cannot satisfy that.

  3. Typed intersect? on both sides of C4Element: C4Shape picked one up in feat(c4): migrate C4 element shapes to the unified shapes #7842, so I added the matching declaration to C4Boundary. The field no longer resolves through the index signature.

CI is green, argos included. The netlify/mermaid-js/deploy-preview failure is not from this PR: deploy previews are failing repo-wide right now (same on #8031, #8032, #8034).

Anything else you would like changed before this goes in?

@pbrolin47

Copy link
Copy Markdown
Collaborator

Hi @filipsajdak
I think the PR LGTM, and agree the failing checks does not seem to relate to this PR. I want to discuss with other maintainers how-to proceed with this. It's on my agenda.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Thanks! To save you time in that discussion, here is the evidence that the failing checks are repo-wide rather than from this PR.

All four failures here are Netlify: netlify/mermaid-js/deploy-preview plus its Header rules, Pages changed and Redirect rules sub-checks. Checking netlify/mermaid-js/deploy-preview on the most recent open PRs from other authors, just now:

PR deploy-preview
#8047 FAILURE
#8046 FAILURE
#8045 FAILURE
#8044 FAILURE
#8038 FAILURE
#8034 FAILURE
#8032 FAILURE
#8031 FAILURE

Eight out of eight, none of them mine. It started around 2026-08-06: PRs from a few days earlier still show it green, so it looks like a Netlify-side or build-config change rather than anything in a diff.

Everything else on this PR is green, including argos and the full 8-shard e2e, and the diff touches no docs and nothing the Netlify build consumes.

Happy to open a separate issue for the Netlify breakage if that would help - it is affecting every open PR, so it is worth tracking on its own rather than per-PR.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Force-pushed to remove the Co-Authored-By: Claude / Claude-Session: trailers from the commit message. Message-only rewrite - the tree is byte-identical, verified with git diff <old-tip> <new-tip> returning empty, so the fix itself is unchanged and any review you have already done still applies.

This was raised in review on #7859, citing the project's own rule against tagging commits as AI-authored. Since this repo merges rather than squashes, the message would land on develop verbatim, so it had to be corrected before merge rather than after.

Nothing else about the PR moved: same single commit, same diff, same tests.

@pbrolin47

Copy link
Copy Markdown
Collaborator

Hi @filipsajdak,
Sorry for the late response. Was OOO last week. Regarding the Netlify problems, after discussion with other maintainers I got advised to re-run the deployment without cache from lates branch commit, but I needed some permissions to be able to do that. Not this PR is all good

@pbrolin47
pbrolin47 added this pull request to the merge queue Aug 18, 2026
Merged via the queue into mermaid-js:develop with commit 556d2a3 Aug 18, 2026
31 checks passed
filipsajdak added a commit to filipsajdak/mermaid that referenced this pull request Aug 18, 2026
Resolves against mermaid-js#7874 (boundary as a relationship endpoint), which landed
on develop while this branch was open and touches the same file.

- imports: keep both sides (this branch's unified edge helpers, develop's
  `intersect`, which `drawBoundary` now uses to give a boundary its own
  intersect function)
- `getIntersectPoints`: this branch rewrote it to aim each endpoint at the
  other shape's centre, and develop widened its parameters from `C4Shape`
  to `C4Element` so a boundary can be an endpoint. Both are wanted, so the
  widening is applied to the rewritten version rather than either side
  being taken whole.

`drawRels`' `getC4ShapeObj` parameter is widened to `C4Element` to match
`c4Db.getC4Shape`, which develop changed to fall back to boundaries.

132 C4 unit tests pass; tsc, eslint and Prettier clean.
filipsajdak added a commit to filipsajdak/mermaid that referenced this pull request Aug 18, 2026
Brings in develop, including mermaid-js#7874 (a relationship may name a boundary as
an endpoint, mermaid-js#4864), which landed while this branch was open.

The only conflict is `c4Renderer.ts`: mermaid-js#7874 modified it, this branch
deletes it. The deletion stands, and mermaid-js#7874's fix is not lost with it.
That fix had two halves:

- `c4Db.getC4Shape` falling back to boundaries, and the `C4Element` type -
  both live outside the renderer and are unaffected.
- giving a boundary its own `intersect` function so the legacy grid could
  find where a line meets its box. The unified pipeline has no use for
  this: a boundary is an ordinary group node and dagre routes the edge to
  the cluster itself.

Verified by rendering mermaid-js#7874's own diagram through the unified pipeline -
the edge, its label and the cluster are all present - and the cypress case
it added (C4.9) now runs against this branch too.
filipsajdak added a commit to filipsajdak/mermaid that referenced this pull request Aug 18, 2026
mermaid-js#7874 fixed mermaid-js#4864 for the legacy renderer by giving a boundary its own
intersect function. This branch removes that renderer, so the case needs a
test on the pipeline that replaces it, or nothing here would catch a
regression until the cypress screenshot diverged.

The boundary is an ordinary group node in this pipeline, so the assertion
is simply that the edge is built with the boundary as its endpoint.
filipsajdak added a commit to filipsajdak/mermaid that referenced this pull request Aug 18, 2026
mermaid-js#7874 added a `C4.9` cypress case for a relationship pointing at a
boundary; this branch had already taken `C4.9` for its dark-theme case.
Both are wanted and independent, so develop's keeps the number it merged
with and this branch's becomes `C4.10`.

`c4Renderer.ts` merged cleanly. 138 C4 unit tests pass.
github-actions Bot added a commit to zhongmiao-org/mermaid-markdown-bridge that referenced this pull request Sep 10, 2026
Upstream release: https://github.com/mermaid-js/mermaid/releases/tag/mermaid%4012.0.0

Release notes:
**Mermaid 12.0.0** ships ELK as the bundled default layout engine, a new `redux-color` / `neo` default appearance, and two new diagram types: [UML use case diagrams](https://mermaid.ai/open-source/syntax/usecase.html).

This is a breaking release (ES2024, Safari 17.4+, Node 22.12+). Existing flowcharts, state and class diagrams will re-lay out and recolour; add `layout: dagre`, `theme: default` and `look: classic` to your config to keep the old look. Full changelog below.

### Major Changes

-   [#8213](mermaid-js/mermaid#8213) [`33442fd`](mermaid-js/mermaid@33442fd) Thanks [@aloisklink](https://github.com/aloisklink)! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+

    Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support
    older browsers, you may need to polyfill or transpile mermaid.

    Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome,
    older iOS devices don't get major Safari updates.

    Node.JS v22.12+ is also declared as requirement in our `package.json` files,
    but as mermaid requires a browser, this is mainly so we can use dependencies that
    also declare a Node.JS v22.12+ requirement, without causing issues for users when
    running `npm install`.

-   [#8155](mermaid-js/mermaid#8155) [`810893c`](mermaid-js/mermaid@810893c) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat!: **ELK is now bundled with mermaid and is the default layout algorithm.**

    ELK previously shipped as a separate `@mermaid-js/layout-elk` package that sites had to install and register. It is now part of mermaid itself and registered automatically, so `layout: elk` — and the `elk.stress`, `elk.force`, `elk.mrtree`, `elk.sporeOverlap`, `elk.box` and `elk.rectpacking` variants — work with no setup.

    **This changes how existing diagrams look.** Flowchart, state, class, entity-relationship, requirement, use-case diagrams that do not specify a `layout` are now laid out by ELK instead of dagre. To keep the previous layout, set dagre explicitly:

    ```yaml
    ---
    config:
      layout: dagre
    ---
    ```

    or globally, `mermaid.initialize({ layout: 'dagre' })`.

    Mindmap is unchanged: it keeps laying out with cose-bilkent unless a layout is explicitly requested (in the tiny build, which ships neither ELK nor cose-bilkent, it falls back to dagre).

    Other notes:

    -   ELK is loaded as a separate chunk in the ESM builds, so it is only fetched when a diagram actually uses it. The single-file IIFE build (`mermaid.min.js`) inlines it and grows by roughly 500 kB gzipped.
    -   The **tiny** build deliberately omits ELK to stay small, and falls back to dagre for diagrams that ask for an ELK layout. Its size is unchanged.
    -   `@mermaid-js/layout-elk` is no longer needed on normal builds — existing `mermaid.registerLayoutLoaders(elkLayouts)` calls keep working and can be removed. It is still published, and remains the way to add ELK to the tiny build.
    -   `dist/mermaid.esm.min.mjs` now contains syntax that `es-module-lexer` (used by Vite) rejects with `content contains invalid JS syntax`. Bundler users are unaffected as long as they import the `mermaid` package specifier, which resolves to the core build; only builds that point Vite directly at that dist file need to switch to the package specifier or the core build.
    -   State diagrams now resolve their layout through the same registration check as every other diagram, so an unavailable layout falls back instead of failing to render.

    An unrecognised `theme` name now resolves to the default theme in name as well as in variables; previously the invalid name stayed in place while the default's variables were loaded, and every palette-aware stylesheet gates on the name. `theme: 'null'`, the documented way to disable the pre-defined themes, is unaffected. Note that `neo` paints node strokes with a gradient when the theme sets `useGradient`, which `base` does; setting a custom `nodeBorder` on `base` turns the gradient off.

-   [#8211](mermaid-js/mermaid#8211) [`a19bd08`](mermaid-js/mermaid@a19bd08) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - **Removed: the `defaultRenderer` option of the `flowchart`, `class` and `state` config sections.**

    Use the top-level `layout` option instead:

    ```yaml
    ---
    config:
      layout: dagre # or elk
    ---
    ```

    ### Why

    `defaultRenderer` named a rendering engine back when there was more than one. There is not.
    All of its accepted values — `dagre`, `dagre-wrapper`, `dagre-d3` — had come to select the
    same unified renderer, and its `elk` value did nothing but set `layout: elk` on your behalf.
    The option's documented purpose no longer existed.

    What it still did was stranger than doing nothing. The detectors branched on whether the
    value was _recognised_, so a **valid** value routed a diagram to the modern renderer and an
    **invalid** one silently routed it to the legacy renderer. Since the default was always a
    valid value, the legacy `class` and `state` renderers were unreachable except by
    misconfiguring the option.

    ### What changes for you

    -   `layout` selects the layout algorithm, as it already did. Nothing else does.
    -   `graph`, `classDiagram` and `stateDiagram` always render with the unified diagram. This
        was already true for every valid configuration.
    -   The legacy `flowchart`, `class` and `state` diagram ids are gone. `flowchart-v2`,
        `classDiagram` and `stateDiagram` are what `detectType` now returns for that syntax.
    -   `flowchart-elk` as an explicit diagram keyword still works.

    Configuration that still sets `defaultRenderer` is ignored rather than rejected, so nothing
    throws — but it no longer has any effect, and diagrams that relied on
    `flowchart: { defaultRenderer: 'elk' }` to get an ELK layout should set `layout: elk`.

-   [#8223](mermaid-js/mermaid#8223) [`6df1149`](mermaid-js/mermaid@6df1149) Thanks [@aloisklink](https://github.com/aloisklink)! - refactor!: remove layout internal exports

    Remove the
    `clearLayoutRenderState`, `createCommonLayoutRenderer`, `defaultMeasureLayout`, `paintLayoutData` functions,
    and the `CommonLayout*` types from Mermaid's public API.

    These functions were only used by the `@mermaid-js/layout-elk` package,
    which now bundles them. Removing them makes it easier to avoid major versions
    of mermaid.

### Minor Changes

-   [#8048](mermaid-js/mermaid#8048) [`7a3c1a8`](mermaid-js/mermaid@7a3c1a8) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: add UML use case diagrams with actors, use cases, system boundaries, relationships, stereotypes, notes, JSON tables, class and style support, accessibility metadata, and business variants.

-   [#8073](mermaid-js/mermaid#8073) [`cd48a64`](mermaid-js/mermaid@cd48a64) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: `defaultMeasureLayout` accepts a `unwrapGroupLabels` option so a layout engine can ask for cluster labels to be measured at their natural width instead of the `flowchart.wrappingWidth` fallback.

    `insertCluster` paints a plain cluster label with an infinite width while the measurement pass wrapped it at 200px, so a layout that sizes compound nodes from the measured label sized them too narrow. Layouts now opt in explicitly; core no longer inspects the layout's name. Markdown cluster labels stay wrapped, since those are painted wrapped.

    The bundled ELK layout opts in, which changes the size of labelled subgraphs in ELK-laid-out diagrams. Diagrams laid out with dagre are unaffected.

-   [#8073](mermaid-js/mermaid#8073) [`0cf3797`](mermaid-js/mermaid@0cf3797) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: let a container pick its own ELK algorithm with `@{ algorithm: … }`, and add `elk.box` and `elk.rectpacking` to the selectable `layout` values.

    A subgraph carrying `@{ algorithm: elk.box }` is laid out with that algorithm in its own coordinate system instead of inheriting the diagram's. Supported values are `elk.layered`, `elk.box`, `elk.rectpacking`, `elk.stress`, `elk.force`, `elk.mrtree`, `elk.radial`, and `elk.sporeOverlap`; anything else is ignored with a warning rather than handed to ELK, where an unknown id would abort the layout. Containers with edges crossing their boundary fall back to the inherited algorithm, since isolated layout and cross-boundary edges are incompatible.

    Also in this release:

    -   ELK cluster labels are measured unwrapped, so a compound node is sized to fit its label instead of to a 200px wrapping width. **This changes the size — and therefore the layout — of existing ELK diagrams that have labelled subgraphs.**
    -   `elk.box` and `elk.rectpacking` place nodes but never route edges. Edges they leave unrouted now fall back to a straight line between the two node centres instead of failing the render.
    -   `keepEntryNodeOnTop` pins a cycle's true entry rather than the first-declared node when a back-edge feeds the entry, so recursive flows read from where they actually start.

-   [#8193](mermaid-js/mermaid#8193) [`813c766`](mermaid-js/mermaid@813c766) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - **`theme`, `look` and `layout` can now be set per diagram type**, in each diagram's config section — `mermaid.initialize({ look: 'classic', flowchart: { look: 'handDrawn' } })`, or the same under `config` in front matter. The schema uses the same mechanism to give a diagram type its own default. Resolution, highest first: front matter or directive, `initialize()`, the diagram type's default, the global default; a diagram-scoped value beats a global one set in the same layer.

    Swimlanes take `layout: swimlane` from that schema default instead of having it forced by their `init` hook, so `swimlane: { layout: ... }` and a `layout` in front matter are now honoured. An unregistered layout also always falls back to `dagre` with a warning rather than throwing — state diagrams skipped that fallback, and mindmaps threw when `cose-bilkent` was absent, as it is in `@mermaid-js/tiny`.

-   [#8147](mermaid-js/mermaid#8147) [`43d9fbc`](mermaid-js/mermaid@43d9fbc) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): class boxes and flowchart subgraph containers now take a per-item colour under the `redux-color` and `redux-dark-color` themes, cycling every 12 as ER entities already do. Collapsed subgraphs keep the slot they would have had expanded; nodes inside a subgraph stay uniform, and explicit `classDef` or `style` still wins over the palette.

-   [#8191](mermaid-js/mermaid#8191) [`2878cf3`](mermaid-js/mermaid@2878cf3) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): composite states now take a per-container colour under the `redux-color` and `redux-dark-color` themes, as flowchart subgraphs already do. Each composite gets the palette's border colour on its outline and its background tint behind the title strip; nested composites each take the next colour, so depth stays readable. The body keeps the theme's own `compositeBackground`. `redux-dark-color` colours the outlines only, matching how it treats ER, requirement and sequence.

    The concurrency regions produced by a `--` divider share the colour of the composite they split, rather than taking one of their own — the author wrote a single composite, so it is drawn as one thing in parts. Adding a `--` therefore leaves every other composite's colour untouched.

    Under the `handDrawn` look, concurrency regions are now filled solid rather than hatched, so that they can carry the palette tint the same way every other look does.

    States inside a composite stay uniform. A composite carrying its own `classDef` or `style` keeps those colours and takes no palette slot, and neither do its concurrency regions; the slot is still spent, so styling one composite does not shift the colours of the ones after it. Note that this opt-out is all-or-nothing: a `classDef` that sets only text properties, such as `font-weight`, still takes that composite out of the palette.

-   [#8176](mermaid-js/mermaid#8176) [`16b9a7d`](mermaid-js/mermaid@16b9a7d) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): swimlane lanes take a per-lane colour under the `redux-color` and `redux-dark-color` themes, cycling every 12 as flowchart subgraphs do. The lane holding ungrouped nodes takes its own slot and now follows the diagram's `look`.

-   [#8178](mermaid-js/mermaid#8178) [`75e6c30`](mermaid-js/mermaid@75e6c30) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat(usecase): use case diagrams now take colour by role — actors, use cases and system boundaries each get their own colour from the new `usecaseActorBkg`/`usecaseActorBorder`, `usecaseBkg`/`usecaseBorder` and `usecaseBoundaryBkg`/`usecaseBoundaryBorder` theme variables, with `usecaseIncludeLine`/`usecaseExtendLine` separating the two dashed relationship kinds by hue. `redux-color` and `redux-dark-color` set them; every other theme is unchanged. Colour is keyed to the kind of element, so editing a diagram never recolours the elements around the edit. Set `usecase.colorScheme: 'rotate'` for the per-element palette cycle instead, and `classDef`/`style` still wins over both.

### Patch Changes

-   [#8211](mermaid-js/mermaid#8211) [`dc2e453`](mermaid-js/mermaid@dc2e453) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(block): restore node borders under looks that use a gradient stroke.

    A block diagram rendered with `look: neo` lost its node borders entirely. The look-specific
    rules set `stroke: url(#<svgId>-gradient)`, and those definitions were appended by
    `rendering-util/render.ts` — which block does not use, since it runs its own render loop.
    An `url(#…)` paint that resolves to nothing is painted as **none**, not as a fallback
    colour, so the border disappeared rather than degrading to a plain stroke.

    The definitions now live in `rendering-util/insertLookDefs.ts` and both render paths call
    it, so the two cannot drift into producing different gradients.

    This only became visible recently: block did not previously pass `look` to its nodes, so
    none of the look-specific rules applied to it and the missing definitions went unnoticed.

-   [#8181](mermaid-js/mermaid#8181) [`ce0302d`](mermaid-js/mermaid@ce0302d) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(block): apply the redux colour palette to composite blocks. Composites now take a per-container colour under `redux-color` and `redux-dark-color`, matching how flowchart subgraphs are coloured — one counter over containers, in declaration order, with the plain shapes left on the flat theme colour. The palette is opt-in: block does not default to `redux-color` the way flowchart and several other diagram types do, so set `theme: redux-color` (or `redux-dark-color`) explicitly to see it. `classDef`/`style` still win.

-   [#8211](mermaid-js/mermaid#8211) [`ad98070`](mermaid-js/mermaid@ad98070) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(block): take the node border width from the theme, as every other diagram does.

    `block/styles.ts` pinned `stroke-width: 1px` on node shapes while the flowchart, class and
    state stylesheets all read `strokeWidth` from the theme. Under a theme that asks for more —
    `neo` asks for 2 — a block rendered a visibly thinner border than an identical flowchart
    node, and nothing downstream corrected it: the `neo` rules in the shared stylesheet set
    `stroke` and `filter`, never `stroke-width`.

    The gap was invisible until block began propagating `look` to its nodes, because until then
    none of the look-specific rules applied to block at all.

    Affects only themes whose `strokeWidth` is not 1. `neo` is 2; `neo-dark`, `base` and the
    rest are unchanged.

-   [#8231](mermaid-js/mermaid#8231) [`e2aab3f`](mermaid-js/mermaid@e2aab3f) Thanks [@knsv-bot](https://github.com/knsv-bot)! - Use balanced Brandes-Koepf placement for the default ELK preset to improve alignment of branches and composite-state entries. Preserve named non-default presets and explicit placement/alignment options, including `NONE`. Some diagrams may become wider or taller; `depthFirst` retains the previous default layout.

-   [#7874](mermaid-js/mermaid#7874) [`6b79518`](mermaid-js/mermaid@6b79518) Thanks [@filipsajdak](https://github.com/filipsajdak)! - fix(c4): allow boundaries as relationship endpoints

      A `Rel` (or `BiRel`/`Rel_*`) that referenced a boundary alias threw `references an unknown
    shape`, because endpoint lookup only searched shapes and not boundaries. Boundary aliases now
      resolve as relationship endpoints. Fixes #4864.

-   [#8100](mermaid-js/mermaid#8100) [`30325d4`](mermaid-js/mermaid@30325d4) Thanks [@filipsajdak](https://github.com/filipsajdak)! - fix(c4): stop wrapping non-text named attributes that land in a text slot

    A named attribute such as `$tags` or `$sprite` can arrive in the positional
    slot of a text field when the argument before it is omitted. It was then
    stored as `{ text: value }` rather than a string, so `$tags` given without a
    description crashed rendering.

-   [#8148](mermaid-js/mermaid#8148) [`c3ee3c7`](mermaid-js/mermaid@c3ee3c7) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): `redux-dark`, `redux-dark-color` and `neo-dark` shipped `secondBkg` as the literal string `calculated`, so railroad rendered the invalid `fill: calculated`; it is now computed as `theme-dark` does. The same three themes had gantt done-task labels at 1.07:1 contrast — a light fill under their light task ink — now 5.7:1 or better. The ER and requirement stylesheets also validate `look` before interpolating it into a CSS selector.

-   [#8222](mermaid-js/mermaid#8222) [`1ee934d`](mermaid-js/mermaid@1ee934d) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: stop ELK-laid-out diagrams logging `Unknown arrow type: arrow_open` once per edge.

    ELK's arrow table put the edge _type_ `arrow_open` — which means "no arrowheads" — into the arrow-type slot, where `none` is the value that marks a deliberate absence. Anything else is reported as an unknown marker name. Diagrams whose db sets `arrowTypeStart` itself, such as flowcharts, never reached that fallback; state diagrams, which leave it unset, warned on every edge.

    Rendering is unchanged: both spellings produced no start marker and the same geometry, since neither has a marker offset. Only the log noise goes away.

-   [#8204](mermaid-js/mermaid#8204) [`a7831c5`](mermaid-js/mermaid@a7831c5) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: keep ELK subgraph frames out to the border anchor of edges that terminate on the group, so transitions to composite states no longer run along the cluster border

-   [#8155](mermaid-js/mermaid#8155) [`0320406`](mermaid-js/mermaid@0320406) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(elk): a composite state or subgraph containing a loop now opens on its own start node.

    Cycle breaking was resolved for the root graph but never passed to containers, so a
    container laid out on its own fell back to ELK's default, `GREEDY`, while the root ran
    whatever `elk.preset` asked for. The two then reversed different edges of the same cycle.

    In a composite state that loops, greedy reverses the edge that turns an ordinary state
    into a source, and sources sit on the first layer — so the diagram opened on that state
    alongside its start circle instead of on the start circle alone. Dagre reverses the other
    edge, which is why the two layout engines disagreed on the same diagram.

    Containers now resolve cycle breaking from the preset exactly as the root does. Only
    containers that actually contain a cycle change; an acyclic subgraph gives a cycle-breaking
    strategy nothing to do. `preset: legacy` still reaches containers with `GREEDY`, so it keeps
    reproducing the earlier rendering inside frames as well as outside them.

-   [#8228](mermaid-js/mermaid#8228) [`e36b883`](mermaid-js/mermaid@e36b883) Thanks [@knsv-bot](https://github.com/knsv-bot)! - Reserve subgraph title padding before ELK routes edges so attachments stay on the painted frame. Clip stale interior endpoints along the incoming segment, avoiding edges that run along the subgraph border.

-   [#8199](mermaid-js/mermaid#8199) [`b993915`](mermaid-js/mermaid@b993915) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: keep ELK edges vertical at small nodes — the node now moves onto the routed line instead of the edge bending toward the node's off-centre position

-   [#8155](mermaid-js/mermaid#8155) [`4e00c5c`](mermaid-js/mermaid@4e00c5c) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: render ER relationship edges above subgraph backgrounds under the ELK layout instead of hiding them behind the cluster rect

-   [#8156](mermaid-js/mermaid#8156) [`3f05015`](mermaid-js/mermaid@3f05015) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(er, requirement, timeline): stop the ER, requirement and timeline stylesheets emitting CSS the browser discards for the colour themes.

    All three generate one rule per palette slot. ER and requirement looped to `THEME_COLOR_LIMIT` and indexed the palette by the loop counter, which goes wrong in both directions: a palette shorter than the limit emitted `stroke: undefined` for the overflow slots, and a palette _longer_ than it left entities stamped with a slot that had no rule at all, rendering unstyled beside coloured neighbours. Both now take the loop bound from the palette itself, which is the same length the boxes stamp with — so the rules emitted and the slots stamped cannot disagree. Both also bail before the loop for an empty palette, since wrapping alone is not enough there: `i % 0` is `NaN` and `[][NaN]` is `undefined`.

    Timeline keeps looping to `THEME_COLOR_LIMIT` and wrapping, because it numbers `.section-N` classes rather than palette slots — nothing stamps those, so the palette cycles across however many sections exist.

    `requirement` also emitted `fill: ;` — a property with no value, which is invalid — whenever there was no background palette. That is the live case for `redux-dark-color`, which ships a border palette and no background palette so that it colours outlines only. The declaration is now omitted instead.

    None of these raises an error: the browser discards the invalid declaration, so the only symptom is a shape rendering unstyled.

-   [#8149](mermaid-js/mermaid#8149) [`d57ed55`](mermaid-js/mermaid@d57ed55) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(docs): stop `docs:build` deleting the committed `docs/` directory when a later step fails.

-   [#7906](mermaid-js/mermaid#7906) [`1fc5bb3`](mermaid-js/mermaid@1fc5bb3) Thanks [@knsv-bot](https://github.com/knsv-bot)! - perf: avoid quadratic parse cost on deeply-indented diagrams (~1.4 s → ~30 ms on a pathological fixture)

-   [#8211](mermaid-js/mermaid#8211) [`b979eb4`](mermaid-js/mermaid@b979eb4) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(handdrawn): tighten the hachure fill so shapes read as filled rather than striped.

    The hand-drawn fill drew 4px-wide strokes 5.2px apart, which left visible diagonal
    banding across every shape — legible as a texture up close, but at normal diagram size it
    read as stripes rather than a fill. Strokes are now 1.5px at 1.5px spacing, so the fill
    reads as an even tint while keeping the drawn edge quality.

    The striped fill behind state-diagram start, end and fork markers moves the same way, so
    those come out close to solid — which is what the UML convention asks for anyway.

    Swimlane lane titles previously pinned their own stroke weight and so ignored the shared
    value; they now follow it like every other shape.

-   [#8152](mermaid-js/mermaid#8152) [`7ee4c3f`](mermaid-js/mermaid@7ee4c3f) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: edges attach to non-rectangular shapes on the outline instead of half a pixel off it.

    **This is not ELK-specific.** `intersectPolygon` is how every non-rectangular shape finds its edge attachment — diamond, stadium, hexagon, trapezoid, subroutine — in every layout, so a dagre-rendered flowchart with a decision diamond is affected exactly as much as an ELK one.

    `intersectLine` comes from Graphics Gems, where the coordinates were integers and the numerator was nudged half a denominator away from zero so the integer division rounded instead of truncating. JavaScript division does neither, so the nudge stopped being a correction and became the whole error: every result came back displaced by `0.5 * sign(num) * sign(denom)`. The magnitude is always exactly half a unit, but the sign is per axis, because the numerator is computed separately for x and y while the denominator is shared — so the two axes could move the same way or opposite ways depending on the geometry, which is why it never looked like a constant offset anyone could spot by eye.

    That was enough to give an otherwise orthogonal edge a tiny diagonal opening segment, and to put an attachment just inside the node it was meant to touch.

    `question.ts` had been subtracting a flat 0.5 from both axes to compensate for diamonds. That only cancelled the bias when both signs came out positive, and doubled the error to a full unit when they did not — so the compensation goes along with the cause. **Rendered output moves by up to a pixel wherever a polygon shape terminates an edge.**

-   [#8048](mermaid-js/mermaid#8048) [`a6ec7ff`](mermaid-js/mermaid@a6ec7ff) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: treat `</br>` as a line break in labels

    `lineBreakRegex` only matched `<br>`, `<br/>` and `<br />`, so the malformed-but-common
    `</br>` survived as literal text wherever labels render as plain SVG text. HTML parsers
    already treat `</br>` as a `<br>`, so the tag appeared to work in HTML-label mode and
    failed everywhere else. It is now accepted on both paths.

    Also fixes `hasBreaks()` returning alternating results for the same input: it called
    `.test()` on a global regex, which advances `lastIndex` between calls. `wrapLabel()` used
    the same stateful check and could therefore re-wrap labels that already contained breaks.

-   [#8152](mermaid-js/mermaid#8152) [`1246a55`](mermaid-js/mermaid@1246a55) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: don't draw a line hop that has no room next to a bend.

    A crossing close to a corner used to get a hop squeezed into whatever space was left — as little as 2.9px against a requested 6px, opening exactly on the corner's tangent point. At that size the arc no longer clears the line it is meant to hop, so the two strokes still touch and the corner's curve runs straight into the arc's. It reads as a rendering fault rather than as a crossing.

    Hops now keep a straight run clear of the bend, and one that would still have to shrink below 60% of the requested radius is dropped instead of drawn. An undrawn hop is an ordinary crossing, which is a much better failure than a broken-looking one.

    **This changes swimlane diagrams as well as ELK ones.** Swimlanes are the existing consumer of line hops and have them on today, so a swimlane with a crossing near a bend will render differently even for someone not using ELK at all. It shows up wherever a layout stacks edges in narrow lanes: ELK routes subgraph-internal edges 10px apart, and 10px does not hold a 7.07px corner cut plus a 6px hop.

    A crossing is also ignored now when it lands inside the stretch where either edge is rounding a bend. Crossings are found on polylines, but a rounded edge is not drawn as its polyline — it leaves the line up to 7.07px before each bend and rejoins it that far after. A crossing found inside that stretch is somewhere the stroke never goes, so the hop was arching over blank paper while the two lines carried on touching beside it.

-   [#8146](mermaid-js/mermaid#8146) [`def4c81`](mermaid-js/mermaid@def4c81) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): `redux-color` and `redux-dark-color` now define every variable their base themes define, instead of silently falling back to untuned values for `primaryBorderColor`, `clusterBkg`, `clusterBorder`, `altBackground`, `compositeTitleBackground` and the state and requirement edge-label backgrounds. Pie, gantt and user-journey also read the theme palette rather than a single pale tint, so pie slices are distinguishable and gantt sections are visibly banded.

-   [#8189](mermaid-js/mermaid#8189) [`3802472`](mermaid-js/mermaid@3802472) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): venn circles follow the `redux-color` and `redux-dark-color` palettes. Neither theme defined any `venn*` variable, so every circle rendered in one flat colour.

-   [#8185](mermaid-js/mermaid#8185) [`f5af413`](mermaid-js/mermaid@f5af413) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: sequence diagram actors under the `neo` look and `redux` themes. Notes no longer render bold,
    a `rect` section band is no longer drawn white on white, and participants follow one vertical
    model: every lifeline starts and ends on a shared line, all participant glyphs are one size with
    their feet on a common edge, single-line labels share one baseline in the header and in the
    mirrored footer, and a multiline label grows away from the lifeline instead of moving it. The
    `classic` look is unchanged.

-   [#8157](mermaid-js/mermaid#8157) [`a3a92ba`](mermaid-js/mermaid@a3a92ba) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(sequence): index each actor colour palette by its own length.

    Every actor-drawing call site read `bkgColorArray[actorCount % borderColorArray.length]` — one palette indexed by the other's length. Under `redux-color` both palettes have twelve entries, so the wrong length happens to give the right answer and nothing is visibly broken today. It goes wrong as soon as the two differ in length: the overflow actors resolve to `undefined`, and d3 then strips the inline fill for some actors and not others.

    Both palettes now cycle within their own length, via a shared helper. An absent or empty palette still yields `undefined` rather than a substitute colour, which is what `redux-dark-color` relies on: it ships a border palette and an empty background palette so actors are outlined but not filled.

-   [#8232](mermaid-js/mermaid#8232) [`e691042`](mermaid-js/mermaid@e691042) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Circle, double circle, Delay and Display nodes size themselves from the label's height as well as its width, so a wrapped label stays inside the outline. Each was sized from the width alone and clipped any label taller than it was wide, which `flowchart.wrappingWidth: 120` made common.

    This replaces the wrapping exemption those shapes carried along with stadium and diamond: it stopped them wrapping at all, so a long label produced a single-line node hundreds of pixels wide. Stadium needs no exemption — its `w >= 1.5h` floor already keeps a wrapped label from closing the caps into a circle — and diamond never needed one.

    Stacked (`docs`) and lined (`lin-doc`) document nodes lift their label clear of the wavy bottom edge by the wave's full depth rather than half of it, so the last line is no longer clipped by the trough. Plain `doc` already did this; the two variants had copied it with the offset halved. Node dimensions are unchanged.

-   [#8154](mermaid-js/mermaid#8154) [`ba0deed`](mermaid-js/mermaid@ba0deed) Thanks [@aloisklink](https://github.com/aloisklink)! - fix: bundle `fastdom` to fix issues with global `define`

-   [#8227](mermaid-js/mermaid#8227) [`b05824a`](mermaid-js/mermaid@b05824a) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(flowchart): stop stadium labels from wrapping into a circle and exempt selected shapes from automatic label wrapping.

    Long stadium labels wrapped to `flowchart.wrappingWidth`, grew taller without growing wider, and turned the stadium into a circle. Stadium now keeps its semicircular caps with an outer width of at least 1.5 times its height and enough clearance that the label stays inside the arcs.

    Stadium (`terminal`, `pill`), circle (`circ`), diamond (`diam`, `decision`, `question`), double circle (`dbl-circ`, `double-circle`), Display (`curv-trap`, `curved-trapezoid`, `display`) and Delay (`delay`, `half-rounded-rectangle`) no longer wrap their labels at `flowchart.wrappingWidth`. Explicit line breaks are kept, and an explicit node width still takes precedence.

-   [#8192](mermaid-js/mermaid#8192) [`0ec29e9`](mermaid-js/mermaid@0ec29e9) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(state): draw the edge between a state and its note dashed under the `neo` look. It was rendering solid.

    The dashes were expressed only as CSS — `.note-edge { stroke-dasharray: 5 }` — which is enough under `classic` but not under `neo`, where `insertEdge` writes an inline `stroke-dasharray` on every edge, computed from the path length so the arrow markers keep their gaps. An inline style outranks a stylesheet rule, so the note edge took the solid pattern and the `.note-edge` rule simply lost. The note edge now declares `pattern: 'dashed'`, which is what `insertEdge` reads to choose its dash generator. `classic` is unchanged.

-   [#8225](mermaid-js/mermaid#8225) [`0d77926`](mermaid-js/mermaid@0d77926) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(swimlane): seed cycle removal from source nodes so feedback edges are reversed instead of forward flow edges. A cycle like `task --> decision --> fix --> task` no longer places `fix` in the middle of the `start --> task` flow line.

-   [#8211](mermaid-js/mermaid#8211) [`93edd72`](mermaid-js/mermaid@93edd72) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: keep association markers apart on use-case ovals under ELK. Ovals get the same vertical padding as horizontal, edge attachment points on them are spread across the side, and the ELK renderer now accounts for every end-marker offset (extension, circle, cross, …) so a short entry stub can no longer flip a marker around.

-   Updated dependencies \[[`33442fd`](mermaid-js/mermaid@33442fd)]:
    -   @mermaid-js/parser@2.0.0

### Keeping the previous defaults in your integration

Mermaid 12 changes what unconfigured diagrams look like: ELK replaces dagre as the default layout, and `redux-color` / `neo` replace `default` / `classic` as theme and look for flowchart, class, state, ER, requirement, sequence, use case, swimlane, Venn and agentflow. If you embed Mermaid and want your users' existing diagrams to render as before, set all three explicitly at initialisation:

```js
mermaid.initialize({ layout: 'dagre', theme: 'default', look: 'classic' });
```

Diagram authors can do the same per diagram in front matter, and a value there always wins over `initialize()`.

- [Layouts: how `layout` is resolved, ELK variants, and the tiny build](https://mermaid.ai/open-source/config/layouts.html)
- [Flowchart: default theme, look and layout in v12](https://mermaid.ai/open-source/syntax/flowchart.html#default-theme-look-and-layout-v-mermaid-release-version)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: give me a 98K <240642031+inhuman-0@users.noreply.github.com>
github-actions Bot added a commit to zhongmiao-org/mermaid-markdown-bridge that referenced this pull request Sep 10, 2026
Upstream release: https://github.com/mermaid-js/mermaid/releases/tag/mermaid%4012.0.0

Release notes:
**Mermaid 12.0.0** ships ELK as the bundled default layout engine, a new `redux-color` / `neo` default appearance, and two new diagram types: [UML use case diagrams](https://mermaid.ai/open-source/syntax/usecase.html).

This is a breaking release (ES2024, Safari 17.4+, Node 22.12+). Existing flowcharts, state and class diagrams will re-lay out and recolour; add `layout: dagre`, `theme: default` and `look: classic` to your config to keep the old look. Full changelog below.

### Major Changes

-   [#8213](mermaid-js/mermaid#8213) [`33442fd`](mermaid-js/mermaid@33442fd) Thanks [@aloisklink](https://github.com/aloisklink)! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+

    Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support
    older browsers, you may need to polyfill or transpile mermaid.

    Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome,
    older iOS devices don't get major Safari updates.

    Node.JS v22.12+ is also declared as requirement in our `package.json` files,
    but as mermaid requires a browser, this is mainly so we can use dependencies that
    also declare a Node.JS v22.12+ requirement, without causing issues for users when
    running `npm install`.

-   [#8155](mermaid-js/mermaid#8155) [`810893c`](mermaid-js/mermaid@810893c) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat!: **ELK is now bundled with mermaid and is the default layout algorithm.**

    ELK previously shipped as a separate `@mermaid-js/layout-elk` package that sites had to install and register. It is now part of mermaid itself and registered automatically, so `layout: elk` — and the `elk.stress`, `elk.force`, `elk.mrtree`, `elk.sporeOverlap`, `elk.box` and `elk.rectpacking` variants — work with no setup.

    **This changes how existing diagrams look.** Flowchart, state, class, entity-relationship, requirement, use-case diagrams that do not specify a `layout` are now laid out by ELK instead of dagre. To keep the previous layout, set dagre explicitly:

    ```yaml
    ---
    config:
      layout: dagre
    ---
    ```

    or globally, `mermaid.initialize({ layout: 'dagre' })`.

    Mindmap is unchanged: it keeps laying out with cose-bilkent unless a layout is explicitly requested (in the tiny build, which ships neither ELK nor cose-bilkent, it falls back to dagre).

    Other notes:

    -   ELK is loaded as a separate chunk in the ESM builds, so it is only fetched when a diagram actually uses it. The single-file IIFE build (`mermaid.min.js`) inlines it and grows by roughly 500 kB gzipped.
    -   The **tiny** build deliberately omits ELK to stay small, and falls back to dagre for diagrams that ask for an ELK layout. Its size is unchanged.
    -   `@mermaid-js/layout-elk` is no longer needed on normal builds — existing `mermaid.registerLayoutLoaders(elkLayouts)` calls keep working and can be removed. It is still published, and remains the way to add ELK to the tiny build.
    -   `dist/mermaid.esm.min.mjs` now contains syntax that `es-module-lexer` (used by Vite) rejects with `content contains invalid JS syntax`. Bundler users are unaffected as long as they import the `mermaid` package specifier, which resolves to the core build; only builds that point Vite directly at that dist file need to switch to the package specifier or the core build.
    -   State diagrams now resolve their layout through the same registration check as every other diagram, so an unavailable layout falls back instead of failing to render.

    An unrecognised `theme` name now resolves to the default theme in name as well as in variables; previously the invalid name stayed in place while the default's variables were loaded, and every palette-aware stylesheet gates on the name. `theme: 'null'`, the documented way to disable the pre-defined themes, is unaffected. Note that `neo` paints node strokes with a gradient when the theme sets `useGradient`, which `base` does; setting a custom `nodeBorder` on `base` turns the gradient off.

-   [#8211](mermaid-js/mermaid#8211) [`a19bd08`](mermaid-js/mermaid@a19bd08) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - **Removed: the `defaultRenderer` option of the `flowchart`, `class` and `state` config sections.**

    Use the top-level `layout` option instead:

    ```yaml
    ---
    config:
      layout: dagre # or elk
    ---
    ```

    ### Why

    `defaultRenderer` named a rendering engine back when there was more than one. There is not.
    All of its accepted values — `dagre`, `dagre-wrapper`, `dagre-d3` — had come to select the
    same unified renderer, and its `elk` value did nothing but set `layout: elk` on your behalf.
    The option's documented purpose no longer existed.

    What it still did was stranger than doing nothing. The detectors branched on whether the
    value was _recognised_, so a **valid** value routed a diagram to the modern renderer and an
    **invalid** one silently routed it to the legacy renderer. Since the default was always a
    valid value, the legacy `class` and `state` renderers were unreachable except by
    misconfiguring the option.

    ### What changes for you

    -   `layout` selects the layout algorithm, as it already did. Nothing else does.
    -   `graph`, `classDiagram` and `stateDiagram` always render with the unified diagram. This
        was already true for every valid configuration.
    -   The legacy `flowchart`, `class` and `state` diagram ids are gone. `flowchart-v2`,
        `classDiagram` and `stateDiagram` are what `detectType` now returns for that syntax.
    -   `flowchart-elk` as an explicit diagram keyword still works.

    Configuration that still sets `defaultRenderer` is ignored rather than rejected, so nothing
    throws — but it no longer has any effect, and diagrams that relied on
    `flowchart: { defaultRenderer: 'elk' }` to get an ELK layout should set `layout: elk`.

-   [#8223](mermaid-js/mermaid#8223) [`6df1149`](mermaid-js/mermaid@6df1149) Thanks [@aloisklink](https://github.com/aloisklink)! - refactor!: remove layout internal exports

    Remove the
    `clearLayoutRenderState`, `createCommonLayoutRenderer`, `defaultMeasureLayout`, `paintLayoutData` functions,
    and the `CommonLayout*` types from Mermaid's public API.

    These functions were only used by the `@mermaid-js/layout-elk` package,
    which now bundles them. Removing them makes it easier to avoid major versions
    of mermaid.

### Minor Changes

-   [#8048](mermaid-js/mermaid#8048) [`7a3c1a8`](mermaid-js/mermaid@7a3c1a8) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: add UML use case diagrams with actors, use cases, system boundaries, relationships, stereotypes, notes, JSON tables, class and style support, accessibility metadata, and business variants.

-   [#8073](mermaid-js/mermaid#8073) [`cd48a64`](mermaid-js/mermaid@cd48a64) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: `defaultMeasureLayout` accepts a `unwrapGroupLabels` option so a layout engine can ask for cluster labels to be measured at their natural width instead of the `flowchart.wrappingWidth` fallback.

    `insertCluster` paints a plain cluster label with an infinite width while the measurement pass wrapped it at 200px, so a layout that sizes compound nodes from the measured label sized them too narrow. Layouts now opt in explicitly; core no longer inspects the layout's name. Markdown cluster labels stay wrapped, since those are painted wrapped.

    The bundled ELK layout opts in, which changes the size of labelled subgraphs in ELK-laid-out diagrams. Diagrams laid out with dagre are unaffected.

-   [#8073](mermaid-js/mermaid#8073) [`0cf3797`](mermaid-js/mermaid@0cf3797) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: let a container pick its own ELK algorithm with `@{ algorithm: … }`, and add `elk.box` and `elk.rectpacking` to the selectable `layout` values.

    A subgraph carrying `@{ algorithm: elk.box }` is laid out with that algorithm in its own coordinate system instead of inheriting the diagram's. Supported values are `elk.layered`, `elk.box`, `elk.rectpacking`, `elk.stress`, `elk.force`, `elk.mrtree`, `elk.radial`, and `elk.sporeOverlap`; anything else is ignored with a warning rather than handed to ELK, where an unknown id would abort the layout. Containers with edges crossing their boundary fall back to the inherited algorithm, since isolated layout and cross-boundary edges are incompatible.

    Also in this release:

    -   ELK cluster labels are measured unwrapped, so a compound node is sized to fit its label instead of to a 200px wrapping width. **This changes the size — and therefore the layout — of existing ELK diagrams that have labelled subgraphs.**
    -   `elk.box` and `elk.rectpacking` place nodes but never route edges. Edges they leave unrouted now fall back to a straight line between the two node centres instead of failing the render.
    -   `keepEntryNodeOnTop` pins a cycle's true entry rather than the first-declared node when a back-edge feeds the entry, so recursive flows read from where they actually start.

-   [#8193](mermaid-js/mermaid#8193) [`813c766`](mermaid-js/mermaid@813c766) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - **`theme`, `look` and `layout` can now be set per diagram type**, in each diagram's config section — `mermaid.initialize({ look: 'classic', flowchart: { look: 'handDrawn' } })`, or the same under `config` in front matter. The schema uses the same mechanism to give a diagram type its own default. Resolution, highest first: front matter or directive, `initialize()`, the diagram type's default, the global default; a diagram-scoped value beats a global one set in the same layer.

    Swimlanes take `layout: swimlane` from that schema default instead of having it forced by their `init` hook, so `swimlane: { layout: ... }` and a `layout` in front matter are now honoured. An unregistered layout also always falls back to `dagre` with a warning rather than throwing — state diagrams skipped that fallback, and mindmaps threw when `cose-bilkent` was absent, as it is in `@mermaid-js/tiny`.

-   [#8147](mermaid-js/mermaid#8147) [`43d9fbc`](mermaid-js/mermaid@43d9fbc) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): class boxes and flowchart subgraph containers now take a per-item colour under the `redux-color` and `redux-dark-color` themes, cycling every 12 as ER entities already do. Collapsed subgraphs keep the slot they would have had expanded; nodes inside a subgraph stay uniform, and explicit `classDef` or `style` still wins over the palette.

-   [#8191](mermaid-js/mermaid#8191) [`2878cf3`](mermaid-js/mermaid@2878cf3) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): composite states now take a per-container colour under the `redux-color` and `redux-dark-color` themes, as flowchart subgraphs already do. Each composite gets the palette's border colour on its outline and its background tint behind the title strip; nested composites each take the next colour, so depth stays readable. The body keeps the theme's own `compositeBackground`. `redux-dark-color` colours the outlines only, matching how it treats ER, requirement and sequence.

    The concurrency regions produced by a `--` divider share the colour of the composite they split, rather than taking one of their own — the author wrote a single composite, so it is drawn as one thing in parts. Adding a `--` therefore leaves every other composite's colour untouched.

    Under the `handDrawn` look, concurrency regions are now filled solid rather than hatched, so that they can carry the palette tint the same way every other look does.

    States inside a composite stay uniform. A composite carrying its own `classDef` or `style` keeps those colours and takes no palette slot, and neither do its concurrency regions; the slot is still spent, so styling one composite does not shift the colours of the ones after it. Note that this opt-out is all-or-nothing: a `classDef` that sets only text properties, such as `font-weight`, still takes that composite out of the palette.

-   [#8176](mermaid-js/mermaid#8176) [`16b9a7d`](mermaid-js/mermaid@16b9a7d) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): swimlane lanes take a per-lane colour under the `redux-color` and `redux-dark-color` themes, cycling every 12 as flowchart subgraphs do. The lane holding ungrouped nodes takes its own slot and now follows the diagram's `look`.

-   [#8178](mermaid-js/mermaid#8178) [`75e6c30`](mermaid-js/mermaid@75e6c30) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat(usecase): use case diagrams now take colour by role — actors, use cases and system boundaries each get their own colour from the new `usecaseActorBkg`/`usecaseActorBorder`, `usecaseBkg`/`usecaseBorder` and `usecaseBoundaryBkg`/`usecaseBoundaryBorder` theme variables, with `usecaseIncludeLine`/`usecaseExtendLine` separating the two dashed relationship kinds by hue. `redux-color` and `redux-dark-color` set them; every other theme is unchanged. Colour is keyed to the kind of element, so editing a diagram never recolours the elements around the edit. Set `usecase.colorScheme: 'rotate'` for the per-element palette cycle instead, and `classDef`/`style` still wins over both.

### Patch Changes

-   [#8211](mermaid-js/mermaid#8211) [`dc2e453`](mermaid-js/mermaid@dc2e453) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(block): restore node borders under looks that use a gradient stroke.

    A block diagram rendered with `look: neo` lost its node borders entirely. The look-specific
    rules set `stroke: url(#<svgId>-gradient)`, and those definitions were appended by
    `rendering-util/render.ts` — which block does not use, since it runs its own render loop.
    An `url(#…)` paint that resolves to nothing is painted as **none**, not as a fallback
    colour, so the border disappeared rather than degrading to a plain stroke.

    The definitions now live in `rendering-util/insertLookDefs.ts` and both render paths call
    it, so the two cannot drift into producing different gradients.

    This only became visible recently: block did not previously pass `look` to its nodes, so
    none of the look-specific rules applied to it and the missing definitions went unnoticed.

-   [#8181](mermaid-js/mermaid#8181) [`ce0302d`](mermaid-js/mermaid@ce0302d) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(block): apply the redux colour palette to composite blocks. Composites now take a per-container colour under `redux-color` and `redux-dark-color`, matching how flowchart subgraphs are coloured — one counter over containers, in declaration order, with the plain shapes left on the flat theme colour. The palette is opt-in: block does not default to `redux-color` the way flowchart and several other diagram types do, so set `theme: redux-color` (or `redux-dark-color`) explicitly to see it. `classDef`/`style` still win.

-   [#8211](mermaid-js/mermaid#8211) [`ad98070`](mermaid-js/mermaid@ad98070) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(block): take the node border width from the theme, as every other diagram does.

    `block/styles.ts` pinned `stroke-width: 1px` on node shapes while the flowchart, class and
    state stylesheets all read `strokeWidth` from the theme. Under a theme that asks for more —
    `neo` asks for 2 — a block rendered a visibly thinner border than an identical flowchart
    node, and nothing downstream corrected it: the `neo` rules in the shared stylesheet set
    `stroke` and `filter`, never `stroke-width`.

    The gap was invisible until block began propagating `look` to its nodes, because until then
    none of the look-specific rules applied to block at all.

    Affects only themes whose `strokeWidth` is not 1. `neo` is 2; `neo-dark`, `base` and the
    rest are unchanged.

-   [#8231](mermaid-js/mermaid#8231) [`e2aab3f`](mermaid-js/mermaid@e2aab3f) Thanks [@knsv-bot](https://github.com/knsv-bot)! - Use balanced Brandes-Koepf placement for the default ELK preset to improve alignment of branches and composite-state entries. Preserve named non-default presets and explicit placement/alignment options, including `NONE`. Some diagrams may become wider or taller; `depthFirst` retains the previous default layout.

-   [#7874](mermaid-js/mermaid#7874) [`6b79518`](mermaid-js/mermaid@6b79518) Thanks [@filipsajdak](https://github.com/filipsajdak)! - fix(c4): allow boundaries as relationship endpoints

      A `Rel` (or `BiRel`/`Rel_*`) that referenced a boundary alias threw `references an unknown
    shape`, because endpoint lookup only searched shapes and not boundaries. Boundary aliases now
      resolve as relationship endpoints. Fixes #4864.

-   [#8100](mermaid-js/mermaid#8100) [`30325d4`](mermaid-js/mermaid@30325d4) Thanks [@filipsajdak](https://github.com/filipsajdak)! - fix(c4): stop wrapping non-text named attributes that land in a text slot

    A named attribute such as `$tags` or `$sprite` can arrive in the positional
    slot of a text field when the argument before it is omitted. It was then
    stored as `{ text: value }` rather than a string, so `$tags` given without a
    description crashed rendering.

-   [#8148](mermaid-js/mermaid#8148) [`c3ee3c7`](mermaid-js/mermaid@c3ee3c7) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): `redux-dark`, `redux-dark-color` and `neo-dark` shipped `secondBkg` as the literal string `calculated`, so railroad rendered the invalid `fill: calculated`; it is now computed as `theme-dark` does. The same three themes had gantt done-task labels at 1.07:1 contrast — a light fill under their light task ink — now 5.7:1 or better. The ER and requirement stylesheets also validate `look` before interpolating it into a CSS selector.

-   [#8222](mermaid-js/mermaid#8222) [`1ee934d`](mermaid-js/mermaid@1ee934d) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: stop ELK-laid-out diagrams logging `Unknown arrow type: arrow_open` once per edge.

    ELK's arrow table put the edge _type_ `arrow_open` — which means "no arrowheads" — into the arrow-type slot, where `none` is the value that marks a deliberate absence. Anything else is reported as an unknown marker name. Diagrams whose db sets `arrowTypeStart` itself, such as flowcharts, never reached that fallback; state diagrams, which leave it unset, warned on every edge.

    Rendering is unchanged: both spellings produced no start marker and the same geometry, since neither has a marker offset. Only the log noise goes away.

-   [#8204](mermaid-js/mermaid#8204) [`a7831c5`](mermaid-js/mermaid@a7831c5) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: keep ELK subgraph frames out to the border anchor of edges that terminate on the group, so transitions to composite states no longer run along the cluster border

-   [#8155](mermaid-js/mermaid#8155) [`0320406`](mermaid-js/mermaid@0320406) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(elk): a composite state or subgraph containing a loop now opens on its own start node.

    Cycle breaking was resolved for the root graph but never passed to containers, so a
    container laid out on its own fell back to ELK's default, `GREEDY`, while the root ran
    whatever `elk.preset` asked for. The two then reversed different edges of the same cycle.

    In a composite state that loops, greedy reverses the edge that turns an ordinary state
    into a source, and sources sit on the first layer — so the diagram opened on that state
    alongside its start circle instead of on the start circle alone. Dagre reverses the other
    edge, which is why the two layout engines disagreed on the same diagram.

    Containers now resolve cycle breaking from the preset exactly as the root does. Only
    containers that actually contain a cycle change; an acyclic subgraph gives a cycle-breaking
    strategy nothing to do. `preset: legacy` still reaches containers with `GREEDY`, so it keeps
    reproducing the earlier rendering inside frames as well as outside them.

-   [#8228](mermaid-js/mermaid#8228) [`e36b883`](mermaid-js/mermaid@e36b883) Thanks [@knsv-bot](https://github.com/knsv-bot)! - Reserve subgraph title padding before ELK routes edges so attachments stay on the painted frame. Clip stale interior endpoints along the incoming segment, avoiding edges that run along the subgraph border.

-   [#8199](mermaid-js/mermaid#8199) [`b993915`](mermaid-js/mermaid@b993915) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: keep ELK edges vertical at small nodes — the node now moves onto the routed line instead of the edge bending toward the node's off-centre position

-   [#8155](mermaid-js/mermaid#8155) [`4e00c5c`](mermaid-js/mermaid@4e00c5c) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: render ER relationship edges above subgraph backgrounds under the ELK layout instead of hiding them behind the cluster rect

-   [#8156](mermaid-js/mermaid#8156) [`3f05015`](mermaid-js/mermaid@3f05015) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(er, requirement, timeline): stop the ER, requirement and timeline stylesheets emitting CSS the browser discards for the colour themes.

    All three generate one rule per palette slot. ER and requirement looped to `THEME_COLOR_LIMIT` and indexed the palette by the loop counter, which goes wrong in both directions: a palette shorter than the limit emitted `stroke: undefined` for the overflow slots, and a palette _longer_ than it left entities stamped with a slot that had no rule at all, rendering unstyled beside coloured neighbours. Both now take the loop bound from the palette itself, which is the same length the boxes stamp with — so the rules emitted and the slots stamped cannot disagree. Both also bail before the loop for an empty palette, since wrapping alone is not enough there: `i % 0` is `NaN` and `[][NaN]` is `undefined`.

    Timeline keeps looping to `THEME_COLOR_LIMIT` and wrapping, because it numbers `.section-N` classes rather than palette slots — nothing stamps those, so the palette cycles across however many sections exist.

    `requirement` also emitted `fill: ;` — a property with no value, which is invalid — whenever there was no background palette. That is the live case for `redux-dark-color`, which ships a border palette and no background palette so that it colours outlines only. The declaration is now omitted instead.

    None of these raises an error: the browser discards the invalid declaration, so the only symptom is a shape rendering unstyled.

-   [#8149](mermaid-js/mermaid#8149) [`d57ed55`](mermaid-js/mermaid@d57ed55) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(docs): stop `docs:build` deleting the committed `docs/` directory when a later step fails.

-   [#7906](mermaid-js/mermaid#7906) [`1fc5bb3`](mermaid-js/mermaid@1fc5bb3) Thanks [@knsv-bot](https://github.com/knsv-bot)! - perf: avoid quadratic parse cost on deeply-indented diagrams (~1.4 s → ~30 ms on a pathological fixture)

-   [#8211](mermaid-js/mermaid#8211) [`b979eb4`](mermaid-js/mermaid@b979eb4) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(handdrawn): tighten the hachure fill so shapes read as filled rather than striped.

    The hand-drawn fill drew 4px-wide strokes 5.2px apart, which left visible diagonal
    banding across every shape — legible as a texture up close, but at normal diagram size it
    read as stripes rather than a fill. Strokes are now 1.5px at 1.5px spacing, so the fill
    reads as an even tint while keeping the drawn edge quality.

    The striped fill behind state-diagram start, end and fork markers moves the same way, so
    those come out close to solid — which is what the UML convention asks for anyway.

    Swimlane lane titles previously pinned their own stroke weight and so ignored the shared
    value; they now follow it like every other shape.

-   [#8152](mermaid-js/mermaid#8152) [`7ee4c3f`](mermaid-js/mermaid@7ee4c3f) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: edges attach to non-rectangular shapes on the outline instead of half a pixel off it.

    **This is not ELK-specific.** `intersectPolygon` is how every non-rectangular shape finds its edge attachment — diamond, stadium, hexagon, trapezoid, subroutine — in every layout, so a dagre-rendered flowchart with a decision diamond is affected exactly as much as an ELK one.

    `intersectLine` comes from Graphics Gems, where the coordinates were integers and the numerator was nudged half a denominator away from zero so the integer division rounded instead of truncating. JavaScript division does neither, so the nudge stopped being a correction and became the whole error: every result came back displaced by `0.5 * sign(num) * sign(denom)`. The magnitude is always exactly half a unit, but the sign is per axis, because the numerator is computed separately for x and y while the denominator is shared — so the two axes could move the same way or opposite ways depending on the geometry, which is why it never looked like a constant offset anyone could spot by eye.

    That was enough to give an otherwise orthogonal edge a tiny diagonal opening segment, and to put an attachment just inside the node it was meant to touch.

    `question.ts` had been subtracting a flat 0.5 from both axes to compensate for diamonds. That only cancelled the bias when both signs came out positive, and doubled the error to a full unit when they did not — so the compensation goes along with the cause. **Rendered output moves by up to a pixel wherever a polygon shape terminates an edge.**

-   [#8048](mermaid-js/mermaid#8048) [`a6ec7ff`](mermaid-js/mermaid@a6ec7ff) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: treat `</br>` as a line break in labels

    `lineBreakRegex` only matched `<br>`, `<br/>` and `<br />`, so the malformed-but-common
    `</br>` survived as literal text wherever labels render as plain SVG text. HTML parsers
    already treat `</br>` as a `<br>`, so the tag appeared to work in HTML-label mode and
    failed everywhere else. It is now accepted on both paths.

    Also fixes `hasBreaks()` returning alternating results for the same input: it called
    `.test()` on a global regex, which advances `lastIndex` between calls. `wrapLabel()` used
    the same stateful check and could therefore re-wrap labels that already contained breaks.

-   [#8152](mermaid-js/mermaid#8152) [`1246a55`](mermaid-js/mermaid@1246a55) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: don't draw a line hop that has no room next to a bend.

    A crossing close to a corner used to get a hop squeezed into whatever space was left — as little as 2.9px against a requested 6px, opening exactly on the corner's tangent point. At that size the arc no longer clears the line it is meant to hop, so the two strokes still touch and the corner's curve runs straight into the arc's. It reads as a rendering fault rather than as a crossing.

    Hops now keep a straight run clear of the bend, and one that would still have to shrink below 60% of the requested radius is dropped instead of drawn. An undrawn hop is an ordinary crossing, which is a much better failure than a broken-looking one.

    **This changes swimlane diagrams as well as ELK ones.** Swimlanes are the existing consumer of line hops and have them on today, so a swimlane with a crossing near a bend will render differently even for someone not using ELK at all. It shows up wherever a layout stacks edges in narrow lanes: ELK routes subgraph-internal edges 10px apart, and 10px does not hold a 7.07px corner cut plus a 6px hop.

    A crossing is also ignored now when it lands inside the stretch where either edge is rounding a bend. Crossings are found on polylines, but a rounded edge is not drawn as its polyline — it leaves the line up to 7.07px before each bend and rejoins it that far after. A crossing found inside that stretch is somewhere the stroke never goes, so the hop was arching over blank paper while the two lines carried on touching beside it.

-   [#8146](mermaid-js/mermaid#8146) [`def4c81`](mermaid-js/mermaid@def4c81) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): `redux-color` and `redux-dark-color` now define every variable their base themes define, instead of silently falling back to untuned values for `primaryBorderColor`, `clusterBkg`, `clusterBorder`, `altBackground`, `compositeTitleBackground` and the state and requirement edge-label backgrounds. Pie, gantt and user-journey also read the theme palette rather than a single pale tint, so pie slices are distinguishable and gantt sections are visibly banded.

-   [#8189](mermaid-js/mermaid#8189) [`3802472`](mermaid-js/mermaid@3802472) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): venn circles follow the `redux-color` and `redux-dark-color` palettes. Neither theme defined any `venn*` variable, so every circle rendered in one flat colour.

-   [#8185](mermaid-js/mermaid#8185) [`f5af413`](mermaid-js/mermaid@f5af413) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: sequence diagram actors under the `neo` look and `redux` themes. Notes no longer render bold,
    a `rect` section band is no longer drawn white on white, and participants follow one vertical
    model: every lifeline starts and ends on a shared line, all participant glyphs are one size with
    their feet on a common edge, single-line labels share one baseline in the header and in the
    mirrored footer, and a multiline label grows away from the lifeline instead of moving it. The
    `classic` look is unchanged.

-   [#8157](mermaid-js/mermaid#8157) [`a3a92ba`](mermaid-js/mermaid@a3a92ba) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(sequence): index each actor colour palette by its own length.

    Every actor-drawing call site read `bkgColorArray[actorCount % borderColorArray.length]` — one palette indexed by the other's length. Under `redux-color` both palettes have twelve entries, so the wrong length happens to give the right answer and nothing is visibly broken today. It goes wrong as soon as the two differ in length: the overflow actors resolve to `undefined`, and d3 then strips the inline fill for some actors and not others.

    Both palettes now cycle within their own length, via a shared helper. An absent or empty palette still yields `undefined` rather than a substitute colour, which is what `redux-dark-color` relies on: it ships a border palette and an empty background palette so actors are outlined but not filled.

-   [#8232](mermaid-js/mermaid#8232) [`e691042`](mermaid-js/mermaid@e691042) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Circle, double circle, Delay and Display nodes size themselves from the label's height as well as its width, so a wrapped label stays inside the outline. Each was sized from the width alone and clipped any label taller than it was wide, which `flowchart.wrappingWidth: 120` made common.

    This replaces the wrapping exemption those shapes carried along with stadium and diamond: it stopped them wrapping at all, so a long label produced a single-line node hundreds of pixels wide. Stadium needs no exemption — its `w >= 1.5h` floor already keeps a wrapped label from closing the caps into a circle — and diamond never needed one.

    Stacked (`docs`) and lined (`lin-doc`) document nodes lift their label clear of the wavy bottom edge by the wave's full depth rather than half of it, so the last line is no longer clipped by the trough. Plain `doc` already did this; the two variants had copied it with the offset halved. Node dimensions are unchanged.

-   [#8154](mermaid-js/mermaid#8154) [`ba0deed`](mermaid-js/mermaid@ba0deed) Thanks [@aloisklink](https://github.com/aloisklink)! - fix: bundle `fastdom` to fix issues with global `define`

-   [#8227](mermaid-js/mermaid#8227) [`b05824a`](mermaid-js/mermaid@b05824a) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(flowchart): stop stadium labels from wrapping into a circle and exempt selected shapes from automatic label wrapping.

    Long stadium labels wrapped to `flowchart.wrappingWidth`, grew taller without growing wider, and turned the stadium into a circle. Stadium now keeps its semicircular caps with an outer width of at least 1.5 times its height and enough clearance that the label stays inside the arcs.

    Stadium (`terminal`, `pill`), circle (`circ`), diamond (`diam`, `decision`, `question`), double circle (`dbl-circ`, `double-circle`), Display (`curv-trap`, `curved-trapezoid`, `display`) and Delay (`delay`, `half-rounded-rectangle`) no longer wrap their labels at `flowchart.wrappingWidth`. Explicit line breaks are kept, and an explicit node width still takes precedence.

-   [#8192](mermaid-js/mermaid#8192) [`0ec29e9`](mermaid-js/mermaid@0ec29e9) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(state): draw the edge between a state and its note dashed under the `neo` look. It was rendering solid.

    The dashes were expressed only as CSS — `.note-edge { stroke-dasharray: 5 }` — which is enough under `classic` but not under `neo`, where `insertEdge` writes an inline `stroke-dasharray` on every edge, computed from the path length so the arrow markers keep their gaps. An inline style outranks a stylesheet rule, so the note edge took the solid pattern and the `.note-edge` rule simply lost. The note edge now declares `pattern: 'dashed'`, which is what `insertEdge` reads to choose its dash generator. `classic` is unchanged.

-   [#8225](mermaid-js/mermaid#8225) [`0d77926`](mermaid-js/mermaid@0d77926) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(swimlane): seed cycle removal from source nodes so feedback edges are reversed instead of forward flow edges. A cycle like `task --> decision --> fix --> task` no longer places `fix` in the middle of the `start --> task` flow line.

-   [#8211](mermaid-js/mermaid#8211) [`93edd72`](mermaid-js/mermaid@93edd72) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: keep association markers apart on use-case ovals under ELK. Ovals get the same vertical padding as horizontal, edge attachment points on them are spread across the side, and the ELK renderer now accounts for every end-marker offset (extension, circle, cross, …) so a short entry stub can no longer flip a marker around.

-   Updated dependencies \[[`33442fd`](mermaid-js/mermaid@33442fd)]:
    -   @mermaid-js/parser@2.0.0

### Keeping the previous defaults in your integration

Mermaid 12 changes what unconfigured diagrams look like: ELK replaces dagre as the default layout, and `redux-color` / `neo` replace `default` / `classic` as theme and look for flowchart, class, state, ER, requirement, sequence, use case, swimlane, Venn and agentflow. If you embed Mermaid and want your users' existing diagrams to render as before, set all three explicitly at initialisation:

```js
mermaid.initialize({ layout: 'dagre', theme: 'default', look: 'classic' });
```

Diagram authors can do the same per diagram in front matter, and a value there always wins over `initialize()`.

- [Layouts: how `layout` is resolved, ELK variants, and the tiny build](https://mermaid.ai/open-source/config/layouts.html)
- [Flowchart: default theme, look and layout in v12](https://mermaid.ai/open-source/syntax/flowchart.html#default-theme-look-and-layout-v-mermaid-release-version)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: give me a 98K <240642031+inhuman-0@users.noreply.github.com>
github-actions Bot added a commit to zhongmiao-org/mermaid-markdown-bridge that referenced this pull request Sep 11, 2026
Upstream release: https://github.com/mermaid-js/mermaid/releases/tag/mermaid%4012.0.0

Release notes:
**Mermaid 12.0.0** ships ELK as the bundled default layout engine, a new `redux-color` / `neo` default appearance, and two new diagram types: [UML use case diagrams](https://mermaid.ai/open-source/syntax/usecase.html).

This is a breaking release (ES2024, Safari 17.4+, Node 22.12+). Existing flowcharts, state and class diagrams will re-lay out and recolour; add `layout: dagre`, `theme: default` and `look: classic` to your config to keep the old look. Full changelog below.

### Major Changes

-   [#8213](mermaid-js/mermaid#8213) [`33442fd`](mermaid-js/mermaid@33442fd) Thanks [@aloisklink](https://github.com/aloisklink)! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+

    Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support
    older browsers, you may need to polyfill or transpile mermaid.

    Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome,
    older iOS devices don't get major Safari updates.

    Node.JS v22.12+ is also declared as requirement in our `package.json` files,
    but as mermaid requires a browser, this is mainly so we can use dependencies that
    also declare a Node.JS v22.12+ requirement, without causing issues for users when
    running `npm install`.

-   [#8155](mermaid-js/mermaid#8155) [`810893c`](mermaid-js/mermaid@810893c) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat!: **ELK is now bundled with mermaid and is the default layout algorithm.**

    ELK previously shipped as a separate `@mermaid-js/layout-elk` package that sites had to install and register. It is now part of mermaid itself and registered automatically, so `layout: elk` — and the `elk.stress`, `elk.force`, `elk.mrtree`, `elk.sporeOverlap`, `elk.box` and `elk.rectpacking` variants — work with no setup.

    **This changes how existing diagrams look.** Flowchart, state, class, entity-relationship, requirement, use-case diagrams that do not specify a `layout` are now laid out by ELK instead of dagre. To keep the previous layout, set dagre explicitly:

    ```yaml
    ---
    config:
      layout: dagre
    ---
    ```

    or globally, `mermaid.initialize({ layout: 'dagre' })`.

    Mindmap is unchanged: it keeps laying out with cose-bilkent unless a layout is explicitly requested (in the tiny build, which ships neither ELK nor cose-bilkent, it falls back to dagre).

    Other notes:

    -   ELK is loaded as a separate chunk in the ESM builds, so it is only fetched when a diagram actually uses it. The single-file IIFE build (`mermaid.min.js`) inlines it and grows by roughly 500 kB gzipped.
    -   The **tiny** build deliberately omits ELK to stay small, and falls back to dagre for diagrams that ask for an ELK layout. Its size is unchanged.
    -   `@mermaid-js/layout-elk` is no longer needed on normal builds — existing `mermaid.registerLayoutLoaders(elkLayouts)` calls keep working and can be removed. It is still published, and remains the way to add ELK to the tiny build.
    -   `dist/mermaid.esm.min.mjs` now contains syntax that `es-module-lexer` (used by Vite) rejects with `content contains invalid JS syntax`. Bundler users are unaffected as long as they import the `mermaid` package specifier, which resolves to the core build; only builds that point Vite directly at that dist file need to switch to the package specifier or the core build.
    -   State diagrams now resolve their layout through the same registration check as every other diagram, so an unavailable layout falls back instead of failing to render.

    An unrecognised `theme` name now resolves to the default theme in name as well as in variables; previously the invalid name stayed in place while the default's variables were loaded, and every palette-aware stylesheet gates on the name. `theme: 'null'`, the documented way to disable the pre-defined themes, is unaffected. Note that `neo` paints node strokes with a gradient when the theme sets `useGradient`, which `base` does; setting a custom `nodeBorder` on `base` turns the gradient off.

-   [#8211](mermaid-js/mermaid#8211) [`a19bd08`](mermaid-js/mermaid@a19bd08) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - **Removed: the `defaultRenderer` option of the `flowchart`, `class` and `state` config sections.**

    Use the top-level `layout` option instead:

    ```yaml
    ---
    config:
      layout: dagre # or elk
    ---
    ```

    ### Why

    `defaultRenderer` named a rendering engine back when there was more than one. There is not.
    All of its accepted values — `dagre`, `dagre-wrapper`, `dagre-d3` — had come to select the
    same unified renderer, and its `elk` value did nothing but set `layout: elk` on your behalf.
    The option's documented purpose no longer existed.

    What it still did was stranger than doing nothing. The detectors branched on whether the
    value was _recognised_, so a **valid** value routed a diagram to the modern renderer and an
    **invalid** one silently routed it to the legacy renderer. Since the default was always a
    valid value, the legacy `class` and `state` renderers were unreachable except by
    misconfiguring the option.

    ### What changes for you

    -   `layout` selects the layout algorithm, as it already did. Nothing else does.
    -   `graph`, `classDiagram` and `stateDiagram` always render with the unified diagram. This
        was already true for every valid configuration.
    -   The legacy `flowchart`, `class` and `state` diagram ids are gone. `flowchart-v2`,
        `classDiagram` and `stateDiagram` are what `detectType` now returns for that syntax.
    -   `flowchart-elk` as an explicit diagram keyword still works.

    Configuration that still sets `defaultRenderer` is ignored rather than rejected, so nothing
    throws — but it no longer has any effect, and diagrams that relied on
    `flowchart: { defaultRenderer: 'elk' }` to get an ELK layout should set `layout: elk`.

-   [#8223](mermaid-js/mermaid#8223) [`6df1149`](mermaid-js/mermaid@6df1149) Thanks [@aloisklink](https://github.com/aloisklink)! - refactor!: remove layout internal exports

    Remove the
    `clearLayoutRenderState`, `createCommonLayoutRenderer`, `defaultMeasureLayout`, `paintLayoutData` functions,
    and the `CommonLayout*` types from Mermaid's public API.

    These functions were only used by the `@mermaid-js/layout-elk` package,
    which now bundles them. Removing them makes it easier to avoid major versions
    of mermaid.

### Minor Changes

-   [#8048](mermaid-js/mermaid#8048) [`7a3c1a8`](mermaid-js/mermaid@7a3c1a8) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: add UML use case diagrams with actors, use cases, system boundaries, relationships, stereotypes, notes, JSON tables, class and style support, accessibility metadata, and business variants.

-   [#8073](mermaid-js/mermaid#8073) [`cd48a64`](mermaid-js/mermaid@cd48a64) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: `defaultMeasureLayout` accepts a `unwrapGroupLabels` option so a layout engine can ask for cluster labels to be measured at their natural width instead of the `flowchart.wrappingWidth` fallback.

    `insertCluster` paints a plain cluster label with an infinite width while the measurement pass wrapped it at 200px, so a layout that sizes compound nodes from the measured label sized them too narrow. Layouts now opt in explicitly; core no longer inspects the layout's name. Markdown cluster labels stay wrapped, since those are painted wrapped.

    The bundled ELK layout opts in, which changes the size of labelled subgraphs in ELK-laid-out diagrams. Diagrams laid out with dagre are unaffected.

-   [#8073](mermaid-js/mermaid#8073) [`0cf3797`](mermaid-js/mermaid@0cf3797) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: let a container pick its own ELK algorithm with `@{ algorithm: … }`, and add `elk.box` and `elk.rectpacking` to the selectable `layout` values.

    A subgraph carrying `@{ algorithm: elk.box }` is laid out with that algorithm in its own coordinate system instead of inheriting the diagram's. Supported values are `elk.layered`, `elk.box`, `elk.rectpacking`, `elk.stress`, `elk.force`, `elk.mrtree`, `elk.radial`, and `elk.sporeOverlap`; anything else is ignored with a warning rather than handed to ELK, where an unknown id would abort the layout. Containers with edges crossing their boundary fall back to the inherited algorithm, since isolated layout and cross-boundary edges are incompatible.

    Also in this release:

    -   ELK cluster labels are measured unwrapped, so a compound node is sized to fit its label instead of to a 200px wrapping width. **This changes the size — and therefore the layout — of existing ELK diagrams that have labelled subgraphs.**
    -   `elk.box` and `elk.rectpacking` place nodes but never route edges. Edges they leave unrouted now fall back to a straight line between the two node centres instead of failing the render.
    -   `keepEntryNodeOnTop` pins a cycle's true entry rather than the first-declared node when a back-edge feeds the entry, so recursive flows read from where they actually start.

-   [#8193](mermaid-js/mermaid#8193) [`813c766`](mermaid-js/mermaid@813c766) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - **`theme`, `look` and `layout` can now be set per diagram type**, in each diagram's config section — `mermaid.initialize({ look: 'classic', flowchart: { look: 'handDrawn' } })`, or the same under `config` in front matter. The schema uses the same mechanism to give a diagram type its own default. Resolution, highest first: front matter or directive, `initialize()`, the diagram type's default, the global default; a diagram-scoped value beats a global one set in the same layer.

    Swimlanes take `layout: swimlane` from that schema default instead of having it forced by their `init` hook, so `swimlane: { layout: ... }` and a `layout` in front matter are now honoured. An unregistered layout also always falls back to `dagre` with a warning rather than throwing — state diagrams skipped that fallback, and mindmaps threw when `cose-bilkent` was absent, as it is in `@mermaid-js/tiny`.

-   [#8147](mermaid-js/mermaid#8147) [`43d9fbc`](mermaid-js/mermaid@43d9fbc) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): class boxes and flowchart subgraph containers now take a per-item colour under the `redux-color` and `redux-dark-color` themes, cycling every 12 as ER entities already do. Collapsed subgraphs keep the slot they would have had expanded; nodes inside a subgraph stay uniform, and explicit `classDef` or `style` still wins over the palette.

-   [#8191](mermaid-js/mermaid#8191) [`2878cf3`](mermaid-js/mermaid@2878cf3) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): composite states now take a per-container colour under the `redux-color` and `redux-dark-color` themes, as flowchart subgraphs already do. Each composite gets the palette's border colour on its outline and its background tint behind the title strip; nested composites each take the next colour, so depth stays readable. The body keeps the theme's own `compositeBackground`. `redux-dark-color` colours the outlines only, matching how it treats ER, requirement and sequence.

    The concurrency regions produced by a `--` divider share the colour of the composite they split, rather than taking one of their own — the author wrote a single composite, so it is drawn as one thing in parts. Adding a `--` therefore leaves every other composite's colour untouched.

    Under the `handDrawn` look, concurrency regions are now filled solid rather than hatched, so that they can carry the palette tint the same way every other look does.

    States inside a composite stay uniform. A composite carrying its own `classDef` or `style` keeps those colours and takes no palette slot, and neither do its concurrency regions; the slot is still spent, so styling one composite does not shift the colours of the ones after it. Note that this opt-out is all-or-nothing: a `classDef` that sets only text properties, such as `font-weight`, still takes that composite out of the palette.

-   [#8176](mermaid-js/mermaid#8176) [`16b9a7d`](mermaid-js/mermaid@16b9a7d) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): swimlane lanes take a per-lane colour under the `redux-color` and `redux-dark-color` themes, cycling every 12 as flowchart subgraphs do. The lane holding ungrouped nodes takes its own slot and now follows the diagram's `look`.

-   [#8178](mermaid-js/mermaid#8178) [`75e6c30`](mermaid-js/mermaid@75e6c30) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat(usecase): use case diagrams now take colour by role — actors, use cases and system boundaries each get their own colour from the new `usecaseActorBkg`/`usecaseActorBorder`, `usecaseBkg`/`usecaseBorder` and `usecaseBoundaryBkg`/`usecaseBoundaryBorder` theme variables, with `usecaseIncludeLine`/`usecaseExtendLine` separating the two dashed relationship kinds by hue. `redux-color` and `redux-dark-color` set them; every other theme is unchanged. Colour is keyed to the kind of element, so editing a diagram never recolours the elements around the edit. Set `usecase.colorScheme: 'rotate'` for the per-element palette cycle instead, and `classDef`/`style` still wins over both.

### Patch Changes

-   [#8211](mermaid-js/mermaid#8211) [`dc2e453`](mermaid-js/mermaid@dc2e453) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(block): restore node borders under looks that use a gradient stroke.

    A block diagram rendered with `look: neo` lost its node borders entirely. The look-specific
    rules set `stroke: url(#<svgId>-gradient)`, and those definitions were appended by
    `rendering-util/render.ts` — which block does not use, since it runs its own render loop.
    An `url(#…)` paint that resolves to nothing is painted as **none**, not as a fallback
    colour, so the border disappeared rather than degrading to a plain stroke.

    The definitions now live in `rendering-util/insertLookDefs.ts` and both render paths call
    it, so the two cannot drift into producing different gradients.

    This only became visible recently: block did not previously pass `look` to its nodes, so
    none of the look-specific rules applied to it and the missing definitions went unnoticed.

-   [#8181](mermaid-js/mermaid#8181) [`ce0302d`](mermaid-js/mermaid@ce0302d) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(block): apply the redux colour palette to composite blocks. Composites now take a per-container colour under `redux-color` and `redux-dark-color`, matching how flowchart subgraphs are coloured — one counter over containers, in declaration order, with the plain shapes left on the flat theme colour. The palette is opt-in: block does not default to `redux-color` the way flowchart and several other diagram types do, so set `theme: redux-color` (or `redux-dark-color`) explicitly to see it. `classDef`/`style` still win.

-   [#8211](mermaid-js/mermaid#8211) [`ad98070`](mermaid-js/mermaid@ad98070) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(block): take the node border width from the theme, as every other diagram does.

    `block/styles.ts` pinned `stroke-width: 1px` on node shapes while the flowchart, class and
    state stylesheets all read `strokeWidth` from the theme. Under a theme that asks for more —
    `neo` asks for 2 — a block rendered a visibly thinner border than an identical flowchart
    node, and nothing downstream corrected it: the `neo` rules in the shared stylesheet set
    `stroke` and `filter`, never `stroke-width`.

    The gap was invisible until block began propagating `look` to its nodes, because until then
    none of the look-specific rules applied to block at all.

    Affects only themes whose `strokeWidth` is not 1. `neo` is 2; `neo-dark`, `base` and the
    rest are unchanged.

-   [#8231](mermaid-js/mermaid#8231) [`e2aab3f`](mermaid-js/mermaid@e2aab3f) Thanks [@knsv-bot](https://github.com/knsv-bot)! - Use balanced Brandes-Koepf placement for the default ELK preset to improve alignment of branches and composite-state entries. Preserve named non-default presets and explicit placement/alignment options, including `NONE`. Some diagrams may become wider or taller; `depthFirst` retains the previous default layout.

-   [#7874](mermaid-js/mermaid#7874) [`6b79518`](mermaid-js/mermaid@6b79518) Thanks [@filipsajdak](https://github.com/filipsajdak)! - fix(c4): allow boundaries as relationship endpoints

      A `Rel` (or `BiRel`/`Rel_*`) that referenced a boundary alias threw `references an unknown
    shape`, because endpoint lookup only searched shapes and not boundaries. Boundary aliases now
      resolve as relationship endpoints. Fixes #4864.

-   [#8100](mermaid-js/mermaid#8100) [`30325d4`](mermaid-js/mermaid@30325d4) Thanks [@filipsajdak](https://github.com/filipsajdak)! - fix(c4): stop wrapping non-text named attributes that land in a text slot

    A named attribute such as `$tags` or `$sprite` can arrive in the positional
    slot of a text field when the argument before it is omitted. It was then
    stored as `{ text: value }` rather than a string, so `$tags` given without a
    description crashed rendering.

-   [#8148](mermaid-js/mermaid#8148) [`c3ee3c7`](mermaid-js/mermaid@c3ee3c7) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): `redux-dark`, `redux-dark-color` and `neo-dark` shipped `secondBkg` as the literal string `calculated`, so railroad rendered the invalid `fill: calculated`; it is now computed as `theme-dark` does. The same three themes had gantt done-task labels at 1.07:1 contrast — a light fill under their light task ink — now 5.7:1 or better. The ER and requirement stylesheets also validate `look` before interpolating it into a CSS selector.

-   [#8222](mermaid-js/mermaid#8222) [`1ee934d`](mermaid-js/mermaid@1ee934d) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: stop ELK-laid-out diagrams logging `Unknown arrow type: arrow_open` once per edge.

    ELK's arrow table put the edge _type_ `arrow_open` — which means "no arrowheads" — into the arrow-type slot, where `none` is the value that marks a deliberate absence. Anything else is reported as an unknown marker name. Diagrams whose db sets `arrowTypeStart` itself, such as flowcharts, never reached that fallback; state diagrams, which leave it unset, warned on every edge.

    Rendering is unchanged: both spellings produced no start marker and the same geometry, since neither has a marker offset. Only the log noise goes away.

-   [#8204](mermaid-js/mermaid#8204) [`a7831c5`](mermaid-js/mermaid@a7831c5) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: keep ELK subgraph frames out to the border anchor of edges that terminate on the group, so transitions to composite states no longer run along the cluster border

-   [#8155](mermaid-js/mermaid#8155) [`0320406`](mermaid-js/mermaid@0320406) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(elk): a composite state or subgraph containing a loop now opens on its own start node.

    Cycle breaking was resolved for the root graph but never passed to containers, so a
    container laid out on its own fell back to ELK's default, `GREEDY`, while the root ran
    whatever `elk.preset` asked for. The two then reversed different edges of the same cycle.

    In a composite state that loops, greedy reverses the edge that turns an ordinary state
    into a source, and sources sit on the first layer — so the diagram opened on that state
    alongside its start circle instead of on the start circle alone. Dagre reverses the other
    edge, which is why the two layout engines disagreed on the same diagram.

    Containers now resolve cycle breaking from the preset exactly as the root does. Only
    containers that actually contain a cycle change; an acyclic subgraph gives a cycle-breaking
    strategy nothing to do. `preset: legacy` still reaches containers with `GREEDY`, so it keeps
    reproducing the earlier rendering inside frames as well as outside them.

-   [#8228](mermaid-js/mermaid#8228) [`e36b883`](mermaid-js/mermaid@e36b883) Thanks [@knsv-bot](https://github.com/knsv-bot)! - Reserve subgraph title padding before ELK routes edges so attachments stay on the painted frame. Clip stale interior endpoints along the incoming segment, avoiding edges that run along the subgraph border.

-   [#8199](mermaid-js/mermaid#8199) [`b993915`](mermaid-js/mermaid@b993915) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: keep ELK edges vertical at small nodes — the node now moves onto the routed line instead of the edge bending toward the node's off-centre position

-   [#8155](mermaid-js/mermaid#8155) [`4e00c5c`](mermaid-js/mermaid@4e00c5c) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: render ER relationship edges above subgraph backgrounds under the ELK layout instead of hiding them behind the cluster rect

-   [#8156](mermaid-js/mermaid#8156) [`3f05015`](mermaid-js/mermaid@3f05015) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(er, requirement, timeline): stop the ER, requirement and timeline stylesheets emitting CSS the browser discards for the colour themes.

    All three generate one rule per palette slot. ER and requirement looped to `THEME_COLOR_LIMIT` and indexed the palette by the loop counter, which goes wrong in both directions: a palette shorter than the limit emitted `stroke: undefined` for the overflow slots, and a palette _longer_ than it left entities stamped with a slot that had no rule at all, rendering unstyled beside coloured neighbours. Both now take the loop bound from the palette itself, which is the same length the boxes stamp with — so the rules emitted and the slots stamped cannot disagree. Both also bail before the loop for an empty palette, since wrapping alone is not enough there: `i % 0` is `NaN` and `[][NaN]` is `undefined`.

    Timeline keeps looping to `THEME_COLOR_LIMIT` and wrapping, because it numbers `.section-N` classes rather than palette slots — nothing stamps those, so the palette cycles across however many sections exist.

    `requirement` also emitted `fill: ;` — a property with no value, which is invalid — whenever there was no background palette. That is the live case for `redux-dark-color`, which ships a border palette and no background palette so that it colours outlines only. The declaration is now omitted instead.

    None of these raises an error: the browser discards the invalid declaration, so the only symptom is a shape rendering unstyled.

-   [#8149](mermaid-js/mermaid#8149) [`d57ed55`](mermaid-js/mermaid@d57ed55) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(docs): stop `docs:build` deleting the committed `docs/` directory when a later step fails.

-   [#7906](mermaid-js/mermaid#7906) [`1fc5bb3`](mermaid-js/mermaid@1fc5bb3) Thanks [@knsv-bot](https://github.com/knsv-bot)! - perf: avoid quadratic parse cost on deeply-indented diagrams (~1.4 s → ~30 ms on a pathological fixture)

-   [#8211](mermaid-js/mermaid#8211) [`b979eb4`](mermaid-js/mermaid@b979eb4) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(handdrawn): tighten the hachure fill so shapes read as filled rather than striped.

    The hand-drawn fill drew 4px-wide strokes 5.2px apart, which left visible diagonal
    banding across every shape — legible as a texture up close, but at normal diagram size it
    read as stripes rather than a fill. Strokes are now 1.5px at 1.5px spacing, so the fill
    reads as an even tint while keeping the drawn edge quality.

    The striped fill behind state-diagram start, end and fork markers moves the same way, so
    those come out close to solid — which is what the UML convention asks for anyway.

    Swimlane lane titles previously pinned their own stroke weight and so ignored the shared
    value; they now follow it like every other shape.

-   [#8152](mermaid-js/mermaid#8152) [`7ee4c3f`](mermaid-js/mermaid@7ee4c3f) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: edges attach to non-rectangular shapes on the outline instead of half a pixel off it.

    **This is not ELK-specific.** `intersectPolygon` is how every non-rectangular shape finds its edge attachment — diamond, stadium, hexagon, trapezoid, subroutine — in every layout, so a dagre-rendered flowchart with a decision diamond is affected exactly as much as an ELK one.

    `intersectLine` comes from Graphics Gems, where the coordinates were integers and the numerator was nudged half a denominator away from zero so the integer division rounded instead of truncating. JavaScript division does neither, so the nudge stopped being a correction and became the whole error: every result came back displaced by `0.5 * sign(num) * sign(denom)`. The magnitude is always exactly half a unit, but the sign is per axis, because the numerator is computed separately for x and y while the denominator is shared — so the two axes could move the same way or opposite ways depending on the geometry, which is why it never looked like a constant offset anyone could spot by eye.

    That was enough to give an otherwise orthogonal edge a tiny diagonal opening segment, and to put an attachment just inside the node it was meant to touch.

    `question.ts` had been subtracting a flat 0.5 from both axes to compensate for diamonds. That only cancelled the bias when both signs came out positive, and doubled the error to a full unit when they did not — so the compensation goes along with the cause. **Rendered output moves by up to a pixel wherever a polygon shape terminates an edge.**

-   [#8048](mermaid-js/mermaid#8048) [`a6ec7ff`](mermaid-js/mermaid@a6ec7ff) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: treat `</br>` as a line break in labels

    `lineBreakRegex` only matched `<br>`, `<br/>` and `<br />`, so the malformed-but-common
    `</br>` survived as literal text wherever labels render as plain SVG text. HTML parsers
    already treat `</br>` as a `<br>`, so the tag appeared to work in HTML-label mode and
    failed everywhere else. It is now accepted on both paths.

    Also fixes `hasBreaks()` returning alternating results for the same input: it called
    `.test()` on a global regex, which advances `lastIndex` between calls. `wrapLabel()` used
    the same stateful check and could therefore re-wrap labels that already contained breaks.

-   [#8152](mermaid-js/mermaid#8152) [`1246a55`](mermaid-js/mermaid@1246a55) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: don't draw a line hop that has no room next to a bend.

    A crossing close to a corner used to get a hop squeezed into whatever space was left — as little as 2.9px against a requested 6px, opening exactly on the corner's tangent point. At that size the arc no longer clears the line it is meant to hop, so the two strokes still touch and the corner's curve runs straight into the arc's. It reads as a rendering fault rather than as a crossing.

    Hops now keep a straight run clear of the bend, and one that would still have to shrink below 60% of the requested radius is dropped instead of drawn. An undrawn hop is an ordinary crossing, which is a much better failure than a broken-looking one.

    **This changes swimlane diagrams as well as ELK ones.** Swimlanes are the existing consumer of line hops and have them on today, so a swimlane with a crossing near a bend will render differently even for someone not using ELK at all. It shows up wherever a layout stacks edges in narrow lanes: ELK routes subgraph-internal edges 10px apart, and 10px does not hold a 7.07px corner cut plus a 6px hop.

    A crossing is also ignored now when it lands inside the stretch where either edge is rounding a bend. Crossings are found on polylines, but a rounded edge is not drawn as its polyline — it leaves the line up to 7.07px before each bend and rejoins it that far after. A crossing found inside that stretch is somewhere the stroke never goes, so the hop was arching over blank paper while the two lines carried on touching beside it.

-   [#8146](mermaid-js/mermaid#8146) [`def4c81`](mermaid-js/mermaid@def4c81) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): `redux-color` and `redux-dark-color` now define every variable their base themes define, instead of silently falling back to untuned values for `primaryBorderColor`, `clusterBkg`, `clusterBorder`, `altBackground`, `compositeTitleBackground` and the state and requirement edge-label backgrounds. Pie, gantt and user-journey also read the theme palette rather than a single pale tint, so pie slices are distinguishable and gantt sections are visibly banded.

-   [#8189](mermaid-js/mermaid#8189) [`3802472`](mermaid-js/mermaid@3802472) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): venn circles follow the `redux-color` and `redux-dark-color` palettes. Neither theme defined any `venn*` variable, so every circle rendered in one flat colour.

-   [#8185](mermaid-js/mermaid#8185) [`f5af413`](mermaid-js/mermaid@f5af413) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: sequence diagram actors under the `neo` look and `redux` themes. Notes no longer render bold,
    a `rect` section band is no longer drawn white on white, and participants follow one vertical
    model: every lifeline starts and ends on a shared line, all participant glyphs are one size with
    their feet on a common edge, single-line labels share one baseline in the header and in the
    mirrored footer, and a multiline label grows away from the lifeline instead of moving it. The
    `classic` look is unchanged.

-   [#8157](mermaid-js/mermaid#8157) [`a3a92ba`](mermaid-js/mermaid@a3a92ba) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(sequence): index each actor colour palette by its own length.

    Every actor-drawing call site read `bkgColorArray[actorCount % borderColorArray.length]` — one palette indexed by the other's length. Under `redux-color` both palettes have twelve entries, so the wrong length happens to give the right answer and nothing is visibly broken today. It goes wrong as soon as the two differ in length: the overflow actors resolve to `undefined`, and d3 then strips the inline fill for some actors and not others.

    Both palettes now cycle within their own length, via a shared helper. An absent or empty palette still yields `undefined` rather than a substitute colour, which is what `redux-dark-color` relies on: it ships a border palette and an empty background palette so actors are outlined but not filled.

-   [#8232](mermaid-js/mermaid#8232) [`e691042`](mermaid-js/mermaid@e691042) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Circle, double circle, Delay and Display nodes size themselves from the label's height as well as its width, so a wrapped label stays inside the outline. Each was sized from the width alone and clipped any label taller than it was wide, which `flowchart.wrappingWidth: 120` made common.

    This replaces the wrapping exemption those shapes carried along with stadium and diamond: it stopped them wrapping at all, so a long label produced a single-line node hundreds of pixels wide. Stadium needs no exemption — its `w >= 1.5h` floor already keeps a wrapped label from closing the caps into a circle — and diamond never needed one.

    Stacked (`docs`) and lined (`lin-doc`) document nodes lift their label clear of the wavy bottom edge by the wave's full depth rather than half of it, so the last line is no longer clipped by the trough. Plain `doc` already did this; the two variants had copied it with the offset halved. Node dimensions are unchanged.

-   [#8154](mermaid-js/mermaid#8154) [`ba0deed`](mermaid-js/mermaid@ba0deed) Thanks [@aloisklink](https://github.com/aloisklink)! - fix: bundle `fastdom` to fix issues with global `define`

-   [#8227](mermaid-js/mermaid#8227) [`b05824a`](mermaid-js/mermaid@b05824a) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(flowchart): stop stadium labels from wrapping into a circle and exempt selected shapes from automatic label wrapping.

    Long stadium labels wrapped to `flowchart.wrappingWidth`, grew taller without growing wider, and turned the stadium into a circle. Stadium now keeps its semicircular caps with an outer width of at least 1.5 times its height and enough clearance that the label stays inside the arcs.

    Stadium (`terminal`, `pill`), circle (`circ`), diamond (`diam`, `decision`, `question`), double circle (`dbl-circ`, `double-circle`), Display (`curv-trap`, `curved-trapezoid`, `display`) and Delay (`delay`, `half-rounded-rectangle`) no longer wrap their labels at `flowchart.wrappingWidth`. Explicit line breaks are kept, and an explicit node width still takes precedence.

-   [#8192](mermaid-js/mermaid#8192) [`0ec29e9`](mermaid-js/mermaid@0ec29e9) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(state): draw the edge between a state and its note dashed under the `neo` look. It was rendering solid.

    The dashes were expressed only as CSS — `.note-edge { stroke-dasharray: 5 }` — which is enough under `classic` but not under `neo`, where `insertEdge` writes an inline `stroke-dasharray` on every edge, computed from the path length so the arrow markers keep their gaps. An inline style outranks a stylesheet rule, so the note edge took the solid pattern and the `.note-edge` rule simply lost. The note edge now declares `pattern: 'dashed'`, which is what `insertEdge` reads to choose its dash generator. `classic` is unchanged.

-   [#8225](mermaid-js/mermaid#8225) [`0d77926`](mermaid-js/mermaid@0d77926) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(swimlane): seed cycle removal from source nodes so feedback edges are reversed instead of forward flow edges. A cycle like `task --> decision --> fix --> task` no longer places `fix` in the middle of the `start --> task` flow line.

-   [#8211](mermaid-js/mermaid#8211) [`93edd72`](mermaid-js/mermaid@93edd72) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: keep association markers apart on use-case ovals under ELK. Ovals get the same vertical padding as horizontal, edge attachment points on them are spread across the side, and the ELK renderer now accounts for every end-marker offset (extension, circle, cross, …) so a short entry stub can no longer flip a marker around.

-   Updated dependencies \[[`33442fd`](mermaid-js/mermaid@33442fd)]:
    -   @mermaid-js/parser@2.0.0

### Keeping the previous defaults in your integration

Mermaid 12 changes what unconfigured diagrams look like: ELK replaces dagre as the default layout, and `redux-color` / `neo` replace `default` / `classic` as theme and look for flowchart, class, state, ER, requirement, sequence, use case, swimlane, Venn and agentflow. If you embed Mermaid and want your users' existing diagrams to render as before, set all three explicitly at initialisation:

```js
mermaid.initialize({ layout: 'dagre', theme: 'default', look: 'classic' });
```

Diagram authors can do the same per diagram in front matter, and a value there always wins over `initialize()`.

- [Layouts: how `layout` is resolved, ELK variants, and the tiny build](https://mermaid.ai/open-source/config/layouts.html)
- [Flowchart: default theme, look and layout in v12](https://mermaid.ai/open-source/syntax/flowchart.html#default-theme-look-and-layout-v-mermaid-release-version)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: give me a 98K <240642031+inhuman-0@users.noreply.github.com>
ark-65 added a commit to zhongmiao-org/mermaid-markdown-bridge that referenced this pull request Sep 11, 2026
* chore: sync Mermaid runtime to 12.0.0

Upstream release: https://github.com/mermaid-js/mermaid/releases/tag/mermaid%4012.0.0

Release notes:
**Mermaid 12.0.0** ships ELK as the bundled default layout engine, a new `redux-color` / `neo` default appearance, and two new diagram types: [UML use case diagrams](https://mermaid.ai/open-source/syntax/usecase.html).

This is a breaking release (ES2024, Safari 17.4+, Node 22.12+). Existing flowcharts, state and class diagrams will re-lay out and recolour; add `layout: dagre`, `theme: default` and `look: classic` to your config to keep the old look. Full changelog below.

### Major Changes

-   [#8213](mermaid-js/mermaid#8213) [`33442fd`](mermaid-js/mermaid@33442fd) Thanks [@aloisklink](https://github.com/aloisklink)! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+

    Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support
    older browsers, you may need to polyfill or transpile mermaid.

    Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome,
    older iOS devices don't get major Safari updates.

    Node.JS v22.12+ is also declared as requirement in our `package.json` files,
    but as mermaid requires a browser, this is mainly so we can use dependencies that
    also declare a Node.JS v22.12+ requirement, without causing issues for users when
    running `npm install`.

-   [#8155](mermaid-js/mermaid#8155) [`810893c`](mermaid-js/mermaid@810893c) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat!: **ELK is now bundled with mermaid and is the default layout algorithm.**

    ELK previously shipped as a separate `@mermaid-js/layout-elk` package that sites had to install and register. It is now part of mermaid itself and registered automatically, so `layout: elk` — and the `elk.stress`, `elk.force`, `elk.mrtree`, `elk.sporeOverlap`, `elk.box` and `elk.rectpacking` variants — work with no setup.

    **This changes how existing diagrams look.** Flowchart, state, class, entity-relationship, requirement, use-case diagrams that do not specify a `layout` are now laid out by ELK instead of dagre. To keep the previous layout, set dagre explicitly:

    ```yaml
    ---
    config:
      layout: dagre
    ---
    ```

    or globally, `mermaid.initialize({ layout: 'dagre' })`.

    Mindmap is unchanged: it keeps laying out with cose-bilkent unless a layout is explicitly requested (in the tiny build, which ships neither ELK nor cose-bilkent, it falls back to dagre).

    Other notes:

    -   ELK is loaded as a separate chunk in the ESM builds, so it is only fetched when a diagram actually uses it. The single-file IIFE build (`mermaid.min.js`) inlines it and grows by roughly 500 kB gzipped.
    -   The **tiny** build deliberately omits ELK to stay small, and falls back to dagre for diagrams that ask for an ELK layout. Its size is unchanged.
    -   `@mermaid-js/layout-elk` is no longer needed on normal builds — existing `mermaid.registerLayoutLoaders(elkLayouts)` calls keep working and can be removed. It is still published, and remains the way to add ELK to the tiny build.
    -   `dist/mermaid.esm.min.mjs` now contains syntax that `es-module-lexer` (used by Vite) rejects with `content contains invalid JS syntax`. Bundler users are unaffected as long as they import the `mermaid` package specifier, which resolves to the core build; only builds that point Vite directly at that dist file need to switch to the package specifier or the core build.
    -   State diagrams now resolve their layout through the same registration check as every other diagram, so an unavailable layout falls back instead of failing to render.

    An unrecognised `theme` name now resolves to the default theme in name as well as in variables; previously the invalid name stayed in place while the default's variables were loaded, and every palette-aware stylesheet gates on the name. `theme: 'null'`, the documented way to disable the pre-defined themes, is unaffected. Note that `neo` paints node strokes with a gradient when the theme sets `useGradient`, which `base` does; setting a custom `nodeBorder` on `base` turns the gradient off.

-   [#8211](mermaid-js/mermaid#8211) [`a19bd08`](mermaid-js/mermaid@a19bd08) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - **Removed: the `defaultRenderer` option of the `flowchart`, `class` and `state` config sections.**

    Use the top-level `layout` option instead:

    ```yaml
    ---
    config:
      layout: dagre # or elk
    ---
    ```

    ### Why

    `defaultRenderer` named a rendering engine back when there was more than one. There is not.
    All of its accepted values — `dagre`, `dagre-wrapper`, `dagre-d3` — had come to select the
    same unified renderer, and its `elk` value did nothing but set `layout: elk` on your behalf.
    The option's documented purpose no longer existed.

    What it still did was stranger than doing nothing. The detectors branched on whether the
    value was _recognised_, so a **valid** value routed a diagram to the modern renderer and an
    **invalid** one silently routed it to the legacy renderer. Since the default was always a
    valid value, the legacy `class` and `state` renderers were unreachable except by
    misconfiguring the option.

    ### What changes for you

    -   `layout` selects the layout algorithm, as it already did. Nothing else does.
    -   `graph`, `classDiagram` and `stateDiagram` always render with the unified diagram. This
        was already true for every valid configuration.
    -   The legacy `flowchart`, `class` and `state` diagram ids are gone. `flowchart-v2`,
        `classDiagram` and `stateDiagram` are what `detectType` now returns for that syntax.
    -   `flowchart-elk` as an explicit diagram keyword still works.

    Configuration that still sets `defaultRenderer` is ignored rather than rejected, so nothing
    throws — but it no longer has any effect, and diagrams that relied on
    `flowchart: { defaultRenderer: 'elk' }` to get an ELK layout should set `layout: elk`.

-   [#8223](mermaid-js/mermaid#8223) [`6df1149`](mermaid-js/mermaid@6df1149) Thanks [@aloisklink](https://github.com/aloisklink)! - refactor!: remove layout internal exports

    Remove the
    `clearLayoutRenderState`, `createCommonLayoutRenderer`, `defaultMeasureLayout`, `paintLayoutData` functions,
    and the `CommonLayout*` types from Mermaid's public API.

    These functions were only used by the `@mermaid-js/layout-elk` package,
    which now bundles them. Removing them makes it easier to avoid major versions
    of mermaid.

### Minor Changes

-   [#8048](mermaid-js/mermaid#8048) [`7a3c1a8`](mermaid-js/mermaid@7a3c1a8) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: add UML use case diagrams with actors, use cases, system boundaries, relationships, stereotypes, notes, JSON tables, class and style support, accessibility metadata, and business variants.

-   [#8073](mermaid-js/mermaid#8073) [`cd48a64`](mermaid-js/mermaid@cd48a64) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: `defaultMeasureLayout` accepts a `unwrapGroupLabels` option so a layout engine can ask for cluster labels to be measured at their natural width instead of the `flowchart.wrappingWidth` fallback.

    `insertCluster` paints a plain cluster label with an infinite width while the measurement pass wrapped it at 200px, so a layout that sizes compound nodes from the measured label sized them too narrow. Layouts now opt in explicitly; core no longer inspects the layout's name. Markdown cluster labels stay wrapped, since those are painted wrapped.

    The bundled ELK layout opts in, which changes the size of labelled subgraphs in ELK-laid-out diagrams. Diagrams laid out with dagre are unaffected.

-   [#8073](mermaid-js/mermaid#8073) [`0cf3797`](mermaid-js/mermaid@0cf3797) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat: let a container pick its own ELK algorithm with `@{ algorithm: … }`, and add `elk.box` and `elk.rectpacking` to the selectable `layout` values.

    A subgraph carrying `@{ algorithm: elk.box }` is laid out with that algorithm in its own coordinate system instead of inheriting the diagram's. Supported values are `elk.layered`, `elk.box`, `elk.rectpacking`, `elk.stress`, `elk.force`, `elk.mrtree`, `elk.radial`, and `elk.sporeOverlap`; anything else is ignored with a warning rather than handed to ELK, where an unknown id would abort the layout. Containers with edges crossing their boundary fall back to the inherited algorithm, since isolated layout and cross-boundary edges are incompatible.

    Also in this release:

    -   ELK cluster labels are measured unwrapped, so a compound node is sized to fit its label instead of to a 200px wrapping width. **This changes the size — and therefore the layout — of existing ELK diagrams that have labelled subgraphs.**
    -   `elk.box` and `elk.rectpacking` place nodes but never route edges. Edges they leave unrouted now fall back to a straight line between the two node centres instead of failing the render.
    -   `keepEntryNodeOnTop` pins a cycle's true entry rather than the first-declared node when a back-edge feeds the entry, so recursive flows read from where they actually start.

-   [#8193](mermaid-js/mermaid#8193) [`813c766`](mermaid-js/mermaid@813c766) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - **`theme`, `look` and `layout` can now be set per diagram type**, in each diagram's config section — `mermaid.initialize({ look: 'classic', flowchart: { look: 'handDrawn' } })`, or the same under `config` in front matter. The schema uses the same mechanism to give a diagram type its own default. Resolution, highest first: front matter or directive, `initialize()`, the diagram type's default, the global default; a diagram-scoped value beats a global one set in the same layer.

    Swimlanes take `layout: swimlane` from that schema default instead of having it forced by their `init` hook, so `swimlane: { layout: ... }` and a `layout` in front matter are now honoured. An unregistered layout also always falls back to `dagre` with a warning rather than throwing — state diagrams skipped that fallback, and mindmaps threw when `cose-bilkent` was absent, as it is in `@mermaid-js/tiny`.

-   [#8147](mermaid-js/mermaid#8147) [`43d9fbc`](mermaid-js/mermaid@43d9fbc) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): class boxes and flowchart subgraph containers now take a per-item colour under the `redux-color` and `redux-dark-color` themes, cycling every 12 as ER entities already do. Collapsed subgraphs keep the slot they would have had expanded; nodes inside a subgraph stay uniform, and explicit `classDef` or `style` still wins over the palette.

-   [#8191](mermaid-js/mermaid#8191) [`2878cf3`](mermaid-js/mermaid@2878cf3) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): composite states now take a per-container colour under the `redux-color` and `redux-dark-color` themes, as flowchart subgraphs already do. Each composite gets the palette's border colour on its outline and its background tint behind the title strip; nested composites each take the next colour, so depth stays readable. The body keeps the theme's own `compositeBackground`. `redux-dark-color` colours the outlines only, matching how it treats ER, requirement and sequence.

    The concurrency regions produced by a `--` divider share the colour of the composite they split, rather than taking one of their own — the author wrote a single composite, so it is drawn as one thing in parts. Adding a `--` therefore leaves every other composite's colour untouched.

    Under the `handDrawn` look, concurrency regions are now filled solid rather than hatched, so that they can carry the palette tint the same way every other look does.

    States inside a composite stay uniform. A composite carrying its own `classDef` or `style` keeps those colours and takes no palette slot, and neither do its concurrency regions; the slot is still spent, so styling one composite does not shift the colours of the ones after it. Note that this opt-out is all-or-nothing: a `classDef` that sets only text properties, such as `font-weight`, still takes that composite out of the palette.

-   [#8176](mermaid-js/mermaid#8176) [`16b9a7d`](mermaid-js/mermaid@16b9a7d) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - feat(themes): swimlane lanes take a per-lane colour under the `redux-color` and `redux-dark-color` themes, cycling every 12 as flowchart subgraphs do. The lane holding ungrouped nodes takes its own slot and now follows the diagram's `look`.

-   [#8178](mermaid-js/mermaid#8178) [`75e6c30`](mermaid-js/mermaid@75e6c30) Thanks [@knsv-bot](https://github.com/knsv-bot)! - feat(usecase): use case diagrams now take colour by role — actors, use cases and system boundaries each get their own colour from the new `usecaseActorBkg`/`usecaseActorBorder`, `usecaseBkg`/`usecaseBorder` and `usecaseBoundaryBkg`/`usecaseBoundaryBorder` theme variables, with `usecaseIncludeLine`/`usecaseExtendLine` separating the two dashed relationship kinds by hue. `redux-color` and `redux-dark-color` set them; every other theme is unchanged. Colour is keyed to the kind of element, so editing a diagram never recolours the elements around the edit. Set `usecase.colorScheme: 'rotate'` for the per-element palette cycle instead, and `classDef`/`style` still wins over both.

### Patch Changes

-   [#8211](mermaid-js/mermaid#8211) [`dc2e453`](mermaid-js/mermaid@dc2e453) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(block): restore node borders under looks that use a gradient stroke.

    A block diagram rendered with `look: neo` lost its node borders entirely. The look-specific
    rules set `stroke: url(#<svgId>-gradient)`, and those definitions were appended by
    `rendering-util/render.ts` — which block does not use, since it runs its own render loop.
    An `url(#…)` paint that resolves to nothing is painted as **none**, not as a fallback
    colour, so the border disappeared rather than degrading to a plain stroke.

    The definitions now live in `rendering-util/insertLookDefs.ts` and both render paths call
    it, so the two cannot drift into producing different gradients.

    This only became visible recently: block did not previously pass `look` to its nodes, so
    none of the look-specific rules applied to it and the missing definitions went unnoticed.

-   [#8181](mermaid-js/mermaid#8181) [`ce0302d`](mermaid-js/mermaid@ce0302d) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(block): apply the redux colour palette to composite blocks. Composites now take a per-container colour under `redux-color` and `redux-dark-color`, matching how flowchart subgraphs are coloured — one counter over containers, in declaration order, with the plain shapes left on the flat theme colour. The palette is opt-in: block does not default to `redux-color` the way flowchart and several other diagram types do, so set `theme: redux-color` (or `redux-dark-color`) explicitly to see it. `classDef`/`style` still win.

-   [#8211](mermaid-js/mermaid#8211) [`ad98070`](mermaid-js/mermaid@ad98070) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(block): take the node border width from the theme, as every other diagram does.

    `block/styles.ts` pinned `stroke-width: 1px` on node shapes while the flowchart, class and
    state stylesheets all read `strokeWidth` from the theme. Under a theme that asks for more —
    `neo` asks for 2 — a block rendered a visibly thinner border than an identical flowchart
    node, and nothing downstream corrected it: the `neo` rules in the shared stylesheet set
    `stroke` and `filter`, never `stroke-width`.

    The gap was invisible until block began propagating `look` to its nodes, because until then
    none of the look-specific rules applied to block at all.

    Affects only themes whose `strokeWidth` is not 1. `neo` is 2; `neo-dark`, `base` and the
    rest are unchanged.

-   [#8231](mermaid-js/mermaid#8231) [`e2aab3f`](mermaid-js/mermaid@e2aab3f) Thanks [@knsv-bot](https://github.com/knsv-bot)! - Use balanced Brandes-Koepf placement for the default ELK preset to improve alignment of branches and composite-state entries. Preserve named non-default presets and explicit placement/alignment options, including `NONE`. Some diagrams may become wider or taller; `depthFirst` retains the previous default layout.

-   [#7874](mermaid-js/mermaid#7874) [`6b79518`](mermaid-js/mermaid@6b79518) Thanks [@filipsajdak](https://github.com/filipsajdak)! - fix(c4): allow boundaries as relationship endpoints

      A `Rel` (or `BiRel`/`Rel_*`) that referenced a boundary alias threw `references an unknown
    shape`, because endpoint lookup only searched shapes and not boundaries. Boundary aliases now
      resolve as relationship endpoints. Fixes #4864.

-   [#8100](mermaid-js/mermaid#8100) [`30325d4`](mermaid-js/mermaid@30325d4) Thanks [@filipsajdak](https://github.com/filipsajdak)! - fix(c4): stop wrapping non-text named attributes that land in a text slot

    A named attribute such as `$tags` or `$sprite` can arrive in the positional
    slot of a text field when the argument before it is omitted. It was then
    stored as `{ text: value }` rather than a string, so `$tags` given without a
    description crashed rendering.

-   [#8148](mermaid-js/mermaid#8148) [`c3ee3c7`](mermaid-js/mermaid@c3ee3c7) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): `redux-dark`, `redux-dark-color` and `neo-dark` shipped `secondBkg` as the literal string `calculated`, so railroad rendered the invalid `fill: calculated`; it is now computed as `theme-dark` does. The same three themes had gantt done-task labels at 1.07:1 contrast — a light fill under their light task ink — now 5.7:1 or better. The ER and requirement stylesheets also validate `look` before interpolating it into a CSS selector.

-   [#8222](mermaid-js/mermaid#8222) [`1ee934d`](mermaid-js/mermaid@1ee934d) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: stop ELK-laid-out diagrams logging `Unknown arrow type: arrow_open` once per edge.

    ELK's arrow table put the edge _type_ `arrow_open` — which means "no arrowheads" — into the arrow-type slot, where `none` is the value that marks a deliberate absence. Anything else is reported as an unknown marker name. Diagrams whose db sets `arrowTypeStart` itself, such as flowcharts, never reached that fallback; state diagrams, which leave it unset, warned on every edge.

    Rendering is unchanged: both spellings produced no start marker and the same geometry, since neither has a marker offset. Only the log noise goes away.

-   [#8204](mermaid-js/mermaid#8204) [`a7831c5`](mermaid-js/mermaid@a7831c5) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: keep ELK subgraph frames out to the border anchor of edges that terminate on the group, so transitions to composite states no longer run along the cluster border

-   [#8155](mermaid-js/mermaid#8155) [`0320406`](mermaid-js/mermaid@0320406) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(elk): a composite state or subgraph containing a loop now opens on its own start node.

    Cycle breaking was resolved for the root graph but never passed to containers, so a
    container laid out on its own fell back to ELK's default, `GREEDY`, while the root ran
    whatever `elk.preset` asked for. The two then reversed different edges of the same cycle.

    In a composite state that loops, greedy reverses the edge that turns an ordinary state
    into a source, and sources sit on the first layer — so the diagram opened on that state
    alongside its start circle instead of on the start circle alone. Dagre reverses the other
    edge, which is why the two layout engines disagreed on the same diagram.

    Containers now resolve cycle breaking from the preset exactly as the root does. Only
    containers that actually contain a cycle change; an acyclic subgraph gives a cycle-breaking
    strategy nothing to do. `preset: legacy` still reaches containers with `GREEDY`, so it keeps
    reproducing the earlier rendering inside frames as well as outside them.

-   [#8228](mermaid-js/mermaid#8228) [`e36b883`](mermaid-js/mermaid@e36b883) Thanks [@knsv-bot](https://github.com/knsv-bot)! - Reserve subgraph title padding before ELK routes edges so attachments stay on the painted frame. Clip stale interior endpoints along the incoming segment, avoiding edges that run along the subgraph border.

-   [#8199](mermaid-js/mermaid#8199) [`b993915`](mermaid-js/mermaid@b993915) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: keep ELK edges vertical at small nodes — the node now moves onto the routed line instead of the edge bending toward the node's off-centre position

-   [#8155](mermaid-js/mermaid#8155) [`4e00c5c`](mermaid-js/mermaid@4e00c5c) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: render ER relationship edges above subgraph backgrounds under the ELK layout instead of hiding them behind the cluster rect

-   [#8156](mermaid-js/mermaid#8156) [`3f05015`](mermaid-js/mermaid@3f05015) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(er, requirement, timeline): stop the ER, requirement and timeline stylesheets emitting CSS the browser discards for the colour themes.

    All three generate one rule per palette slot. ER and requirement looped to `THEME_COLOR_LIMIT` and indexed the palette by the loop counter, which goes wrong in both directions: a palette shorter than the limit emitted `stroke: undefined` for the overflow slots, and a palette _longer_ than it left entities stamped with a slot that had no rule at all, rendering unstyled beside coloured neighbours. Both now take the loop bound from the palette itself, which is the same length the boxes stamp with — so the rules emitted and the slots stamped cannot disagree. Both also bail before the loop for an empty palette, since wrapping alone is not enough there: `i % 0` is `NaN` and `[][NaN]` is `undefined`.

    Timeline keeps looping to `THEME_COLOR_LIMIT` and wrapping, because it numbers `.section-N` classes rather than palette slots — nothing stamps those, so the palette cycles across however many sections exist.

    `requirement` also emitted `fill: ;` — a property with no value, which is invalid — whenever there was no background palette. That is the live case for `redux-dark-color`, which ships a border palette and no background palette so that it colours outlines only. The declaration is now omitted instead.

    None of these raises an error: the browser discards the invalid declaration, so the only symptom is a shape rendering unstyled.

-   [#8149](mermaid-js/mermaid#8149) [`d57ed55`](mermaid-js/mermaid@d57ed55) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(docs): stop `docs:build` deleting the committed `docs/` directory when a later step fails.

-   [#7906](mermaid-js/mermaid#7906) [`1fc5bb3`](mermaid-js/mermaid@1fc5bb3) Thanks [@knsv-bot](https://github.com/knsv-bot)! - perf: avoid quadratic parse cost on deeply-indented diagrams (~1.4 s → ~30 ms on a pathological fixture)

-   [#8211](mermaid-js/mermaid#8211) [`b979eb4`](mermaid-js/mermaid@b979eb4) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(handdrawn): tighten the hachure fill so shapes read as filled rather than striped.

    The hand-drawn fill drew 4px-wide strokes 5.2px apart, which left visible diagonal
    banding across every shape — legible as a texture up close, but at normal diagram size it
    read as stripes rather than a fill. Strokes are now 1.5px at 1.5px spacing, so the fill
    reads as an even tint while keeping the drawn edge quality.

    The striped fill behind state-diagram start, end and fork markers moves the same way, so
    those come out close to solid — which is what the UML convention asks for anyway.

    Swimlane lane titles previously pinned their own stroke weight and so ignored the shared
    value; they now follow it like every other shape.

-   [#8152](mermaid-js/mermaid#8152) [`7ee4c3f`](mermaid-js/mermaid@7ee4c3f) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: edges attach to non-rectangular shapes on the outline instead of half a pixel off it.

    **This is not ELK-specific.** `intersectPolygon` is how every non-rectangular shape finds its edge attachment — diamond, stadium, hexagon, trapezoid, subroutine — in every layout, so a dagre-rendered flowchart with a decision diamond is affected exactly as much as an ELK one.

    `intersectLine` comes from Graphics Gems, where the coordinates were integers and the numerator was nudged half a denominator away from zero so the integer division rounded instead of truncating. JavaScript division does neither, so the nudge stopped being a correction and became the whole error: every result came back displaced by `0.5 * sign(num) * sign(denom)`. The magnitude is always exactly half a unit, but the sign is per axis, because the numerator is computed separately for x and y while the denominator is shared — so the two axes could move the same way or opposite ways depending on the geometry, which is why it never looked like a constant offset anyone could spot by eye.

    That was enough to give an otherwise orthogonal edge a tiny diagonal opening segment, and to put an attachment just inside the node it was meant to touch.

    `question.ts` had been subtracting a flat 0.5 from both axes to compensate for diamonds. That only cancelled the bias when both signs came out positive, and doubled the error to a full unit when they did not — so the compensation goes along with the cause. **Rendered output moves by up to a pixel wherever a polygon shape terminates an edge.**

-   [#8048](mermaid-js/mermaid#8048) [`a6ec7ff`](mermaid-js/mermaid@a6ec7ff) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: treat `</br>` as a line break in labels

    `lineBreakRegex` only matched `<br>`, `<br/>` and `<br />`, so the malformed-but-common
    `</br>` survived as literal text wherever labels render as plain SVG text. HTML parsers
    already treat `</br>` as a `<br>`, so the tag appeared to work in HTML-label mode and
    failed everywhere else. It is now accepted on both paths.

    Also fixes `hasBreaks()` returning alternating results for the same input: it called
    `.test()` on a global regex, which advances `lastIndex` between calls. `wrapLabel()` used
    the same stateful check and could therefore re-wrap labels that already contained breaks.

-   [#8152](mermaid-js/mermaid#8152) [`1246a55`](mermaid-js/mermaid@1246a55) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: don't draw a line hop that has no room next to a bend.

    A crossing close to a corner used to get a hop squeezed into whatever space was left — as little as 2.9px against a requested 6px, opening exactly on the corner's tangent point. At that size the arc no longer clears the line it is meant to hop, so the two strokes still touch and the corner's curve runs straight into the arc's. It reads as a rendering fault rather than as a crossing.

    Hops now keep a straight run clear of the bend, and one that would still have to shrink below 60% of the requested radius is dropped instead of drawn. An undrawn hop is an ordinary crossing, which is a much better failure than a broken-looking one.

    **This changes swimlane diagrams as well as ELK ones.** Swimlanes are the existing consumer of line hops and have them on today, so a swimlane with a crossing near a bend will render differently even for someone not using ELK at all. It shows up wherever a layout stacks edges in narrow lanes: ELK routes subgraph-internal edges 10px apart, and 10px does not hold a 7.07px corner cut plus a 6px hop.

    A crossing is also ignored now when it lands inside the stretch where either edge is rounding a bend. Crossings are found on polylines, but a rounded edge is not drawn as its polyline — it leaves the line up to 7.07px before each bend and rejoins it that far after. A crossing found inside that stretch is somewhere the stroke never goes, so the hop was arching over blank paper while the two lines carried on touching beside it.

-   [#8146](mermaid-js/mermaid#8146) [`def4c81`](mermaid-js/mermaid@def4c81) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): `redux-color` and `redux-dark-color` now define every variable their base themes define, instead of silently falling back to untuned values for `primaryBorderColor`, `clusterBkg`, `clusterBorder`, `altBackground`, `compositeTitleBackground` and the state and requirement edge-label backgrounds. Pie, gantt and user-journey also read the theme palette rather than a single pale tint, so pie slices are distinguishable and gantt sections are visibly banded.

-   [#8189](mermaid-js/mermaid#8189) [`3802472`](mermaid-js/mermaid@3802472) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(themes): venn circles follow the `redux-color` and `redux-dark-color` palettes. Neither theme defined any `venn*` variable, so every circle rendered in one flat colour.

-   [#8185](mermaid-js/mermaid#8185) [`f5af413`](mermaid-js/mermaid@f5af413) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix: sequence diagram actors under the `neo` look and `redux` themes. Notes no longer render bold,
    a `rect` section band is no longer drawn white on white, and participants follow one vertical
    model: every lifeline starts and ends on a shared line, all participant glyphs are one size with
    their feet on a common edge, single-line labels share one baseline in the header and in the
    mirrored footer, and a multiline label grows away from the lifeline instead of moving it. The
    `classic` look is unchanged.

-   [#8157](mermaid-js/mermaid#8157) [`a3a92ba`](mermaid-js/mermaid@a3a92ba) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(sequence): index each actor colour palette by its own length.

    Every actor-drawing call site read `bkgColorArray[actorCount % borderColorArray.length]` — one palette indexed by the other's length. Under `redux-color` both palettes have twelve entries, so the wrong length happens to give the right answer and nothing is visibly broken today. It goes wrong as soon as the two differ in length: the overflow actors resolve to `undefined`, and d3 then strips the inline fill for some actors and not others.

    Both palettes now cycle within their own length, via a shared helper. An absent or empty palette still yields `undefined` rather than a substitute colour, which is what `redux-dark-color` relies on: it ships a border palette and an empty background palette so actors are outlined but not filled.

-   [#8232](mermaid-js/mermaid#8232) [`e691042`](mermaid-js/mermaid@e691042) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Circle, double circle, Delay and Display nodes size themselves from the label's height as well as its width, so a wrapped label stays inside the outline. Each was sized from the width alone and clipped any label taller than it was wide, which `flowchart.wrappingWidth: 120` made common.

    This replaces the wrapping exemption those shapes carried along with stadium and diamond: it stopped them wrapping at all, so a long label produced a single-line node hundreds of pixels wide. Stadium needs no exemption — its `w >= 1.5h` floor already keeps a wrapped label from closing the caps into a circle — and diamond never needed one.

    Stacked (`docs`) and lined (`lin-doc`) document nodes lift their label clear of the wavy bottom edge by the wave's full depth rather than half of it, so the last line is no longer clipped by the trough. Plain `doc` already did this; the two variants had copied it with the offset halved. Node dimensions are unchanged.

-   [#8154](mermaid-js/mermaid#8154) [`ba0deed`](mermaid-js/mermaid@ba0deed) Thanks [@aloisklink](https://github.com/aloisklink)! - fix: bundle `fastdom` to fix issues with global `define`

-   [#8227](mermaid-js/mermaid#8227) [`b05824a`](mermaid-js/mermaid@b05824a) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(flowchart): stop stadium labels from wrapping into a circle and exempt selected shapes from automatic label wrapping.

    Long stadium labels wrapped to `flowchart.wrappingWidth`, grew taller without growing wider, and turned the stadium into a circle. Stadium now keeps its semicircular caps with an outer width of at least 1.5 times its height and enough clearance that the label stays inside the arcs.

    Stadium (`terminal`, `pill`), circle (`circ`), diamond (`diam`, `decision`, `question`), double circle (`dbl-circ`, `double-circle`), Display (`curv-trap`, `curved-trapezoid`, `display`) and Delay (`delay`, `half-rounded-rectangle`) no longer wrap their labels at `flowchart.wrappingWidth`. Explicit line breaks are kept, and an explicit node width still takes precedence.

-   [#8192](mermaid-js/mermaid#8192) [`0ec29e9`](mermaid-js/mermaid@0ec29e9) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(state): draw the edge between a state and its note dashed under the `neo` look. It was rendering solid.

    The dashes were expressed only as CSS — `.note-edge { stroke-dasharray: 5 }` — which is enough under `classic` but not under `neo`, where `insertEdge` writes an inline `stroke-dasharray` on every edge, computed from the path length so the arrow markers keep their gaps. An inline style outranks a stylesheet rule, so the note edge took the solid pattern and the `.note-edge` rule simply lost. The note edge now declares `pattern: 'dashed'`, which is what `insertEdge` reads to choose its dash generator. `classic` is unchanged.

-   [#8225](mermaid-js/mermaid#8225) [`0d77926`](mermaid-js/mermaid@0d77926) Thanks [@knsv-bot](https://github.com/knsv-bot)! - fix(swimlane): seed cycle removal from source nodes so feedback edges are reversed instead of forward flow edges. A cycle like `task --> decision --> fix --> task` no longer places `fix` in the middle of the `start --> task` flow line.

-   [#8211](mermaid-js/mermaid#8211) [`93edd72`](mermaid-js/mermaid@93edd72) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: keep association markers apart on use-case ovals under ELK. Ovals get the same vertical padding as horizontal, edge attachment points on them are spread across the side, and the ELK renderer now accounts for every end-marker offset (extension, circle, cross, …) so a short entry stub can no longer flip a marker around.

-   Updated dependencies \[[`33442fd`](mermaid-js/mermaid@33442fd)]:
    -   @mermaid-js/parser@2.0.0

### Keeping the previous defaults in your integration

Mermaid 12 changes what unconfigured diagrams look like: ELK replaces dagre as the default layout, and `redux-color` / `neo` replace `default` / `classic` as theme and look for flowchart, class, state, ER, requirement, sequence, use case, swimlane, Venn and agentflow. If you embed Mermaid and want your users' existing diagrams to render as before, set all three explicitly at initialisation:

```js
mermaid.initialize({ layout: 'dagre', theme: 'default', look: 'classic' });
```

Diagram authors can do the same per diagram in front matter, and a value there always wins over `initialize()`.

- [Layouts: how `layout` is resolved, ELK variants, and the tiny build](https://mermaid.ai/open-source/config/layouts.html)
- [Flowchart: default theme, look and layout in v12](https://mermaid.ai/open-source/syntax/flowchart.html#default-theme-look-and-layout-v-mermaid-release-version)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: give me a 98K <240642031+inhuman-0@users.noreply.github.com>

* fix: serialize asynchronous Mermaid preview rendering

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: give me a 98K <240642031+inhuman-0@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Bug / Error Something isn't working or is incorrect

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C4 diagram not possible to create relation from boundaries to container

3 participants