Koder Design — Typography
Closed vocabulary of typeface roles, canonical CSS variables, hosting rules, weights, scale, fallback chain, and tests for typography in every Koder surface (web/landing, Flutter mobile/desktop, CLI/TUI). Self-hosted-first: webfonts ship as `.woff2` from the consumer's own origin; Google Fonts / jsDelivr / unpkg are forbidden.
Koder Design — Typography
Canonical typography for the Koder Stack and every Koder product UI. The set of typeface roles is closed; the implementations behind each role evolve (Wave 1 reuses Inter + JetBrains Mono; Wave 2 introduces Koder Display; Wave 3 ships proprietary Koder Sans + Koder Mono). References to "the body font" or "the code font" in any other spec or component MUST resolve to a role defined here, not to a specific typeface name.
Roles (closed vocabulary)
| Role | Wave 1 implementation | Wave 2 target | Wave 2 ETA | Purpose |
|---|---|---|---|---|
| *ans* | Inter | Koder Sans | 2027 | UI body, controls, navigation, default everywhere |
| *ono* | JetBrains Mono | Koder Mono | 2027 | Code, terminal output, numeric tables, IDs, hashes |
| *isplay* | Inter @ 700/800 | Koder Display | 2026-Q4 | Headlines, hero text, posters, OG images |
| *erif* | ui-serif, Georgia, serif |
(unchanged) | — | Long |
Wave 1 reuses upstream open-source typefaces under SIL Open Font License 1.1; no Koder-owned typeface ships yet. Wave 2 introduces the full *oder family*(Sans + Mono + Display), designed in-house by the owner with AI assistance, shipped under OFL with Reserved Font Name. See meta/brand/koder-design#003-#030 for the roadmap; supersedes the earlier commissionedfoundry plan in `projects/koderstack#128`.
Canonical CSS variables (KDS-prefixed)
Every Koder web surface declares these once at :root and resolves every font-family through them. Hard-coding the typeface name in component CSS is forbidden — components consume the variable so the Wave-2/3 swap is a single declaration change.
:root {
--kds-font-sans: 'Inter', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--kds-font-mono: 'JetBrains Mono', ui-monospace, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace;
--kds-font-display: var(--kds-font-sans);
--kds-font-serif: ui-serif, Georgia, "Times New Roman", serif;
}
body, button, input, select, textarea { font-family: var(--kds-font-sans); }
code, kbd, pre, samp, .mono { font-family: var(--kds-font-mono); }
h1, h2, h3, .display { font-family: var(--kds-font-display); }The legacy --kdr-* variables in tools/design-gen/assets/css/base.css remain for non-typeface tokens until the broader rename sweep; for typography always prefer --kds-font-*.
Hosting — selfhostedfirst
- Webfont files MUST ship as
.woff2from the consumer's own origin(e.g.
kds.koder.dev/assets/fonts/,<product>.koder.dev/assets/fonts/). @font-facedeclarations MUST point to a relative path on the sameorigin or to a Koder-owned
*.koder.devhost. The following hosts are forbidden anywhere a Koder product loads:fonts.googleapis.com,fonts.gstatic.com,cdn.jsdelivr.net,unpkg.com,cdnjs.cloudflare.com, and every other third-party font CDN.font-display: swapis mandatory (system fallback shows immediatelywhile the webfont streams in — mitigates FOIT).
- For pages that critically depend on the webfont (hero / above-fold
text), the page shell MUST emit
<link rel="preload" as="font" type="font/woff2" crossorigin>.
Rationale: privacy (policies/analytics.kmd forbids third-party beacons; Google Fonts is one), reliability (no third-party outage in the critical path), and policies/self-hosted-first.kmd G1.
Weights
| Role | Required weights | Notes |
|---|---|---|
| sans | 400 (body), 500 (medium), 600 (UI emphasis), 700 (heading) | Variable font preferred when available |
| mono | 400 (code), 700 (highlight) | Variable font preferred |
| display | 700, 800 | Wave 2 may ship dedicated display face |
| serif | 400, 700 | Reader-mode only |
A surface MAY skip an unused weight; it MUST NOT introduce weights outside this table without amending this spec.
Variable fonts
Variable single-file .woff2 (e.g. Inter VF, JetBrains Mono VF) is preferred over multiple static weight files: smaller total payload, single HTTP request, finer weight control. When a variable file is shipped, declare it with font-weight: 100 900; in @font-face and drop the per-weight static files from the same family.
Subsetting
- Default subset:
latin+latin-ext(covers enUS + ptBR + everyEuropean Latin-script locale).
- Extra scripts (
cyrillic,greek,vietnamese,thai, …) ship asseparate
@font-face unicode-rangedeclarations only when a product ships a locale that requires them; otherwise they MUST NOT ship. - Subset payload budget: * 50 KB per weight*per script. Larger
payloads require an exception comment in the consumer's CSS.
Type scale
Use these px values for font-size; map to CSS custom properties when the surface has more than ~5 sizes.
12 (caption) · 14 (small) · 16 (body, base) · 18 (lead)
24 (h4) · 32 (h3) · 48 (h2) · 64 (h1 / display)Line-heights:
- 1.5 — body, paragraphs
- 1.4 — controls, dense UI
- 1.2 — headlines, display
- 1.6 — long-form reading
Fallback chain — required
Every font-family declaration MUST end in a system fallback chain so a webfont failure degrades gracefully. The chains above (--kds-font-*) are the canonical chains; do not invent new ones per component.
Forbidden anywhere: Comic Sans MS, Papyrus, Times New Roman as a primary face (Times is OK only inside the serif fallback chain).
Tests
| ID | Test |
|---|---|
| T1 | grep -rn 'fonts.googleapis|fonts.gstatic|jsdelivr.*font|unpkg.*font' <consumer>/ → empty |
| T2 | Every font-family in CSSDarttempl resolves to a role in the table above or its fallback chain |
| T3 | @font-face blocks include font-display: swap |
| T4 | Hero/critical pages emit <link rel="preload" as="font" ...> for the body face |
| T5 | Each shipped .woff2 ≤ 50 KB per subset/weight (or carries an exception comment) |
| T6 | Variable face used when the upstream provides one (Inter VF / JetBrains Mono VF) |
| T7 | AAA contrast preserved across the fallback chain (system fonts pass tools/a11y-test/) |
Cross-surface integration
- *eb / landing / templ pages*—
@font-face+--kds-font-*vars at:root(seetools/design-gen/assets/css/base.cssfor the canonical KDS implementation). - *lutter (koder_kit, mobile/desktop)*— bundle the same
.woff2files under
assets/fonts/and declare them inpubspec.yaml fonts:blocks; exposeKdsTextStyles.sans/mono/display/serifhelpers matching the role table. - *S (koderwebkit)*— re
export the `kdsfont-*` values viaKdsTokens.font.sans/mono/...for consumers that bypass CSS. - *LI/TUI*— terminals use the OS default mono; no spec is needed
beyond "rely on the user's terminal font."
Roadmap
- *ave 1 (now, 2026
0511)* Inter + JetBrains Mono self-hosted, OFL.Spec captured here; KDS landing reflects the canonical chains.
- *ave 2 (2026
2027)* *oder family*— ownerled Sans + Mono +Display, designed in-house with AI assistance, OFL with Reserved Font Name. Implemented in three phases:
- *hase 1 (4
6 months)* Koder Display ships in `kdsfont-display`(headlines, hero, OG, splash). First visible ROI of the family.
- *hase 2 (6
10 months)* Koder Mono ships in `kdsfont-mono`,substituindo JetBrains Mono. Koda-optimized (disambiguation, programming ligatures, tabular figures).
- *hase 3 (10
18 months)* Koder Sans ships in `kdsfont-sans`,substituindo Inter. Workhorse body face, variable wght + opsz axes.
- *hase 1 (4
Tracked in meta/brand/koder-design/backlog/{pending,in-progress,done}/ tickets #003#030. Supersedes the earlier commissionedfoundry plan in projects/koder-stack#128.
References
meta/docs/stack/policies/self-hosted-first.kmdG1 (no third-party origin in critical path)meta/docs/stack/policies/analytics.kmd(no Google Fonts beacon)meta/docs/stack/rfcs/design-RFC-001-koder-design-system.kmdmeta/brand/koder-design/backlog/pending/003-pivot-owner-led-typeface-family.kmd— Wave 2 pivot decision recordmeta/brand/koder-design/backlog/pending/— full Wave 2 roadmap (#003-#030)projects/koder-stack/backlog/done/128-koder-display-commissioned-typeface-wave-2.md— superseded Wave 2 commission proposalprojects/koder-stack/backlog/pending/098-foundry-fonts-typography-store-scoping.md— the *arketplace*of fonts (distinct from this spec, which is the Koder Stack's own typography)tools/design-gen/assets/css/base.css— canonical KDS web implementationtools/design-gen/assets/fonts/— hosted.woff2files