Koder Design — Typography

mandatory

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) Longform reading mode (optional, optin per surface)

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 .woff2 from the consumer's own origin

    (e.g. kds.koder.dev/assets/fonts/, <product>.koder.dev/assets/fonts/).

  • @font-face declarations MUST point to a relative path on the same

    origin or to a Koder-owned *.koder.dev host. 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: swap is mandatory (system fallback shows immediately

    while 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 + every

    European Latin-script locale).

  • Extra scripts (cyrillic, greek, vietnamese, thai, …) ship as

    separate @font-face unicode-range declarations 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 (see tools/design-gen/assets/css/base.css for the canonical KDS implementation).

  • *lutter (koder_kit, mobile/desktop)*— bundle the same .woff2

    files under assets/fonts/ and declare them in pubspec.yaml fonts: blocks; expose KdsTextStyles.sans/mono/display/serif helpers matching the role table.

  • *S (koderwebkit)*— reexport the `kdsfont-*` values via

    KdsTokens.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, 20260511)* Inter + JetBrains Mono self-hosted, OFL.

    Spec captured here; KDS landing reflects the canonical chains.

  • *ave 2 (20262027)* *oder family*— ownerled Sans + Mono +

    Display, designed in-house with AI assistance, OFL with Reserved Font Name. Implemented in three phases:

    • *hase 1 (46 months)* Koder Display ships in `kdsfont-display`

      (headlines, hero, OG, splash). First visible ROI of the family.

    • *hase 2 (610 months)* Koder Mono ships in `kdsfont-mono`,

      substituindo JetBrains Mono. Koda-optimized (disambiguation, programming ligatures, tabular figures).

    • *hase 3 (1018 months)* Koder Sans ships in `kdsfont-sans`,

      substituindo Inter. Workhorse body face, variable wght + opsz axes.

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.kmd G1 (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.kmd
  • meta/brand/koder-design/backlog/pending/003-pivot-owner-led-typeface-family.kmd — Wave 2 pivot decision record
  • meta/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 proposal
  • projects/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 implementation
  • tools/design-gen/assets/fonts/ — hosted .woff2 files

Source: ../home/koder/dev/koder/meta/docs/stack/specs/fonts/typography.kmd