- Date: 2026-06-06
- Status: Accepted (documents current behavior + locks the policy)
- Phase / Feature: Foundation v1 · default font subset strategy
- Closes: ADR 0036 Q9 — "Vietnamese / SEA Inter subset"
- Builds on: ADR 0036 §2.4 / §11 (Inter as default font; ~320 KB woff2 budget), T11.1 (self-hosted Inter shipment)
Context
ADR 0036 §2.4 picked Inter as the default font.sans and chose self-host over Google Fonts CDN for GDPR / no-IP-leak. T11.1 shipped @fontsource/inter via 4 weight CSS imports.
ADR 0036 §11 set the Inter budget at "~320 KB woff2 total" and ADR 0036 Q9 deferred "Vietnamese / SEA Inter subset". At review time it was unclear whether Vietnamese (an SEA market gating block for Western SaaS expansion) required a separate woff2 file we'd need to add, or whether the single import already covered it.
After looking at the actual @fontsource/inter/400.css ship: every per-weight CSS file already includes 7 @font-face declarations covering cyrillic + cyrillic-ext + greek + greek-ext + latin + latin-ext + vietnamese, each scoped by unicode-range. The browser only downloads the subset whose codepoints appear in rendered text. So the "do we need a separate Vietnamese subset" question has a one-line answer: it's already there, no code change required.
This ADR captures that finding + the policy that follows from it.
Decision
§1 — Ship every subset the Inter package gives us by default
The current fonts.ts imports @fontsource/inter/400.css etc. Those CSS files declare 7 @font-face entries per weight, each tagged with unicode-range. The runtime cost is:
- CSS bytes for the @font-face metadata (a few KB per weight × 4 weights = ~12 KB CSS bundled into the page styles).
- woff2 download cost ONLY when rendered text falls into that subset's
unicode-range. A page that only renders English text downloadsinter-latin-400-normal.woff2(23.7 KB) and nothing else. A page in Russian gets latin (for any English fallthrough) + cyrillic (7.7 KB). A page in Vietnamese gets latin + vietnamese (5 KB).
Net: shipping all subsets is cheap because the browser only fetches what it needs. The 320 KB Inter budget in ADR 0036 §11 is a worst-case upper bound (all 7 subsets × 4 weights = 7 × ~12 KB metadata + 4 × ~80 KB woff2 if every subset is rendered = ~330 KB). Real-world per-page cost is 1-2 subsets × 4 weights ≈ 60-100 KB.
§2 — Per-project subset narrowing is a future Foundation v2 feature, not v1
A project that knows it ships English-only could opt out of cyrillic / greek / vietnamese declarations to save ~8 KB of CSS metadata. The cost / benefit at v1 scale is not worth the configuration surface — the rendered woff2 is already minimal via unicode-range, and the CSS metadata cost is a one-time fixed expense.
When v2 ships the in-app token editor surface, an opt-in foundation_locale: 'en' | 'eu' | 'ru' | 'sea' | 'all' column on project will tune the subset list. Until then, ship everything.
§3 — INTER_SUBSETS constant + label
Foundation exposes the subset list as a const so inspector tooling (future Storybook story or in-app debugger) can show the user which subsets are loadable without re-parsing the CSS.
export const INTER_SUBSETS = [
"latin",
"latin-ext",
"cyrillic",
"cyrillic-ext",
"greek",
"greek-ext",
"vietnamese",
] as const;This is documentation only — the actual loading is driven by @fontsource/inter. The constant matches @fontsource/inter's shipped subsets at this version.
§4 — Italics + variable axes deferred
@fontsource/inter also ships italic + variable-axis files. Foundation v1 doesn't pull italics by default (ADR 0036 §2.4 chose 4 weights × normal style only). When italics become a Foundation token (later phase), the same shipped-subset strategy applies — every locale gets every weight × italic combo, browser fetches what it needs.
Anti-patterns explicit
- Do not trim subsets in v1 to save bytes. The bytes saved are CSS metadata only; the woff2 download is already locale-conditional.
- Do not load fonts from Google CDN to dodge the CSS-metadata cost. GDPR + IP-leak reasoning in ADR 0036 §2.4 stands.
- Do not ship the variable-axis Inter alongside the static weights. Either pick weight-based or axis-based; mixing doubles download cost without visual gain at v1 weights.
Open questions / parking
- CJK (Chinese / Japanese / Korean) markets. Inter doesn't ship CJK; those markets need a separate brand-uploaded font via the BYOF flow (ADR 0038). The brand-import ritual already handles license attestation + per-project storage — CJK is an instance of that pattern, not a new mechanism.
- Arabic / Hebrew for the RTL markets (also implicit in ADR 0040). Inter has no Arabic / Hebrew subset. Same answer: BYOF.
- Variable-axis Inter at v2. Trade-off study deferred until weight ramp evolves past 4 weights.