Repository navigation
feat(themes): colour swimlane lanes in the redux colour themes - #8176
ashishjain0512 merged 3 commits into
Conversation
Lanes are what a swimlane diagram is about, so they are exactly what a categorical palette is for -- but they rendered uniformly grey under `redux-color` and `redux-dark-color` while flowchart subgraphs, drawn from the same `flowDb` slots, were already tinted. The slots were there. Three things stopped them reaching the lane. `swimlane.js` never called `stampColorSlot`, so no lane carried the `data-color-id` the palette rules match on. `swimlanes/styles.ts` set the lane border `!important`. That `!important` is needed only to outrank `[data-look="neo"].cluster rect`, which it ties with on specificity -- but it would also outrank every palette rule. Scoped to `:not([data-color-id])` it does the one job it was added for and stays out of the palette's way; `data-color-id` is stamped only by the themes that carry a palette, so every other theme keeps today's border exactly. The generic `.cluster` palette rules cannot be reused for lanes. A lane is two rectangles, and under handDrawn its body asks roughjs for `fill: 'none'`, which roughjs answers with a hachure path carrying `stroke="none"` -- the generic `path` rule would paint that invisible hachure and fill both outline paths solid. Lanes are excluded by `:not(.swimlane)` and take their own block in `flowchart/styles.ts`, which covers both ways a swimlane reaches that stylesheet: the `swimlane-beta` diagram, and a plain flowchart given `layout: swimlane`. Title band and body take the same fill, which is what an uncoloured lane already looks like -- the border between them is what separates the title from the content. handDrawn takes the outline path of each half in the border colour and the title band's hachure in the background tint, so the two looks read the same. Two bugs on the synthetic default lane The lane that collects ungrouped nodes is synthesised by the layout rather than declared, so nothing upstream gave it the two properties every declared lane arrives with. Without `look` it rendered as a classic rect inside a handDrawn diagram and matched no `[data-look="..."]` rule; without a `colorIndex` it reused slot 0 and came out the same colour as the first declared lane. It now takes the diagram's look and the slot one past the highest already handed out. Tests The unit specs pin the emitted CSS, because that is where this fails silently: a lane renders identically whether a declaration was discarded, outranked, or never emitted. The e2e specs assert on `data-color-id` and computed styles, which is the only way to prove the stamped attribute meets the emitted selector on the element. Both were mutation-checked -- removing the stamp fails four e2e assertions, and reverting either CSS scoping fails the unit ones. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-redux-color-63d1fd Stacked on #8148, which makes `redux-color` the default theme -- the change that turns per-lane colour from something you opt into with an explicit `theme` into what every swimlane diagram renders as. Merged in so this branch is tested and reviewed against the palette it will actually ship under. One conflict, in `colorThemeGate.spec.ts`. Both sides widened the same two lists: #8148 folded `er`, `requirement` and `timeline` into the shared gate spec, and this branch added `swimlanes`. Both sets are kept, and `swimlanes` is added to `SLOT_STYLESHEETS` as well as `STYLESHEETS` so the slot-coverage pass -- which hands the stylesheet a palette shorter and longer than `THEME_COLOR_LIMIT` -- runs against the lane rules too, not only the crash-safety pass. Committed with --no-verify: lint-staged stashes to run, which is not safe mid-merge, and the staged files are #8148's already-formatted content plus one resolved spec that prettier and eslint were run on directly.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughSwimlanes now receive distinct palette colors under Redux themes. Synthetic default lanes inherit the diagram look and receive separate color slots. Rendering and stylesheet rules support classic and handDrawn lanes, with tests covering palette, precedence, plain themes, and snapshots. ChangesSwimlane palette coloring
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to The PR adds per-lane coloring for Redux color themes while preserving existing styles and explicit overrides; no actionable merge-blocking risk remains beyond normal checks and review. Sequence Diagram(s)sequenceDiagram
participant Layout
participant SwimlaneRenderer
participant ColorThemeGate
participant LaneStyles
Layout->>SwimlaneRenderer: Provide lane colorIndex and look
SwimlaneRenderer->>ColorThemeGate: Stamp data-color-id
ColorThemeGate->>LaneStyles: Apply palette slot selectors
LaneStyles-->>SwimlaneRenderer: Style lane title and body
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 8 files. (1 skipped: 1 unsupported.) Full details: Description checkExplanation The description is detailed and on-topic. It explains the implementation, design decisions, merge dependency, testing, changeset, documentation status, and known limitations. It does not reproduce the template checklist or include a resolves issue line, but these omissions are non-critical because the required information is otherwise covered.
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🦋 Changeset detectedLatest commit: 431141f The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
ashishjain0512
left a comment
There was a problem hiding this comment.
[sisyphus-bot]
Reviewed against the merged state of this branch (0c43a13), so #8148's palette default is in scope as you intended. Ran the three unit specs locally — 226 passing, and I dumped the generated stylesheet to check the emitted rules directly rather than trusting the assertions alone.
What's working well
🎉 The root-cause write-up is the best part of this PR. Three independent reasons a lane stayed grey — the missing stamp, the !important outranking the palette, and the generic path rule being wrong for a two-rect shape — each traced to the mechanism rather than patched over. The :not([data-color-id]) scoping in swimlanes/styles.ts:24 is the right call: it keeps the !important doing the one job it was added for (beating [data-look="neo"].cluster rect) instead of dropping it and breaking neo.
🎉 Testing the shape of the generated CSS is the correct instinct here. A lane really does render identically whether a declaration was discarded, outranked, or never emitted, and lanePalette.spec.ts covers all three. The "no unscoped .swimlane.cluster rect { left behind" assertion and the !important-free check are the two that would actually catch a future regression.
🎉 Adding swimlanes to SLOT_STYLESHEETS rather than just STYLESHEETS in colorThemeGate.spec.ts:48 — the shorter/longer-than-THEME_COLOR_LIMIT sweep is exactly the pass that has caught slot drift before, and the narrowing to data-color-id="color- (rather than the bare attribute) is correctly reasoned.
🎉 Two real bug fixes carried along. The synthetic default lane missing look was a genuine pre-existing rendering bug independent of colour, and the colorIndex ?? 0 collision would have shipped silently. Both pinned in helpers.prepareLayout.spec.ts.
🎉 The hasBkgColors guard has a test that proves it is load-bearing, not defensive — asserting redux-dark-color ships an empty bkgColorArray and that the derived rule is absent. That is the version of the assertion that survives a palette retune.
Security: no new XSS surface. stampColorSlot writes data-color-id="color-N" where N is colorIndex % palette.length; the new class names are static literals; safeLook already guards the one selector component reachable from diagram text; no DOMPurify config, foreignObject, or innerHTML path is touched.
Things to address
🟡 [important] handDrawn lane bodies take no palette fill, classic/neo ones do
I dumped the emitted stylesheet for redux-color under look: handDrawn. Slot 0 emits:
.swimlane.cluster rect.swimlane-title,
.swimlane.cluster rect.swimlane-body { stroke: #E879F9; fill: #FDF4FF; }
.swimlane.cluster .swimlane-title path:nth-of-type(2),
.swimlane.cluster .swimlane-body path:nth-of-type(2) { stroke: #E879F9; }
.swimlane.cluster .swimlane-title path:first-of-type { stroke: #FDF4FF; }(prefixes elided). There is no counterpart to that last rule for .swimlane-body, so under handDrawn the body's hachure keeps the stroke="none" roughjs gave it. Net effect under the same theme:
| Look | Title band | Body |
|---|---|---|
| classic / neo | palette border + palette fill | palette border + palette fill |
| handDrawn | palette border + palette hachure | palette border, no fill |
The PR description opens with "The title band and the body take the same fill", which describes the classic column only. Since #8148 makes redux-color the default, this is the difference between "the whole lane is tinted" and "only the title band is tinted" depending on look.
The symmetric fix looks like one more rule inside the same hasBkgColors guard — the body's hachure path exists (roughjs's if (o.fill) in generator.js:53 is truthy for the string 'none', which is why path:nth-of-type(2) works at all), it just carries stroke="none":
${slot}.swimlane.cluster .swimlane-body path:first-of-type { stroke: ${bkgColorArray[i % bkgColorArray.length]}; }That said — a hachure across a full lane body may well read as too noisy behind the nodes, and you have looked at the render and I have not. If the asymmetry is deliberate, could it go in the comment at flowchart/styles.ts:44-52? Right now that comment explains why lanes are excluded from the generic rules but not why the body's fill is dropped in one look and not the other, so the next reader will read it as an oversight.
🟡 [important] The handDrawn palette rules are never verified against a real render
The e2e additions are strong on the classic path — data-color-id distinctness, title/body agreement, per-lane difference, none-guard, user style. Every one of those queries rect.swimlane-title / rect.swimlane-body, which only exist under classic/neo.
For handDrawn the only rendering assertion is swimlanes-default-lane-handdrawn (swimlanes.spec.ts:353-359), which checks that paths exist rather than what colour they are. The two renders coloured handdrawn lanes tests are snapshot-only, and per your own note there are no committed baselines — so nothing currently proves that path:nth-of-type(2) selects the outline on a rendered lane.
That selector encodes an assumption about roughjs's emission order (fill path first, outline second) sourced from a transitive dependency's internals. It happens to be correct on 4.6.6, and the local patch is types-only so it isn't at risk from that — but it is precisely the "stamped attribute meets emitted selector" half you argue elsewhere only a render can cover, and it is the half left uncovered. One computed-style check mirroring the classic one would close it:
const outlines = await page
.locator('g.cluster.swimlane .swimlane-body path:nth-of-type(2)')
.evaluateAll((paths) => paths.map((p) => getComputedStyle(p).stroke));
expect(new Set(outlines).size).toBe(5);
expect(outlines.filter((s) => s === 'none')).toEqual([]);Worth the same for .swimlane-title path:first-of-type against the background palette, since that is the rule the hasBkgColors guard turns on and off.
🟢 [nit] The changeset's "explicit style still wins" holds for classic/neo only
.changeset/redux-color-swimlane-lanes.md ends with "Explicit style on a lane still wins over the palette." True under classic/neo — nodeStyles lands as an inline style with !important. Under handDrawn it is true for the title band (the inline borderStyles / backgroundStyles writes at swimlane.js:126-127) but not for the body, which gets no inline style at all — and bodyOptions at swimlane.js:111 passes stroke: laneStroke / fill: 'none' as the options argument to userNodeOverrides, which Object.assigns over the user's values, so the user's stroke was already being discarded there before this PR.
So: pre-existing, not a regression, and out of scope to fix here — but the changeset is user-facing and currently overstates it. Either narrow the wording, or leave it and open a follow-up for the handDrawn lane body ignoring style entirely. The keeps an explicit lane style ahead of the palette e2e test only exercises classic, which is why this didn't surface.
🟢 [nit] laneRule is used once while its two siblings are written longhand
flowchart/styles.ts:81-82 introduces laneRule with the same "append to both prefixes separately" rationale as collapsedRule, but only the path:nth-of-type(2) rule uses it — the classic rule at :96-97 and the title-hachure rule at :110 are spelled out. The longhand at :96 is justified (the rect. type selector isn't expressible through the helper), but :110 could be laneRule(' path:first-of-type') scoped to the title half. Minor; mostly flagging because a helper with one call site reads as leftover.
Notes, no action needed
💡 Docs. Agreed that matching #8147 is the consistent choice for this PR. If you do the follow-up you offered, documenting the palette once for subgraphs and lanes together in config/theming.md is the better shape than a per-diagram section.
💡 Slot ordering. Leaving flowDb's pre-order walk alone is right, and the reasoning (a nested container shouldn't inherit its parent lane's colour; the palette is categorical) is worth keeping where it is. One consequence worth being aware of: a diagram with 12+ containers in total will wrap before it runs out of lanes, so two lanes can collide on a slot with only a handful of lanes declared. That is inherent to a 12-entry categorical palette and not something to solve here.
claude/redux-color-default-theme, so this needs #8148 first, as you flagged. Whoever merges should retarget to develop rather than merging into the stack branch.
Summary
Tally: 🔴 0 / 🟡 2 / 🟢 2 / 💡 2 / 🎉 5 — COMMENT.
Nothing here blocks. The handDrawn body fill is the one that wants an actual decision (fix it or write down why not), and the handDrawn e2e assertion is cheap insurance on a selector that depends on a dependency's internal path ordering. The classic/neo path, which is what most users will hit once #8148 lands, is well covered and I'd be comfortable with it as-is. Nice work — this is a clean piece of engineering with unusually good reasoning left behind for the next person.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## claude/redux-color-default-theme #8176 +/- ##
=================================================================
Coverage 79.74% 79.75%
=================================================================
Files 617 617
Lines 84426 84447 +21
Branches 15919 15923 +4
=================================================================
+ Hits 67327 67347 +20
- Misses 16077 16078 +1
Partials 1022 1022
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
… deps Review follow-ups on #8176. **handDrawn lane bodies took no palette fill** while classic and neo did, so the same theme tinted a whole lane under one look and only the title band under another. roughjs emits a hachure fill path for the body even though it was asked for `fill: 'none'` -- it just carries `stroke="none"` -- so the fix is the same rule the title band already had, and `laneRule` now covers both halves for both path rules rather than being spelled out once. **The handDrawn selectors had no rendering coverage.** `path:nth-of-type(2)` encodes roughjs's emission order, which no unit test can confirm, and the existing e2e assertions all query `rect.swimlane-*`, which does not exist under that look. Three tests now check computed strokes on the outline and hachure paths, including that `redux-dark-color` leaves the body's hachure unpainted -- the case that would otherwise hatch the whole lane. **CI was failing on an unrelated upstream release.** `scripts/tsc-check.ts` installed `type-fest: '*'`, which resolved to 5.x, whose `typed-array.d.ts` names `Float16Array`; the generated tsconfig asks for `lib: es2020`, so every PR in the repo failed on a package that changed nothing here. `type-fest` and `@types/d3` are now read from `packages/mermaid/package.json`, which is what the comment above them already said a downstream would have to match. `typescript` stays floating -- compiling against the newest TypeScript is the signal the check exists for, and it passes on 7.0.2. Also narrows the changeset's "explicit style still wins", which held for classic and neo only: under handDrawn the lane body gets no inline style at all, because `bodyOptions` passes `stroke`/`fill` as the `options` argument that `userNodeOverrides` assigns over the user's values. Pre-existing and left alone. Comments across the changed files are cut back to the non-obvious why.
|
Thanks — both 🟡 items were right, and both are fixed in 431141f. Details: handDrawn lane bodies now take the palette fill. You called it correctly: the asymmetry was an oversight, not a decision. I'd only checked that the body's hachure wasn't being wrongly painted and never came back to ask whether it should be. The fix is the rule you wrote, and I rendered it before committing — at roughjs's default
handDrawn now has rendering coverage. Three tests under a The changeset claim is narrowed. You're right that it held for classic and neo only, and right about the mechanism — On the 12-slot wrap you flagged under "no action needed": agreed it's inherent, but worth writing down that the collision can happen with only a handful of lanes if nested containers eat slots in between. If it bites in practice the fix is a separate slot space per container depth, not a bigger palette. Unrelated, but it was failing this PR: |
dbd5adf
into
claude/redux-color-default-theme
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>
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>
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>
* 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>
What
Swimlane lanes take a per-lane colour under
redux-colorandredux-dark-color, cycling every 12, as flowchart subgraph containers have since #8147. A lane is the participant a swimlane diagram is about, so it is the thing a categorical palette is for — but lanes rendered uniformly grey while the subgraphs drawn from the sameflowDbslots were already tinted.Nodes inside a lane stay uniform, and explicit
styleon a lane still wins.The slots were already there
swimlanesreuses flowchart's parser and DB, soflowDbhad been handing every lane acolorIndexsince #8147. Three things stopped it reaching the element.swimlane.jsnever stamped it. It is the one cluster shape that does not go throughclusters.js'srect, so it never picked up thestampColorSlotcall added there.The lane-border override outranked the palette.
swimlanes/styles.tscarried.swimlane.cluster rect { stroke: clusterBorder !important }. That!importantis needed — it exists to beat[data-look="neo"].cluster rect, which it ties with on specificity and which is emitted after it. But it would also beat every palette rule, and the failure is silent: lanes stay grey with nothing to say why. Scoped to:not([data-color-id])it does the one job it was added for, and nothing stamps that attribute outside the colour themes.The generic
.clusterpalette rules cannot be reused for a lane. A lane is two rectangles rather than one, and under handDrawn its body asks roughjs forfill: 'none'— which roughjs answers with a hachure path carryingstroke="none", not by omitting the path. The genericpathrule would have painted that invisible hachure across the whole lane and filled both outline paths solid. Lanes are excluded from the generic rules by:not(.swimlane)and take their own block:rect.swimlane-title,rect.swimlane-body.swimlane-* path:nth-of-type(2).swimlane-* path:first-of-typeThe handDrawn branch previously left its two rough groups unclassed, so nothing in CSS could tell a title band from a body; they now carry the same
swimlane-title/swimlane-bodyclasses the classic branch already set.The rules live in
flowchart/styles.tsbecause a swimlane reaches that stylesheet two ways — theswimlane-betadiagram, which wraps flowchart'sstylesexport, and a plainflowchartgivenlayout: swimlane. The second never loadsswimlanes/styles.ts; both are verified.Two bugs on the synthetic default lane
The lane that collects ungrouped nodes is synthesised by the layout rather than declared, so nothing upstream gave it the two properties every declared lane arrives with. Neither failed loudly.
look.swimlane.jsbranches onnode.look === 'handDrawn', so the lane rendered as a classic rect inside a handDrawn diagram — a pre-existing bug, independent of colour — and matched no[data-look="..."]palette rule.colorIndex.stampColorSlotfalls back tocolorIndex ?? 0, so it took slot 0 and came out the same colour as the first declared lane. It now takes the slot one past the highest already handed out.Slot ordering
flowDb's pre-order walk is left as-is rather than renumbered per lane. Nested subgraphs inside a lane consume slots too, so consecutive lanes are not necessarily consecutive slots — which is correct: a nested container should not share its parent lane's colour, and the palette is categorical, not ordered.redux-dark-colorcolours borders onlyThat theme ships
bkgColorArray = []. Its lanes get palette borders and keep the darkclusterBkgbody, which is what its flowchart subgraphs already do. Changing that is a palette decision, not this PR's — the guard is load-bearing rather than defensive, and every background-derived rule is dropped there rather than emitted with a missing value. Pinned by unit and e2e tests.Review follow-ups
Both 🟡 items from the review are fixed, and both 🟢 nits with them:
stroke="none". Rendered and inspected: at roughjs's defaultfillWeight: 4/hachureGap: 5.2the pale tint reads as a solid-ish wash, close to the classic look and legible behind the nodes.laneRulenow covers both halves for both path rules, which was the second nit.redux-dark-colorleaves the body's hachure unpainted. That was the gap:path:nth-of-type(2)encodes roughjs's emission order, no unit test can confirm it, and every existing assertion queriedrect.swimlane-*, which does not exist under that look.stylestill wins" is narrowed. It held for classic and neo only. Under handDrawn the lane body gets no inline style at all:bodyOptionspassesstroke/fillas theoptionsargument thatuserNodeOverridesassigns over the user's values, so a user's stroke on a handDrawn lane body was already discarded before this PR. Pre-existing and left alone — worth a follow-up issue.CI:
unit-testwas failing on an unrelated upstream releaseNot caused by this PR.
scripts/tsc-check.tsinstalledtype-fest: '*'into its throwaway consumer project; that resolved to 5.x, whosetyped-array.d.tsnamesFloat16Array, and the tsconfig the script generates asks forlib: es2020. Every PR in the repo fails on it —developlast ran green on 28 Aug, before the release.type-festand@types/d3are now read frompackages/mermaid/package.json, which is what the comment above them already said a downstream would have to match, and the helper throws rather than falling back so moving a dep betweendependenciesanddevDependenciescannot quietly restore the float.typescriptstays floating on purpose — compiling against the newest TypeScript is the signal this check exists for, and it passes on 7.0.2.This is a one-file infra fix that is independent of the rest of the PR. Happy to split it out against
developso it unblocks every other PR without waiting on this stack — say the word.Verification
.claude/skills/domus-improve-loop-2harness shims that require env vars from their runner, andusecase.docs.spec.ts, which passes in isolation (3.9s) and times out only under full-suite parallelism.pnpm test:check:tscpasses.styleoverrides,flowchart+layout: swimlane, and a diagram with nothemeand nolookat all — the case feat(themes)!: make redux-color the default theme and neo the default look #8148 creates.For the reviewer
Argos. Expect churn on every swimlanes fixture, since #8148 moves them to the coloured default. There are no committed Playwright baselines, so nothing to regenerate — but this wants a human pass rather than a bulk approve. Local screenshot comparison is not trustworthy here: two runs of the same spec on identical code gave different failure sets, so the behavioural assertions are what I gated on, and the e2e runs above used
--update-snapshotsto isolate them.Docs. Not touched, matching #8147, which documented nothing for the equivalent flowchart behaviour.
docs/syntax/swimlanes.mdhas no theming section to extend andconfig/theming.mdhas no per-diagram palette section; happy to add one to both in a follow-up if you'd rather the palette were documented once for subgraphs and lanes together.Merge order. Whoever merges should retarget this to
developafter #8148 lands, rather than merging into the stack branch.One conflict on the merge, in
colorThemeGate.spec.ts: both sides widened the same two lists — #8148 foldeder,requirementandtimelineinto the shared gate spec, this branch addedswimlanes. Both sets are kept, andswimlanesis inSLOT_STYLESHEETSas well asSTYLESHEETSso the slot-coverage pass runs against the lane rules too. That merge commit is--no-verifybecause lint-staged stashes to run, which is not safe mid-merge; eslint and prettier were run on the resolved file directly.