You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
packages/viewer/src/fonts.css ships five @fontsource faces (Roboto 400/500/700, JetBrains Mono 400/500) at the default font-display: swap with no metric-matched fallback face. On a cold rw serve load the browser lays out text in the system fallback, then reflows the whole page when the web fonts arrive — a layout-shift (CLS) hit for every first visit in standalone mode.
Add @font-face fallback faces with size-adjust / ascent-override / descent-override / line-gap-override tuned so the fallback occupies the same space as the real font. The swap then causes zero reflow.
Why
Independent CLS / SEO / UX win for the whole page in standalone mode — the article currently visibly jumps on first load, comments or not.
It also removes the dominant trigger behind the inline-comment deep-link positioning machinery added in fix(viewer): keep deep-linked inline comment thread aligned to its highlight #570 (fix(viewer): keep deep-linked inline comment thread aligned to its highlight). That fix re-measures the thread's pin after a web-font reflow strands it; with the reflow eliminated, the cold-load deepLinkSettleSeq re-measure becomes a rare belt-and-suspenders path.
Scope / approach
Fallback faces for Roboto (Arial/system fallback) and JetBrains Mono (Menlo/Consolas/monospace fallback) — code blocks reflow too.
Generate metrics with a tool (Fontaine / @capsizecss/metrics + createFontStack, à la next/font) or hand-tune from published metrics. ~6–12 lines of build-time CSS, one file.
Standalone-only: embed.ts does not import fonts.css (the host owns fonts in Backstage), so this cannot regress the embedded/Backstage path.
Out of scope (related, separate)
Markdown images render as bare <img> with no width/height (crates/rw-renderer/src/html.rs), so a late image above a deep-linked highlight still reflows on cold load regardless of fonts. Emitting intrinsic image dimensions from the renderer would close that residual case and let more of the deep-link re-measure path retire — but it's a larger renderer change; tracking separately.
Summary
packages/viewer/src/fonts.cssships five@fontsourcefaces (Roboto 400/500/700, JetBrains Mono 400/500) at the defaultfont-display: swapwith no metric-matched fallback face. On a coldrw serveload the browser lays out text in the system fallback, then reflows the whole page when the web fonts arrive — a layout-shift (CLS) hit for every first visit in standalone mode.Add
@font-facefallback faces withsize-adjust/ascent-override/descent-override/line-gap-overridetuned so the fallback occupies the same space as the real font. The swap then causes zero reflow.Why
fix(viewer): keep deep-linked inline comment thread aligned to its highlight). That fix re-measures the thread's pin after a web-font reflow strands it; with the reflow eliminated, the cold-loaddeepLinkSettleSeqre-measure becomes a rare belt-and-suspenders path.Scope / approach
@capsizecss/metrics+createFontStack, à lanext/font) or hand-tune from published metrics. ~6–12 lines of build-time CSS, one file.embed.tsdoes not importfonts.css(the host owns fonts in Backstage), so this cannot regress the embedded/Backstage path.Out of scope (related, separate)
Markdown images render as bare
<img>with nowidth/height(crates/rw-renderer/src/html.rs), so a late image above a deep-linked highlight still reflows on cold load regardless of fonts. Emitting intrinsic image dimensions from the renderer would close that residual case and let more of the deep-link re-measure path retire — but it's a larger renderer change; tracking separately.References
packages/viewer/src/fonts.csssize-adjust), Fontaine,next/font🤖 Generated with Claude Code