feat: resolve rem/em font units to px before canvas measurement#109
Open
okaris wants to merge 2 commits intochenglou:mainfrom
Open
feat: resolve rem/em font units to px before canvas measurement#109okaris wants to merge 2 commits intochenglou:mainfrom
okaris wants to merge 2 commits intochenglou:mainfrom
Conversation
Canvas ctx.font silently ignores relative CSS units like rem and em (spec limitation — WHATWG HTML #1682). Callers passing CSS-derived font strings such as "0.9rem Inter" get no measurement at all; canvas keeps the previous font and returns wrong widths. This adds resolveFont() which rewrites relative units to absolute px using getComputedStyle(document.documentElement).fontSize before the string reaches canvas. Results are cached at module level so the DOM read happens once. Environments without a document (OffscreenCanvas, Node) fall back to 16px. - getFontMeasurementState() now resolves the font before setting ctx.font - parseFontSize() resolves before extracting the px value - clearMeasurementCaches() clears the resolution cache - resolveFont() is exported for direct use by consumers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Canvas
ctx.fontsilently ignores relative CSS units (rem,em). When callers pass CSS-derived font strings like"0.9rem Inter", canvas keeps its previous font and returns wrong widths. This is a spec limitation — canvas can exist without a document root, so relative units have no reference point (WHATWG HTML #1682).In practice, anyone using Tailwind or a CSS framework with custom
text-smsizes (e.g.--text-sm: 0.9reminstead of the default0.875rem) gets silently wrong measurements. The text measures at whatever font was previously set on the canvas context, not at the intended size.Related: #107 (canvas doesn't account for certain CSS properties).
Solution
Add
resolveFont()inmeasurement.tsthat rewrites relative units to absolute px before the string reaches canvas:Resolution uses
getComputedStyle(document.documentElement).fontSize— the same level of DOM access thatgetEmojiCorrection()already does. Results are cached at module level so the DOM read happens once. Environments without a document (OffscreenCanvas, Node.js) fall back to 16px.Changes
resolveFont(font)— exported, cached, handles rem/em → pxgetFontMeasurementState()— resolves font beforectx.font =and cache keyingparseFontSize()— resolves before extracting the px valueclearMeasurementCaches()— clears the resolution cache + cached root font sizeTest plan
bun test src/layout.test.ts— 85 tests pass (5 new)