fetchtype.tokens.json
{
  "typography": {
    "body":    { "lineHeight": 1.6, "fontSize": "1rem" },
    "heading": { "lineHeight": 1.1, "fontSize": "3rem" },
    "button":  { "fontSize": "0.9375rem" },
    "caption": { "fontSize": "0.8125rem" }
  },
  "color": {
    "light": { "text": "#111827", "background": "#ffffff" },
    "dark":  { "text": "#f9fafb", "background": "#111827" }
  },
  "hierarchy": {
    "scale": "major-third",
    "baseSize": "1rem"
  }
}

$ fetchtype validate --input fetchtype.tokens.json

  ✓ contrast — 15.4:1 light, 14.8:1 dark
  ✓ body line-height — 1.6 (minimum 1.5)
  ✓ button font-size — 15px (minimum 14px)
  ✓ caption font-size — 13px (minimum 11px)
  ✓ prose max-width — 68ch (maximum 75ch)
  ✓ heading sizes — h1 → h6 decreasing
  ✓ heading/body line-height ratio — valid
  ✓ spacing scale — monotonically increasing
  ✓ scale divergence — within ±10%
  ✓ dark mode — complete
  ✓ font fallbacks — present
  ✓ font payload — estimated 94KB (≤ 150KB)
  ✓ references — all aliases resolve, no cycles

  13 passed · 0 errors · 0 warnings
click anywhere to render

typography validation for design systems

Most of what your users do is read.

Typography is the largest functional surface of any interface. It determines whether your product feels considered or careless, whether content is readable or exhausting, whether your brand holds together across every screen or quietly drifts apart. Most teams never validate any of it.

Aa

Typography carries the emotional weight of the interface.

click a role to re-render the whole page

display

Type as brand presence.

Large headings, open spacing, serif warmth. Display typography carries the emotional weight of the product and makes it feel unmistakably designed. These modes are real: fetchtype build emits [data-mode="display"] CSS blocks so each context renders with its own typographic treatment.

the drift

Design intent doesn't survive the build.

Your designer specifies 1.5 line-height. Your developer ships 1.2 because the Tailwind default felt close enough. Your h2 is 24px in the dashboard and 20px in settings. Nobody catches it because there's nothing to catch it.

the numbers

These are testable failures, not taste.

Body text at 1.2 line-height fails WCAG 1.4.12. Text at #6b7280 on white gives 4.0:1 contrast — WCAG AA requires 4.5:1. A 12px button is too small for touch. Prose at 92 characters per line causes reading fatigue. All of these are specific, measurable, and fixable.

the cause

Typography involves expertise most teams don't have.

Type scales, vertical rhythm, font loading strategy, optical sizing, fallback metrics, WCAG thresholds — most developers learned typography by copying what looked right. Without a linter, without a test suite, without a CI check, it drifts.

the cost

The alternative is not doing it at all.

If typography is hard, teams skip it. They pick a safe font, set some arbitrary sizes, and move on. The result is products that feel generic, readability problems that surface in production, and accessibility gaps that appear during audits.

validate

A CI gate for typography.

13 rules check your tokens against WCAG contrast ratios, line-height minimums, font-size thresholds, prose width limits, heading hierarchy, spacing monotonicity, scale divergence, dark mode completeness, font payload size, and structural consistency. Exit code 1 on any error — it works as a build gate.

export

CSS, Tailwind, shadcn, and W3C tokens.

When validation passes, fetchtype exports to multiple formats. --format css for CSS custom properties. --format tailwind for a tailwind.config.ts partial. --format shadcn for shadcn CSS with HSL variables. --format w3c for W3C Design Tokens format. Dark mode, named themes, typography modes, configurable prefix.

review

Typography violations in the PR, not in production.

Add --github to any validate command. fetchtype emits inline annotations on the pull request diff and a step summary with the full diagnostic report. Typography problems show up where code gets reviewed.

what gets checked

13 rules. Specific thresholds, not opinions.

Contrast ≥ 4.5:1 (WCAG AA). Body line-height ≥ 1.5. Button font-size ≥ 14px. Caption/label font-size ≥ 11px. Prose width ≤ 75ch. Heading sizes decrease h1 → h6. Heading line-height < body line-height. Spacing scale monotonically increasing. Scale divergence within ±10%. Dark mode completeness across themes. Font fallback chains present. Font payload estimate ≤ 150KB. Token aliases resolve, no circular refs.

get started

Presets, modes, multiple export formats.

init writes a validated starter token file from a preset or a natural-language prompt. validate checks it against 13 accessibility and readability rules. build exports to CSS, Tailwind, shadcn, or W3C format. suggest recommends fonts for your context. preview renders a live report. That's the workflow.

install

pnpm add -D fetchtype

pnpm exec fetchtype init
pnpm exec fetchtype init --preset dashboard
pnpm exec fetchtype init --prompt "modern SaaS dashboard"
pnpm exec fetchtype validate --input fetchtype.tokens.json
pnpm exec fetchtype build --input fetchtype.tokens.json --format tailwind
pnpm exec fetchtype suggest --context interface
pnpm exec fetchtype preview -i fetchtype.tokens.json