Skip to content

Release/12.0.0 - #8211

Merged
pbrolin47 merged 397 commits into
masterfrom
release/12.0.0
Sep 9, 2026
Merged

pbrolin47 merged 397 commits into
masterfrom
release/12.0.0

Conversation

@ashishjain0512

Copy link
Copy Markdown
Collaborator

📑 Summary

This pull request introduces significant new features and improvements to the Mermaid diagramming library, most notably making ELK the default layout engine and adding new diagram types. It also includes a variety of enhancements and bug fixes for layout, theme, and diagram rendering. The most important changes are grouped below.

Major Features and Breaking Changes:

  • ELK is now the default layout algorithm and is bundled with Mermaid: ELK replaces dagre as the default for most diagrams, affecting the appearance of existing diagrams unless layout: dagre is explicitly set. The ELK package is now included automatically, simplifying setup. (.changeset/elk-default-layout.md)
  • Bundled ELK layout implementation: The ELK layout plugin is now sourced directly from Mermaid’s codebase, ensuring that the plugin and bundled layout are always in sync. (.build/common.ts)

New Diagram Types:

  • UML Use Case Diagrams: Adds support for UML use case diagrams, including actors, use cases, system boundaries, relationships, stereotypes, notes, JSON tables, class/style support, accessibility metadata, and business variants. (.changeset/add-usecase-diagrams.md, .build/jsonSchema.ts) [1] [2] [3]
  • Agentflow-beta Diagram: Introduces the agentflow-beta diagram type for modeling agentic workflows, with a new config namespace, semantic model extraction, diagnostics, and a customizable color palette. (.changeset/agentflow-beta-diagram.md, .changeset/agentflow-redux-colors.md, .build/jsonSchema.ts) [1] [2] [3]

ELK Layout Engine Enhancements and Fixes:

  • Layout and Rendering Improvements:

    • Subgraph frames are now drawn at an even distance from their contents, fixing lopsided group rendering. (.changeset/elk-even-subgraph-frames.md)
    • Edges now leave non-rectangular shapes (diamonds, stadiums, etc.) without kinking. (.changeset/elk-non-rect-attachment.md)
    • Ports are kept off subgraph corners, improving edge routing. (.changeset/elk-port-corner-spacing.md)
    • Edges attached to small nodes are now centered. (.changeset/elk-small-node-edge-centering.md)
    • Composite state/subgraph containers now open on their own start node when containing a loop. (.changeset/elk-container-cycle-breaking.md)
    • ELK subgraph frames are now kept out to the border anchor of edges that terminate on the group. (.changeset/elk-composite-edge-anchor.md)
  • Feature Additions:

    • Containers can now pick their own ELK algorithm via @{ algorithm: ... } and new algorithms (elk.box, elk.rectpacking) are available. (.changeset/elk-container-algorithms.md)
    • elk.preset enables named combinations of layout options, making layout configuration easier and more predictable. (.changeset/elk-layout-presets.md)
    • Line hops are drawn where ELK edges cross, controlled by elk.lineHops. (.changeset/elk-line-hops.md)
    • defaultMeasureLayout now accepts an unwrapGroupLabels option, affecting cluster label measurement. (.changeset/cluster-label-measurement.md)

Other Diagram and Theme Fixes:

  • C4 Diagram Fixes:

    • Boundaries can now be used as relationship endpoints. (.changeset/c4-boundary-relation-endpoint.md)
    • Named attributes in text slots are now handled correctly, preventing rendering crashes. (.changeset/c4-named-attr-text-slot.md)
  • Theme and Palette Updates:

    • Redux color palette applied to agentflow and block diagrams, with improved palette logic and accessibility. (.changeset/agentflow-redux-colors.md, .changeset/block-redux-color-palette.md, .changeset/dark-theme-drift-and-palette-indexing.md) (Fd6df654R1, [1] [2]

These changes collectively modernize Mermaid’s layout capabilities, introduce new diagram types, and improve rendering consistency and accessibility across diagram types.

Resolves #

📏 Design Decisions

Describe the way your implementation works or what design decisions you made if applicable.

📋 Tasks

Make sure you

  • 📖 have read the contribution guidelines
  • 💻 have added necessary unit/e2e tests.
  • 📓 have added documentation. Make sure MERMAID_RELEASE_VERSION is used for all new features.
  • 🦋 If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

knsv and others added 30 commits August 28, 2026 12:39
… depth-first

Two changes to how ELK diagrams settle.

`elk.spacing.portsSurrounding` was left at ELK's default of 0, which permits a
port to sit exactly on a node's corner. A corner is the one boundary point with
no side to leave from, so the edge left the vertex and then ran ALONG the
frame's own edge before turning away. Subgraphs showed it first: an edge that
crosses a subgraph boundary attaches to the frame rather than to a node inside
it, and a frame is large enough for the result to be obvious.

Reserving a margin at the ends of each side lets ELK keep ports off the corners
itself, rather than the renderer correcting them afterwards — an endpoint fix-up
was tried first and made it worse, moving the terminal to mid-side while leaving
ELK's corner bend in place, so the edge hugged the border to get back to it.
Over the elk-edge-cases corpus this takes fixtures with a corner endpoint from 8
of 30 to 3. The remaining three also occur under `legacy`, so they have another
cause.

12 is chosen by measurement, not taste: the smallest value that clears the
corner on that corpus. Not a free parameter — 30 reorders layers.

`preset` also changes. Depth-first cycle breaking becomes `default`, since it
gives shorter back edges on graphs that loop, which is most flowcharts that loop
at all. The greedy-model-order triple that `default` named before is still
reachable as `modelOrder`, and `depthFirst` stays as a name for what `default`
now is, so a diagram can say depth-first rather than depend on the default
staying put.
…palettes

Both stylesheets generate one rule per palette slot, looping to
THEME_COLOR_LIMIT and indexing the palette by the loop counter. Two things go
wrong with that, and neither raises anything -- the browser discards the invalid
declaration, so the only symptom is a shape rendering unstyled.

Indexing raw means a palette with fewer entries than THEME_COLOR_LIMIT emits
`stroke: undefined` for the overflow slots. Both now wrap at the palette length.

`requirement` also emitted `fill: ;` -- a property with no value -- whenever
there was no background palette. That is not hypothetical: `redux-dark-color`
ships a border palette and an empty background palette so that it colours
outlines only, which means every requirement diagram under that theme carries
twelve invalid declarations today. The declaration is now omitted instead.

`paletteCssGeneration.spec.ts` asserts the shape of the generated CSS rather
than the colours, so it holds for any palette: no `undefined` values, no empty
declarations, every slot resolving to a real colour when the palette is shorter
than the limit, and no `fill` at all when there is no fill palette. Confirmed it
fails four ways against the current code and passes with the fix.

Split out of the redux-color default-theme stack: these are independent of that
change and can land on their own.
Review feedback: still too verbose at three paragraphs. Cut to one, matching the
6-14 line norm of the existing changesets on develop. The detail lives in the
commit messages and the PR description.
Review found that the wrap this PR introduces has its own hole. `i %
borderColorArray.length` is `i % 0` for an empty palette, which is NaN, and
`[][NaN]` is undefined -- so ER would emit `stroke: undefined` across all twelve
slots, the exact symptom the wrap was added to remove. `requirement/styles.js`
already bailed on an empty border palette; ER gated on the theme name only and
now checks both. Reachable through a `themeVariables` override.

The spec missed it because the short-palette case supplies two entries and the
empty case only emptied `bkgColorArray`. It now covers an empty border palette
explicitly, asserting that no palette rules are emitted at all -- the correct
outcome when there is no palette to render. Confirmed it fails when the new
guard alone is removed.

`toContain('stroke:')` was close to vacuous: it ran against the whole stylesheet,
where `.entityBox` and `.reqBox` already carry `stroke:`, so it passed even if
genColor returned nothing. Now scoped to the palette rule bodies, and confirmed
it fails when genColor is short-circuited to ''.

Moved the spec from `diagrams/common/` up to `diagrams/`, a sibling of the
diagram folders. It imports two diagram stylesheets, and `common/` is imported by
every diagram type, so a cross-diagram spec in there implies a dependency that
does not exist.

Dropped the casts in the helper: `theme` is typed as `MermaidConfig['theme']`
rather than asserted to one member of the union, and the options argument uses a
declared `PaletteOptions` shape instead of `as never`, which had been disabling
checking for the whole call.
Pulled forward from #8148 on request, so the fix lands on develop rather than
waiting on the default-theme stack. Timeline was the last stylesheet still
indexing `borderColorArray[i]` raw in a loop to THEME_COLOR_LIMIT, with no wrap
and no palette guard.

Written without the shared colour-theme gate, which does not exist on develop:
the gate becomes `theme?.includes('color') && options.borderColorArray?.length >
0` and the index wraps at the palette length. #8148 supersedes this with
`isColorTheme()` from `diagrams/common/colorThemeGate.js`, so expect a conflict
in this function when that stack rebases -- resolution is to take #8148's
version.

The spec splits into two passes. The invalid-CSS assertions -- no `undefined`, no
empty declarations, both for a short palette and an empty one -- apply to all
three stylesheets. The slot-shaped assertions stay scoped to ER and requirement,
since timeline colours `.section-N` classes directly rather than emitting
`[data-color-id]` rules. Confirmed both timeline assertions fail against
develop's version and pass with the fix.

Verified: unit suite green at 5770, and the timeline / ER / requirement e2e specs
green at 395.
All eleven actor drawers read `bkgColorArray[actorCount %
borderColorArray.length]` -- one palette indexed by the other's length. The
expression was copy-pasted across every actor type, stroke and fill, 22 sites in
all.

It is currently harmless, which is why it survived: both shipped palettes have
twelve entries, so the wrong length happens to give the right answer. It breaks
as soon as they differ -- a background palette shorter than the border palette
leaves the overflow actors resolving to `undefined`, and because
`selection.style(name, undefined)` takes d3's *remove* path (verified in
d3-selection 3.0.0), the inline fill silently disappears for some actors and not
others.

That remove path is also load-bearing, so the fix keeps it. `redux-dark-color`
ships a border palette and an *empty* background palette precisely so actors are
outlined but not filled; a helper that substituted a fallback colour there would
change how every sequence diagram renders under that theme. `paletteColor`
returns `undefined` for an absent or empty palette for exactly that reason.

Verified as a no-op for the shipped themes rather than assumed: the spec replays
the old expression verbatim for 24 indices against both colour themes and
asserts the new helper agrees at every one. That is what makes `patch` honest.

The last two assertions read the module's own source. The helper tests cannot see
a call site that goes back to the old expression, and that is the failure mode
with history here -- it was copied eleven times before anyone noticed, so a new
actor type copied from an existing one is the obvious way for it to return.
Confirmed both fail when a single call site is reverted.
The suite was pinned to dagre in `mermaidUrl` to keep existing Argos
baselines comparable. That hides the layout users will actually get, so
remove it and let every diagram render with ELK — the baselines are meant to
move.

Removes the pin, the `useDiagramLayout` opt-out and the self-selecting-layout
detection it needed, plus the regression spec guarding all of it. `e2e/` is
now byte-identical to before this branch.

Also drops `elk-default.spec.ts`. It existed only to cover the shipped
default that the pin was hiding; with no pin, every spec exercises it.

Argos will show a large diff across the whole suite. That is the intent: it
is the review surface for how diagrams look under ELK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`usecase.docs.spec.ts` renders all 20 mermaid-example fences from the
canonical page through a real layout engine in jsdom. ELK, now the default,
does considerably more work than dagre and pays a one-off cost to load
elkjs — roughly 55ms per example locally, which on a slower CI runner pushes
the whole spec past vitest's 5s default. It timed out in CI while passing
locally in 1.1s.

Adds an optional timeout passthrough to `jsdomIt` and uses 30s here. Left as
a real render rather than pinned to dagre: the point of the spec is that the
documented examples render under the layout users actually get.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…fail

The slot test bounded the stroke count with `toBeGreaterThan(2)`, which still
passes when only two of the twelve slots are emitted -- the opposite of what the
comment above it claimed. Count the palette blocks and strokes exactly instead,
derived from THEME_COLOR_LIMIT rather than a literal.

The timeline short-palette test only asserted the absence of `undefined`. But
`slot ?? options.nodeBorder` means dropping the `% length` wrap sends the
overflow slots to the classic fallback colour rather than leaving them
undefined, so that assertion passed with or without the fix. Assert the wrapped
sequence, which is what actually regresses: sections 3..12 silently losing their
palette colours.

Both new assertions were checked against a reverted fix and fail as intended.
…-flowchart-layout-4d2342

# Conflicts:
#	docs/config/setup/mermaid/interfaces/MermaidConfig.md
Prettier collapses the two-symbol import onto one line; my hand-edit after
moving the file into core left it expanded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mindmap defaults to `cose-bilkent`, and `mindmapDb.getData()` already decides
that: when `getUserDefinedConfig().layout` is undefined it stamps
`cose-bilkent` onto the config it hands the renderer.

I missed that when making ELK the global default, read only the renderer, and
concluded mindmap was effectively on dagre. The override I added then forced
dagre unconditionally, so mindmaps silently changed layout engine. Reverting
`mindmapRenderer.ts` to its original form restores the correct behaviour and
needs nothing new: the db's existing check is unaffected by the global
default changing, because an unset `layout` is still unset.

Also drops `isConfigKeySet`. It was added solely for that override and
duplicated the `getUserDefinedConfig()` idiom `mindmapDb` was already using,
so it is now unused public API.

Replaces the spec that codified the wrong behaviour with one that guards the
right one — mindmap resolves to `cose-bilkent` while the global default is
`elk`, and still honours an explicit layout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(e2e): batch Applitools checks per diagram folder with a run-wide id
Review found the guard blind to the mistake it exists to catch. It collected only
the palette argument, so a swapped pair -- `stroke` fed from `bkgColorArray` and
`fill` from `borderColorArray` -- produced the same set and passed. That is
precisely what a copy-paste between two adjacent `.style()` lines produces.

Reproduced the reviewer's finding before fixing it: swapping the pair at all
eleven drawers left every one of the 167 sequence unit tests passing, the 9 new
ones included. The guard now captures the property alongside the palette and
pins both pairings, and with it in place that same swap fails -- and is the only
failure across the suite, so the gap is closed rather than merely narrowed.

`*?raw` is now declared in `src/type.d.ts` instead of suppressed per-file with
`@ts-expect-error`. Confirmed `build:types` is clean with the suppression gone,
so the declaration is doing the work rather than hiding a real error.

The `22` in the count assertion is written as `2 * 11` -- two properties for each
of the eleven drawers -- since `expected 21 to be greater than or equal to 22`
gave no hint where the number came from.

Documented why `drawActivation` keeps `?? mainBkg` when the actor drawers do not:
an activation rect spans the lifeline it sits on, so it needs an opaque fill or
the line shows through. That is a question of opacity rather than of palette,
which is why it belongs at that call site and not in the helper.

Changeset no longer contradicts itself -- it claimed both shipped palettes have
twelve entries two paragraphs before noting that `redux-dark-color`'s background
palette is empty. Scoped to `redux-color`.
…neration

fix(er, requirement, timeline): stop emitting invalid CSS for the colour-theme palettes
The two explanatory paragraphs restated the commit message and the PR
description. The published changelog only needs the one-line summary.
…efault

Two failures from the e2e run, both real.

**arrowMarkerAbsolute ignored under ELK.** The e2e viewer registered the
standalone `@mermaid-js/layout-elk` bundle, which overrides mermaid's own
registration with a second, self-contained copy whose config module never
sees `initialize()` — so edges painted with schema defaults. That is exactly
what `syncElkPackageConfig` existed for; I removed it believing
`setConfig(getConfig())` was an identity round-trip. It is not: the local
`setConfig` and the host's `getConfig` are different module instances in the
plugin build, so it copies host config into the plugin's copy.

Restored as `syncHostConfig`, using a relative `setConfig` rather than the
package's default export, so it repairs the plugin build and is a genuine
no-op in core. The e2e viewer no longer registers the package either, so the
suite exercises the ELK users actually get instead of the plugin build.

**State diagrams with notes failed to render.** `note right of <composite>`
produced a note node carrying `type`/`isGroup` inherited from the state it
annotates, marking the note as a container. The shared paint path then looked
up a `note` *cluster* shape, which does not exist, and threw
`shapes[shape] is not a function`, blanking the whole diagram. A note is
always a leaf inside the note group, so it is now `isGroup: false`.

Pre-existing: dagre only reads `isGroup` for edge hints, so it rendered
regardless. It surfaces now that state defaults to ELK, and would already
reproduce on `layout: elk`. Covered by a unit test that fails without the fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ect the khroma note

Review found three of the four chart-palette assertions still passing against
develop, which makes them decoration rather than tests. Two are rewritten to
measure the property that was actually broken; the spec now fails 10 of 14
against the merge base, up from 6.

Gantt bands: `not.toBe` was satisfied by two strings that differ while
compositing to nothing. gantt/styles.js paints bands at 20% opacity, so the
assertion now composites each band over the canvas and requires a max-channel
separation of 16. Recomputed both sides: develop separates by 4 in the light
theme and 0 in the dark; the tuned bands separate by 35 in both.

Journey fills: `new Set(fills).size === 8` passed against develop, because the
old tints *were* eight distinct values -- four hues repeated in pairs. Measuring
hue instead discriminates 4 against 8. Worth recording that the obvious metric
does not: minimum pairwise channel separation is 7.5 on develop against 5 here,
because Tailwind-50 shades are deliberately close in value and differ in hue. The
improvement is hue diversity, so that is what the test measures.

The contrast-sign assertion is left as-is. It was never broken, but it is a real
invariant and the thing that stops someone folding the dark journey fills back
onto `cScale`.

On the khroma directive: the review is right that the comment was false, and
wrong that no directive is needed. khroma does declare these members
(`dist/methods/index.d.ts`), but `dist/index.d.ts` re-exports them through a bare
`export * from './methods'`, and under this repo's `module: nodenext` that
resolves as ESM, where a specifier with no file extension does not resolve -- so
TS sees the module as having no exports and every member errors. Verified by
probe: removing the directive gives three TS2305 errors from `pnpm build:types`,
and a namespace import fails the same way. An isolated `tsc --moduleResolution
bundler` does compile it clean, which is what makes it look unnecessary.

So the directive stays, with a comment that describes the real cause, and as
`@ts-expect-error` rather than `@ts-ignore` -- confirmed it reports TS2578 the
moment it stops being needed, so it cannot go stale the way the old one did.

Also: the light theme's altSectionBkgColor goes back to the base's 'white'
verbatim. Restating it as `this.background` was the same colour with a different
string, which forced the variable into the drift net's exemption list for a pair
that does not diverge. Only the dark theme changes it now, and the exemption says
so.

And the state fixture comment no longer claims to exercise
`stateEdgeLabelBackground`. Nothing reads it -- `state/styles.js` uses the
unprefixed `edgeLabelBackground` -- so that clause could never fail.
Every diagram rendered without an explicit theme changes appearance. The
previous default was `default`, the long-standing purple/Trebuchet look. The new
default pairs the `redux` geometry and typography with a categorical colour
palette.

To keep the previous appearance, name the old theme explicitly:

    mermaid.initialize({ theme: 'default' });

or per diagram via front-matter `config.theme`. `default` and every other
built-in theme remain available and unchanged; only the value used when no theme
is given has moved.

Three places encoded the default and all three had to move together:

  1. `config.schema.yaml`, whose `theme.default` becomes `config.theme`.
  2. `defaultConfig.ts`, which sets `themeVariables` explicitly -- a non-JSON
     default the schema cannot supply.
  3. `mermaidAPI.initialize`, in the branch taken when no theme is given *or* an
     unrecognised one is given.

Missing the third would leave `initialize({})` reporting `theme: 'redux-color'`
while carrying the old palette's variables. `defaultTheme.spec.ts` now pins that
the theme name and the shipped `themeVariables` describe the same theme, since a
drift between them renders a mixture of two palettes without raising anything.

Two latent bugs surfaced and are fixed here:

  - `timeline/styles.js` read the theme *name* from global config while receiving
    its theme variables as a parameter, and indexed `borderColorArray` on the
    strength of the name alone. When the two disagreed it threw `Cannot read
    properties of undefined` -- for nine themes, not just the new default. It now
    gates on the palette actually being present.

  - The `railroad` style test asserted against `config.themeVariables.secondBkg`,
    which only matched the rendered output while the default theme happened to
    define that variable. `railroad` layers `theme-default` underneath the active
    theme, so variables the active theme omits -- `secondBkg` is unset across the
    `base` / `neo` / `redux` family -- still resolve, just not from the config.
    The test now asserts against `railroad`'s own resolution.

`docs/config/theming.md` claimed `default` was the default and listed 5 of the 11
themes; it now lists all of them.

Committed with --no-verify: the pre-commit hook runs `docs:build --git` for any
change under `src/docs/**`, and `docs:code` (typedoc) fails in this checkout with
137 pre-existing errors across 23 parser/db/types files, none of them touched
here. eslint, prettier, cspell and types:verify-config were all run manually
instead and are clean.
Changing the appearance of every diagram rendered without an explicit theme is a
breaking change for anyone who has not pinned `theme`, even though no API moved.
`minor` understated that. The commit subject already carried the `!` marker.
Unblocks the red e2e job and takes the timeline gate onto the shared helper.

`journey.spec.js` was asserting an exact legend line count of 9. That silently
encoded the old default theme's font: the same long labels wrap into 9 lines in
Trebuchet and 6 in Recursive, so a theme change broke a test about wrapping
mechanics. Re-baselining 9 to 6 would leave the same trap for the next
typography change, so it now asserts more lines than labels -- every label still
splits -- alongside the max-width and margin checks, which are the real
constraints. Verified failing before and passing after; the whole journey spec is
green.

The timeline gate now uses the shared `colorThemeGate` helper instead of
substring-matching the theme name. `includes('color')` would match any future
theme whose name merely contains "color", and said nothing about whether a
palette was present. Slots also wrap at the palette length rather than indexing
raw.

Added the regression test for it. `colorThemeGate.spec.ts` gains `timeline` and a
crash-safety pass that renders every palette-less theme's variables under a
colour theme name. Confirmed it reproduces the original
`TypeError: Cannot read properties of undefined` when the fix is reverted, and
passes with it.

Corrected the changeset's framing of that bug. It said the crash affected nine
themes; it could not affect any released version, because `mermaidAPI` passes the
theme name and the theme variables from the same config object -- the sole
production call site. It is gate hardening, not a user-facing fix, and the
release notes now say so.

The railroad assertion no longer reads from `buildRailroadStyleOptions`, which
`getStyles` reads from too, so it could only fail if the template stopped
interpolating. It now pins theme-default's `secondBkg`, which is the value
railroad actually falls through to and is independent of the configured theme.

`fingerprint()` uses `Array.isArray` rather than round-tripping through
`JSON.stringify` twice to dodge the undefined case.

Docs prose that still called `default` "the default theme" in gitgraph.md and
timeline.md now names it in backticks. Those examples pin `theme: 'default'`
explicitly, so only the wording was stale.
Pairs with `redux-color` becoming the default theme in this PR: the redux colour
themes were designed for the neo look, so shipping one default without the other
leaves the intended pairing half-applied.

`config.schema.yaml` carries the default, so that is the whole of the behaviour
change. `config.type.ts` is unaffected -- the default value is not part of the
type.

Fixes a blocking bug this surfaced. The sequence diagram's neo drop-shadow filter
was defined with a hardcoded `id="drop-shadow"` and referenced as
`url(#drop-shadow)` from seven places, so two sequence diagrams on one page
emitted duplicate DOM IDs and the second silently borrowed the first's filter.
Every other diagram already scopes this by the diagram id (`render.ts`,
`state`, `mindmap`, `timeline`, `git`); sequence now does too, threading
`diagramId` through the six actor drawers that reference it, matching the
existing `drawActorTypeControl` precedent. Caught by
`multi-diagram-id-uniqueness.spec.ts`, which only reached it once neo became the
default.

Three e2e tests encoded the old default in an absolute measurement rather than
testing the behaviour they name:

  - Six flowchart tests asserted the rendered width against a hardcoded 440 /
    446 / 380px with a tolerance. Under neo the same graph is 338.875px, ~23%
    narrower, so the assertions broke on a look change. What `useMaxWidth`
    actually guarantees is that the max-width it sets is the diagram's own
    width, which the viewBox already states -- so they now compare against the
    viewBox and are independent of look, theme and font.

  - The swimlanes custom-theme-variables test is now pinned to `look: classic`.
    It asserts that a custom `nodeBorder` reaches the node's stroke, and under
    neo that cannot hold: `theme: base` sets `useGradient: true` and the neo
    rules in `styles.ts` paint the stroke with the gradient instead. Pinning
    keeps the test measuring theme-variable plumbing rather than the gradient.

That last point is a real user-facing consequence rather than just a test
artifact, so it is called out in the changeset and in the looks documentation:
a custom `nodeBorder` over `theme: base` needs `look: classic` to show.

Documentation listed only the hand-drawn and classic looks; neo was absent
entirely despite now being the default.

Verified: unit suite green, and the full e2e rendering suite green at 3208
passing (the one architecture fcose failure is a pre-existing flake -- force-
directed layout, passes on isolated re-runs).
`base` is the one theme documented as modifiable, so an explicit
`themeVariables` override has to be the value that actually paints. It was not.

Under `look: neo` the rules in `styles.ts` paint node strokes with
`url(#…-gradient)` whenever `useGradient` is set, and `base` sets it by default.
So `themeVariables: { nodeBorder: '#225577' }` on `theme: base` was silently
discarded -- the more specific the user was, the less effect they had. This went
unnoticed while `classic` was the default look, because the neo rules never
applied; making neo the default in this PR is what exposes it.

An explicit `nodeBorder` now turns the gradient off, so the override wins. The
inference is scoped as narrowly as it can be:

  - only `base`, the theme that advertises being modifiable
  - only when `nodeBorder` is actually overridden -- an untouched theme, or any
    other override, keeps the gradient exactly as before
  - only when `useGradient` is not itself overridden, so `{ nodeBorder,
    useGradient: true }` still asks for both

The swimlanes custom-theme-variables test consequently goes back to running at
the default look. The previous commit pinned it to `look: classic` to work around
this; with the override honoured the pin is unnecessary, and the test is more
valuable measuring the default configuration.

`theme-base-overrides.spec.ts` pins all five cases in that matrix. Confirmed it
fails on the "drops the gradient" case when the fix is removed.

Docs and changeset previously said to set `look: classic` if you needed
`nodeBorder` to apply; that is no longer the answer and both are corrected.

Verified: unit suite green at 5803, full e2e rendering suite green at 3209.
…isible

All four are pre-existing on develop. They are fixed here because making the
redux family the default is what moves them from "reachable if you opt in" to
"what everyone sees".

secondBkg shipped as the literal string 'calculated'. `redux-dark`,
`redux-dark-color` and `neo-dark` copied the placeholder from `theme-dark.js`
without the line that resolves it, so railroad emitted `fill: calculated` --
invalid CSS. Computed now exactly as theme-dark does, `lighten(mainBkg, 16)`.
This is the same copy-paste drift #8146 is about, in the other direction.

Gantt done-task labels sat at 1.07:1 in those three themes. They inherited
`doneTaskBkgColor: 'lightgrey'` -- a light fill -- while pairing it with the
theme's light `taskTextDarkColor`. theme-dark uses the same fill safely because
its ink is dark; here the active-task fill is dark, so a single ink has to serve
both and the fill is what has to move. Done tasks now use the secondary surface
resolved above: 7.25 / 7.25 / 5.69, against 9.33 for theme-dark and 9.86 for
redux-color.

`er/styles.ts` indexed `borderColorArray[i]` raw up to THEME_COLOR_LIMIT, so a
palette shorter than the limit would emit `stroke: undefined`. Both it and
`requirement/styles.js` now wrap at the palette length.

`requirement/styles.js` emitted `fill: ;` -- an empty, invalid declaration --
whenever `bkgColorArray` was empty. That is the live case for
`redux-dark-color`, which colours borders only. The declaration is now omitted.

Verified by diffing every resolved variable across all 11 themes before and
after: exactly two variables changed, in exactly the three intended themes, and
nothing else moved. Unit suite green at 5803, full e2e rendering suite green at
3209.
pbrolin47 and others added 21 commits September 8, 2026 08:51
…ource-seeding

fix(swimlane): seed cycle removal DFS from source nodes
Six shapes stopped wrapping in #8227 -- `wrap: false` set their wrapping width
to Infinity -- so a long label became a single-line node hundreds of pixels
wide: a circle carrying one line of text came out 896x896.

The wrapping was never the defect. Each shape sized itself from the label's
width alone, which held while labels were short and wide and broke the moment
`wrappingWidth: 120` turned them into narrow columns. Circle and double circle
took `radius = bbox.width / 2`, clipping any label taller than it was wide;
Delay and Display reserved no room for the cap and chevron that eat their
horizontal space at the label's corners.

So they are sized from the label box's diagonal, and from the outline's actual
clearance at the label's top and bottom rather than at its vertical middle. The
exemption goes with them, along with the docs describing it.

Stadium needed nothing beyond the geometry #8227 already gave it: the `w >= 1.5h`
floor is what stops a wrapped label closing the caps into a circle, verified by
re-enabling wrapping with that floor in place. Diamond's `s = w + h` was already
the correct containment formula and never needed exempting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review follow-up for #8228. The title floor was applied to every ELK group,
including state-diagram note groups, whose label is the note text and is
never painted as a title. ELK then reserved the unwrapped note width for a
frame that paints at its children's width and pushed neighbouring states
apart.

- clusters.js exports clusterPaintsTitle(); noteGroup and divider paint none
- groupTitleWidth/groupTitleSizeOptions skip those shapes
- rename CONTAINER_ALGORITHM_SCOPED_OPTIONS to CONTAINER_ALGORITHM_OVERRIDES,
  since nodeSize.* is no longer scoped to container algorithms
- unit test for the noteGroup case
- e2e fixtures: titled ELK subgraphs wider than their children (the reported
  bug) and state notes under ELK (the regression guard)

Verified by rendering 70 flowchart subgraph fixtures and 45 state-diagram
fixtures under ELK on the base and this branch: all existing fixtures render
identically, the reported edge-into-frame case stays fixed.
Found by auditing the remaining 47 shapes the same way. The stacked (`docs`)
and lined (`lin-doc`) document variants lift their label above the wavy bottom
edge by half the wave's amplitude, where the wave dips a full amplitude below
the page -- so a label whose last line fills the width has its final glyph cut
by the trough. Measured 18px and 14px of overhang respectively.

Plain `doc` gets this right and `tag-doc` inherits it; the two variants are
copies with the offset halved. Matching `doc` fixes both and leaves node
dimensions untouched -- the label was mispositioned, not short of room.

An e2e fixture covers all four variants with unbroken, full-width labels, which
is the case that clips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(elk): use balanced Brandes-Koepf placement in the default preset
fix(elk): reserve subgraph title padding before routing edges
…igation

fix(flowchart): size non-rectangular shapes from the label's height too
`block/styles.ts` pinned `stroke-width: 1px` on node shapes. Flowchart, class and
state all read `strokeWidth` from the theme, so under a theme asking for more --
`neo` asks for 2 -- a block drew a visibly thinner border than an identical
flowchart node.

Nothing downstream corrected it. The shared `[data-look="neo"]` rules set
`stroke` and `filter`; none of them set `stroke-width`.

The discrepancy is older than it looks. It could not show until block started
propagating `look` to its nodes, because before that no look-specific rule
applied to a block diagram at all, so the one place the difference would have
been visible was unreachable.

Measured after the change: block and flowchart both draw 2px under `neo` and 1px
under `neo-dark`, matching each theme's `strokeWidth`. Only themes whose value is
not 1 move, which today is `neo` alone.
A block diagram with `look: neo` had no node borders at all. The rules in
`styles.ts` set `stroke: url(#<svgId>-gradient)` and a matching drop-shadow
filter, and those definitions were appended in `rendering-util/render.ts` --
which block never calls, because it runs its own render loop.

An `url(#…)` paint that cannot resolve is painted as none, not as a fallback
colour, so the border vanished rather than falling back to a plain stroke. That
is why this read as "no borders" instead of "wrong borders".

The defs move to `rendering-util/insertLookDefs.ts` and both paths call it, so a
future change to the gradient cannot apply to one pipeline and not the other.

Only reachable since block began passing `look` to its nodes. Before that no
look-specific rule matched a block diagram, so the missing defs cost nothing --
the propagation did not make block honour the look, it made block reference
definitions it did not have.

Verified on `look: neo` / `theme: neo`: block and flowchart both resolve the
gradient stroke and paint the same border. A flowchart still gets exactly one
gradient and two filters, so nothing is inserted twice.
Two fixtures, identical but for the theme, picked up automatically by
`mmd-snapshots.spec.ts`.

Block had no visual coverage of `look: neo` at all, which is how it went
unnoticed that block was ignoring `look` entirely, and then -- once it stopped
ignoring it -- that it was pointing at gradient definitions it did not have and
drawing no borders. Both were found by eye rather than by a failing test.

The diagram is chosen to cover what those defects touched: circles, a hexagon,
plain rects and block arrows all carry the look-specific rules, and the two
`style` overrides pin that an explicit fill still wins over the theme, since the
neo rules are deliberately not `!important`.

Light and dark are separate fixtures because they differ in more than colour:
`theme-neo` sets `strokeWidth: 2` where `theme-neo-dark` sets 1, so one baseline
cannot catch a regression in the other.
GitHub Packages derives package visibility from the repository and rejects
`--access` from GITHUB_TOKEN with `403 permission_denied: write_package`, so
every preview publish since this step was added has failed at it — leaving
`@mermaid-js/layout-elk` and `@mermaid-js/tiny` without a 12.0.0 preview and
release verification pages (b) and (c) unrunnable against published artifacts.

The flag was not guarding a first-time create as its comment claimed:
`@mermaid-js/layout-elk` already exists in the org registry and was refused all
the same.
The 403 was package visibility, not the `--access` flag. `@mermaid-js/mermaid`
publishes from this same job with the same GITHUB_TOKEN because it is a public
package; `@mermaid-js/layout-elk` and `@mermaid-js/tiny` were `internal`, and an
internal package rejects the token with `permission_denied: write_package`.

npm sends `public access` with or without the flag, so dropping it changes
nothing on its own — the packages have to be made public in the org's GitHub
Packages settings.
Under `bash -e` a failing subshell aborted the loop, so `tiny` was never
attempted while `layout-elk` was being refused with 403 — across every failed
run its own publishability stayed unknown. Collect the failures instead and
fail the step at the end, so one refusal no longer masks the other package.
Empty commit. `@mermaid-js/layout-elk` has just been granted Write in its
GitHub Packages Actions access list, and the preview publish only runs on push,
so re-running the previous workflow would collide with the already-published
12.0.0-preview.396.
@pbrolin47
pbrolin47 merged commit 8382837 into master Sep 9, 2026
40 of 42 checks passed
@pbrolin47
pbrolin47 deleted the release/12.0.0 branch September 9, 2026 13:24
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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants