Pular para o conteúdo

Koder Design — Typography

fonts specs/fonts/typography.kmd

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.

Corpo da especificação

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)

RoleWave 1 implementationWave 2 targetWave 2 ETAPurpose
sansInterKoder Sans2027UI body, controls, navigation, default everywhere
monoJetBrains MonoKoder Mono2027Code, terminal output, numeric tables, IDs, hashes
displayInter @ 700/800Koder Display2026-Q4Headlines, hero text, posters, OG images
serifui-serif, Georgia, serif(unchanged)Long-form reading mode (optional, opt-in 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 Koder 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 commissioned-foundry plan in projects/koder-stack#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 — self-hosted-first

  • 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

RoleRequired weightsNotes
sans400 (body), 500 (medium), 600 (UI emphasis), 700 (heading)Variable font preferred when available
mono400 (code), 700 (highlight)Variable font preferred
display700, 800Wave 2 may ship dedicated display face
serif400, 700Reader-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 en-US + pt-BR + 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

IDTest
T1grep -rn 'fonts.googleapis|fonts.gstatic|jsdelivr.*font|unpkg.*font' <consumer>/ → empty
T2Every font-family in CSS/Dart/templ resolves to a role in the table above or its fallback chain
T3@font-face blocks include font-display: swap
T4Hero/critical pages emit <link rel="preload" as="font" ...> for the body face
T5Each shipped .woff2 ≤ 50 KB per subset/weight (or carries an exception comment)
T6Variable face used when the upstream provides one (Inter VF / JetBrains Mono VF)
T7AAA contrast preserved across the fallback chain (system fonts pass tools/a11y-test/)

Cross-surface integration

  • Web / landing / templ pages@font-face + --kds-font-* vars at :root (see tools/design-gen/assets/css/base.css for the canonical KDS implementation).
  • Flutter (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.
  • JS (koder_web_kit) — re-export the --kds-font-* values via KdsTokens.font.sans/mono/... for consumers that bypass CSS.
  • CLI/TUI — terminals use the OS default mono; no spec is needed beyond "rely on the user's terminal font."

Roadmap

  • Wave 1 (now, 2026-05-11): Inter + JetBrains Mono self-hosted, OFL. Spec captured here; KDS landing reflects the canonical chains.

  • Wave 2 (2026-2027): Koder family — owner-led Sans + Mono + Display, designed in-house with AI assistance, OFL with Reserved Font Name. Implemented in three phases:

    • Phase 1 (4-6 months): Koder Display ships in --kds-font-display (headlines, hero, OG, splash). First visible ROI of the family.
    • Phase 2 (6-10 months): Koder Mono ships in --kds-font-mono, substituindo JetBrains Mono. Koda-optimized (disambiguation, programming ligatures, tabular figures).
    • Phase 3 (10-18 months): Koder Sans ships in --kds-font-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 commissioned-foundry 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 marketplace 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