Skip to content

Diff loaded web font faces between the two sides - #92

Merged
tmashara merged 2 commits into
mainfrom
claude/issue-50-font-faces
Oct 29, 2026
Merged

tmashara merged 2 commits into
mainfrom
claude/issue-50-font-faces

Conversation

@tmashara

@tmashara tmashara commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

What

Each shot now records the web font faces it requested, and faces only one side has are reported:

  • compact row: fonts -"Inter 600" +"Roboto" under failed (union over the row's widths)
  • report.html: fonts … beside the failed-assets line
  • results.json: "fonts": { "added": [], "removed": ["Inter 600"] }. added is left only, removed is right only, same as text. Left out when both sides match.
  • findings:: fonts -"Inter 600" when it repeats on 2+ differing routes. Clean shots are skipped, so an all-clean run never lists font findings.

A face's name is family plus weight, style and stretch, with normal/400/100% dropped and bold mapped to 700. Surrounding quotes are stripped from the family, since new FontFace("My Face") reports "My Face" where the same @font-face reports My Face. A failed bold face now reads font X 700, which matches the docs' existing font Inter 700 example.

Why

A side that loads a different face (a dropped weight, a renamed family) is easy to miss in the pixels and hard to trace back from a dom property. Naming the face points to the cause directly. Fixes #50.

Scope and choices

  • Web fonts only (@font-face and FontFace). System fallbacks never appear in document.fonts, so the issue's +"Arial" can't be produced from there. That would need CDP CSS.getPlatformFontsForNode per text node. The docs say this.
  • loading counts as requested. Navigation uses domcontentloaded and the fonts.ready wait is capped at 5s, so a slow face can still be on the wire when it's read. Counting it as missing would give a flaky -.
  • A fonts diff alone does not make a shot un-clean. A failed asset does, because the fallback makes the pixels untrustworthy. A face one side loads that paints identical pixels is harmless, and the pixels decide. --rerun-failed behaviour is unchanged.

How tested

  • New guard-rail fixture fonts. Both sides use shared/blank.ttf, a 576-byte glyph-less TTF that draws nothing, and only the right also declares a 600 face for the h1. The visual test checks /fonts reports { added: [], removed: ["Blank 600"] } and the row -"Blank 600". It also checks that the shared Blank face and /broken-asset's failing Gone face (present on both sides) produce no fonts.
  • Unit tests for pixelFindings: a repeated face on differing routes is a finding, and on clean routes it isn't.
  • With src/ reverted, the visual test and the first unit test fail. They pass with the change.
  • blank.ttf was generated with fontTools (.notdef only, empty cmap), so it carries no licence. The fixture README says so.
  • npm run typecheck, lint, format:check, npm test (340 pass) and npm run test:visual (348 pass) all pass locally.

🤖 Generated with Claude Code

tmashara and others added 2 commits October 29, 2026 20:20
Each shot now records the @font-face/FontFace faces it requested (loaded,
or still loading at the 5s fonts.ready timeout). Faces only one side has
show under the row as fonts -"Inter 600" +"X", in report.html, in
results.json as fonts { added, removed }, and in findings when they repeat
on differing routes. System fallbacks are invisible to document.fonts and
are not covered.

Fixes #50

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@tmashara
tmashara merged commit c7ba9de into main Oct 29, 2026
3 checks passed
@tmashara
tmashara deleted the claude/issue-50-font-faces branch October 29, 2026 19:37
@tmashara tmashara mentioned this pull request Oct 30, 2026
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.

Diff loaded font faces between the two sides

1 participant