Skip to content

fix(visimer): stop cold-load webfonts from clipping node labels (#3560) - #33

Merged
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync
Aug 18, 2026
Merged

fix(visimer): stop cold-load webfonts from clipping node labels (#3560)#33
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync

Conversation

@inkeep-oss-sync

Copy link
Copy Markdown

Copybara-translated 1 Inkeep OSS change. Rebase-merge this PR so the prepared commit lands directly on public main.

* fix(visimer): stop cold-load webfonts from clipping node labels

Mermaid measures label text against the fonts the document can use at the
moment it renders, then bakes those measurements into fixed-width
foreignObject boxes. On a first visit the webfont is still in flight behind
font-display: swap, so the boxes are sized for the fallback and the real text
overflows them once it swaps in. Every node label ends up clipped a few pixels
short: "Tests green?" loses its "?", "Ship it" reads "Ship i", "Fix bugs"
reads "Fix bug". Nothing in mermaid or in the browser re-measures, so the
diagram stays wrong for the whole session, and any reload hides it because the
font is cached by then. That is why it never shows up in development and shows
up for every first-time visitor.

MermaidCanvasView now looks at document.fonts after each render. If the
document's fonts are still loading it waits for them to settle and renders
again, which re-measures every label against the fonts the browser is actually
painting with. Checking after the render matters: mermaid's own measuring pass
is usually what first requests the webfont, so the pending load is only
visible by then. The wait arms only while fonts are pending, so a page whose
fonts are already available renders exactly once, as before, and it cannot
loop because the corrective render runs with the status already settled.

The fix is in the library rather than in apps/site because any embedder of
@visimer/dom that uses a webfont hits this, and the view is the only layer
that can act on it: it owns the render loop and the lastRenderedCode
short-circuit a correction has to bypass.

Measured with Playwright on a cold browser context against the dev site: 7 of
7 labels on the landing page and 3 of 4 on the playground were clipped before,
0 after, and the cold-load box geometry now matches the warm-cache geometry
exactly.

* fix(visimer): make the font-settled path explicit and pin the arm-once guard

Review follow-ups on the cold-load font fix.

The re-measure now routes both promise settlements through one named
handler instead of swallowing rejection in a bare catch. FontFaceSet.ready
is spec'd never to reject, but a partial polyfill could, and either way the
measurements are as final as they are going to get, so both paths should
re-render.

Adds a test for the arm-once guard. The existing tests all render once
before the fonts settle, so they pass even with the guard removed. The new
one renders twice while fonts are still loading and asserts a single
corrective render, which fails with 4 renders when the guard is dropped.

GitOrigin-RevId: 82fe42e2c9fa3470ab2c3be9afd5ca93ff6e1406
@inkeep-oss-sync
inkeep-oss-sync Bot merged commit 4f95143 into main Aug 18, 2026
@inkeep-oss-sync
inkeep-oss-sync Bot deleted the copybara/sync branch August 18, 2026 01:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant