Skip to content

feat(web): self-host the Inter and JetBrains Mono brand fonts - #4341

Merged
bpamiri merged 4 commits into
developfrom
peter/web-self-host-fonts
Oct 5, 2026
Merged

bpamiri merged 4 commits into
developfrom
peter/web-self-host-fonts

Conversation

@bpamiri

@bpamiri bpamiri commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

web/packages/ui/src/styles/tokens.css sets --font-sans: 'Inter', … and --font-mono: 'JetBrains Mono', …, but nothing in web/ loaded either font. There was no @font-face, no font package, and no webfont link. So every site rendered in the fallback system fonts, unless a visitor happened to have Inter or JetBrains Mono installed. Inter and JetBrains Mono are the intended design, so this PR loads them.

Changes

  • web/packages/ui/src/assets/fonts/: the two font files and their licenses.
    • inter-latin-wght-normal.woff2 (48 KB) and jetbrains-mono-latin-wght-normal.woff2 (40 KB): the Latin subset as variable fonts covering weights 100 to 900. The sites use 400, 500, 600 and 700.
    • The files come from Fontsource 5.3.0, with their SIL Open Font License texts (Inter-OFL.txt, JetBrainsMono-OFL.txt).
  • web/packages/ui/src/styles/fonts.css (new): two @font-face rules using the family names the tokens already use ('Inter', 'JetBrains Mono'), font-display: swap, and the Latin unicode-range. Exported as @wheels-dev/ui/styles/fonts.css.
  • tokens.css gains @import './fonts.css';, so every site that loads the tokens gets the fonts with no per-site edit. That covers the landing, blog and packages layouts, and guides and API through Starlight, whose --sl-font / --sl-font-mono map to our tokens.

There's no third-party font request, and no new npm dependency. Fontsource's packages were tried first, but their exports map rewrites ./* to ./*.css, so CSS can't point at their woff2 files. Importing their full CSS instead would ship every subset.

Verification

  • pnpm build (all sites): landing 1, blog 357 and packages 8 pages built, and guides and API complete. Each site's dist/_astro/ contains both font files, and its CSS has @font-face{font-family:Inter;…font-display:swap;font-weight:100 900;src:url(/_astro/inter-latin-wght-normal.….woff2)…}.
  • Offline docs bundle: with WHEELS_DOCS_BASE=/wheels/guides/, the guides build emits src:url(/wheels/guides/_astro/inter-latin-wght-normal.….woff2), correctly prefixed for the bundle the framework serves.
  • No Google Fonts: nothing in any built page links to Google Fonts. The one match is example text on an API page.
  • prettier --check is clean.

Visual baselines

Every site's text now renders in Inter and JetBrains Mono, so every visual baseline is expected to change. After CI's visual-regression run, I'll check the diff artifact. If it shows only the font change, I'll refresh the baselines with the "Refresh visual baselines" workflow (all) on this branch.

🤖 Generated with Claude Code

bpamiri and others added 2 commits October 5, 2026 07:16
tokens.css names Inter and JetBrains Mono, but nothing loaded them, so
every site rendered in system fonts. Ship both as latin-subset variable
woff2 files in @wheels-dev/ui with font-display: swap, imported from
tokens.css so every site that uses the tokens gets them. No third-party
font request. OFL license texts sit next to the files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Oct 5, 2026
@bpamiri

bpamiri commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

Review @ 1ee218f: APPROVE on content (web/ui). Merge gated on PR CI, with the baseline refresh below

  • Why every page changes. tokens.css already named 'Inter' and 'JetBrains Mono' in --font-sans / --font-mono on develop, but nothing loaded them, so every page has been rendering the system fallbacks. Self-hosting makes the intended faces actually apply. That's why every visual baseline will move: an expected, font-only diff.
  • The files are what they claim to be. Both are genuine WOFF2 (wOF2 magic): Inter at 48,256 bytes and JetBrains Mono at 40,404 bytes, both latin-subset variable fonts.
  • Licences. Each ships with its SIL OFL 1.1 text and copyright line ("The Inter Project Authors", "The JetBrains Mono Project Authors"), which is what the OFL requires for redistribution.
  • The @font-face rules are right:
    • font-weight: 100 900 for a variable font;
    • font-display: swap, so text stays visible during load;
    • the standard latin unicode-range, so other scripts fall back to the system stack listed after the face;
    • URLs relative to fonts.css, so the bundler fingerprints them; the build of all five sites plus the WHEELS_DOCS_BASE bundle confirms they resolve.
  • @import './fonts.css'; is the first statement in tokens.css, as CSS requires. ./styles/fonts.css is also exported from the package for direct use.
  • No third-party request. Nothing under web/ references fonts.googleapis.com / gstatic or another font CDN. The only hit is an example string in the API docs for styleSheetLinkTag(), which is content, not a page dependency. There's no new npm dependency.

INFO (optional):

  • Only upright faces are shipped, so <em> / font-style: italic in Inter or JetBrains Mono will be synthesized (slanted) rather than true italics. If the guides use italics much, the two italic latin files (similar size) would fix it.
  • font-display: swap without a <link rel="preload"> for the Inter file means a brief fallback-then-swap on first visit. A preload in the shared head would shorten that. Neither blocks.

Baselines: once you've confirmed the visual diff is font-only and the bot refreshes "all", that bot commit runs no checks (GITHUB_TOKEN push, as on #4339). Push an empty commit afterwards so PR CI, including Visual regression, runs on the final head before arming.

@bpamiri

bpamiri commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

rev1-r3 review @ 1ee218f: code APPROVE (final sign-off after PR CI, including the visual-regression run and your font-only diff check)

  • The font files are exactly Fontsource's. inter-latin-wght-normal.woff2 (48,256 bytes) and jetbrains-mono-latin-wght-normal.woff2 (40,404 bytes) are valid WOFF2 files. Their SHA-256 hashes (3100e775…, 18be4527…) match @fontsource-variable/inter@5.3.0 and @fontsource-variable/jetbrains-mono@5.3.0 files/… from the npm registry exactly, so there's no re-encoding and no altered binary. The SIL OFL 1.1 texts ship next to them.
  • fonts.css. The two @font-face rules use the exact family names the tokens use ('Inter', 'JetBrains Mono'), font-weight: 100 900 (the variable range), font-display: swap, and Fontsource's Latin unicode-range. The src is a relative url('../assets/fonts/…'), so Vite fingerprints the files into /_astro/ and the base path applies, as your WHEELS_DOCS_BASE build shows.
  • tokens.css. @import './fonts.css'; is the first statement, which @import requires, so every consumer of the tokens gets the fonts with no per-site edit. The new ./styles/fonts.css export is listed next to the existing style exports.
  • Offline docs bundle. tools/build/scripts/build-docs.sh rsyncs each site's whole dist/ (including _astro/) into the bundle, so the fingerprinted fonts ship with it. The bundle's real base is /wheels-docs/guides/ (the body checked /wheels/guides/), but the mechanism is the same.
  • No third-party request and no new dependency. Only the two vendored binaries are added.

The visual baselines are expected to change, as the body says. If the refresh workflow pushes new baseline images to this branch, please send me the new head. I'll confirm the delta is baseline images only before the final.

github-actions Bot and others added 2 commits October 5, 2026 14:40
Manually triggered baseline refresh via
.github/workflows/refresh-visual-baselines.yml
targeting branch peter/web-self-host-fonts.

Run when an intentional content/layout change makes the visual-regression
check fail. The new PNG(s) under web/tests/visual-baselines/ are now the
expected rendering.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bpamiri

bpamiri commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

Re-ack @ 1926f62: APPROVE. The only changes since 1ee218f are the bot's refresh visual baseline(s) (all) (3c89de9), which touches the six baseline PNGs and nothing else, and an empty commit (tree identical) to trigger CI. I looked at the refreshed guides baseline: it renders in Inter with a clean layout, and no text overflows or is cut off, consistent with your font-only review. CI is now running on this head (13 pass, 6 pending, 3 skipping when checked). Merge once it's green, Visual regression included.

@bpamiri

bpamiri commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

rev1-r3 review @ 1926f62: APPROVE (final; PR CI checked)

  • The delta since my approval at 1ee218fbb is baselines only. 3c89de973 (github-actions, "refresh visual baseline(s) (all)") touches only the six web/tests/visual-baselines/*.png, and 1926f62aa is an empty CI trigger. I compared the old and new blog.png. The only differences are the typeface (Inter replacing the fallback sans) and the line reflow from its narrower metrics. The layout, colours and content are unchanged.
  • PR CI on this head: 19 pass, 3 skipped, 0 failing or pending, with Visual regression passing against the refreshed baselines.

@bpamiri
bpamiri merged commit e127df7 into develop Oct 5, 2026
22 checks passed
@bpamiri
bpamiri deleted the peter/web-self-host-fonts branch October 5, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant