Skip to content

Web fonts: self-hosted Literata + Inter (variable, latin subset)#13103

Draft
lokesh wants to merge 1 commit into
internetarchive:masterfrom
lokesh:design/feat/web-fonts
Draft

Web fonts: self-hosted Literata + Inter (variable, latin subset)#13103
lokesh wants to merge 1 commit into
internetarchive:masterfrom
lokesh:design/feat/web-fonts

Conversation

@lokesh

@lokesh lokesh commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Self-hosted web fonts: Literata (serif) + Inter (sans)

Second PR of the visual modernization series (independent of #13102 — can land in either order). Replaces the system Georgia/system-sans stacks with two self-hosted OFL variable fonts:

  • Literata — serif for book titles, banners, quotes (designed for Google Play Books long-form reading; a natural library brand fit). wght 400–700, variable opsz 12–36, latin subset → 55 KB.
  • Inter — sans for UI and body text. wght 400–700, latin subset → 50 KB.

Total font payload: 105 KB, fetched once and cached. Headings intentionally stay sans in this PR — the serif-heading flip ships with the type-scale PR so it can be reviewed together with the heading hierarchy.

How it works

  • static/css/tokens/font-faces.css (new, in the tokens bundle): @font-face with font-weight: 400 700, font-display: swap, latin unicode-range (non-latin scripts fall back to system stacks), plus metric-adjusted local fallbacks (Literata-fallback = Georgia, Inter-fallback = Arial with size-adjust/ascent/descent/line-gap overrides) so the swap barely moves anything.
  • Both woff2 preloaded in site/head.html with crossorigin (mandatory on font preloads — verified exactly one fetch per font, initiator=link, no double-fetch).
  • font-families.css: primitives lead with the new fonts; all 8 semantic tokens now reference var(--font-family-{sans|serif|mono}) instead of verbatim copies (fixes the --font-family-code Menlo divergence).
  • Fixes undefined var(--body-family) at legacy.css:1010,1020var(--font-family-body).
  • Subsetting pipeline (fonttools varLib.instancer + pyftsubset) documented in static/fonts/README.md. Italics not shipped; browsers synthesize an oblique.

Verified

  • npm run lint clean; npx jest 469/469; tokens.css still under budget.
  • CLS 0.010 on a throttled (1.5 Mbps / 80 ms) cold load of the work page — the metric-adjusted fallbacks do their job ("good" threshold is 0.1).
  • document.fonts shows both families loaded; work-page <h1> renders Literata, body renders Inter.

Before / after

Page Before (system fonts) After (Literata + Inter)
Work page before after
Search before after
Home before after

Rollback = revert the token/head.html changes (fonts are additive files).

🤖 Generated with Claude Code

- Ship latin-subset variable woff2 fonts (Literata wght 400-700 /
  opsz 12-36, ~55KB; Inter wght 400-700, ~50KB), built with fonttools
  varLib.instancer + pyftsubset (pipeline documented in
  static/fonts/README.md).
- New tokens/font-faces.css: @font-face with font-display: swap, latin
  unicode-range, and metric-adjusted local fallbacks (Georgia/Arial with
  size-adjust/ascent/descent overrides) - measured CLS 0.01 on a
  throttled cold load.
- Preload both fonts in site/head.html (crossorigin, required for font
  preloads; verified single fetch per font).
- font-families.css: primitives now lead with Inter/Literata; all
  semantic tokens reference the primitives instead of verbatim copies
  (fixes --font-family-code Menlo divergence). Headings stay sans for
  now - the serif flip lands with the type-scale PR.
- Fix undefined var(--body-family) in legacy.css (-> --font-family-body).

Non-latin scripts fall back to system stacks via unicode-range; italics
are synthesized.
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