claude-skills/markdown-html/skills/design-system/references/typography_pairing.md
Claude 8d1f2dec97
feat(markdown-html): add v2.10.0 foundation — orchestrator + design-system
Operationalizes Shihipar's "Claude Code HTML output" essay (Medium, 2026):
markdown collapses past ~100 lines for agent-generated artifacts; HTML
restores density, clarity, shareability, and lightweight interaction.

This foundation PR ships 2 of 5 planned skills:

- markdown-html-orchestrator (context: fork): deterministic doctype
  classifier scoring filename hints + content signals across DOCUMENT /
  REVIEW / SLIDES; silent-routes above two-signal threshold; refuses
  inputs < 100 lines per Shihipar; refuses without design-system
  onboarding. 3 stdlib tools: doctype_classifier.py, route_explainer.py
  (the never-silently-chain enforcer + onboarding gate), and
  output_path_resolver.py (kebab slug + collision suffix).

- design-system: one-time onboarding wizard (10 questions for brand
  primary/accent HEX + heading + body Google Fonts + editorial/technical/
  minimal/playful style + default output dir + syntax theme + TOC
  behavior + optional logo/company). WCAG-AA-validated 12 CSS custom
  properties derived in HSL space; refuses to save if body-text or link
  contrast fails 4.5:1, or if the output dir is unwritable. Pattern
  lifted from research-ops/skills/clinical-research/scripts/ (onboard.py
  + config_loader.py shape) and marketing/landing/scripts/
  (brand_palette_validator.py WCAG + HSL math). Precedence: project >
  global > defaults; MARKDOWN_HTML_NO_CONFIG=1 bypasses.

Plus cs-markdown-html-orchestrator agent, 3 slash commands
(/cs:markdown-html router, /cs:grill-markdown-html 5-question grill,
/cs:design-system onboarding surface), 6 reference docs each citing 5-7
authoritative sources (Tufte, Shihipar, Bret Victor, Maggie Appleton,
Bartosz Ciechanowski, Amelia Wattenberger; WCAG 2.2, Ellen Lupton,
Adobe Spectrum, Sara Soueidan, Material Design 3), 1 JSON schema asset,
domain README + CLAUDE.md.

Converter sub-skills (md-document, md-review, md-slides) land in v2.10.1
follow-up PRs. All three will import design-system/scripts/config_loader.py
for shared brand tokens.

Repo-level updates: scripts/sync-codex-skills.py SKILL_DOMAINS adds
markdown-html → documentation category; .claude-plugin/marketplace.json
adds markdown-html-skills plugin entry (63 → 64 plugins, 16 → 17
domains); root CLAUDE.md gets nav-map row, structure-tree entry, and
v2.10.0 release-notes block.

Validation:
- check_plugin_json.py → OK
- sync-codex-skills.py --dry-run → 2 new symlinks, total 338 → 340
- skill_description_validator.py → PASS on both SKILL.md files
- skill_review_checklist_runner.py → 5/6 PASS (under-100-lines advisory
  fires; same as research-ops orchestrator)
- All 6 Python tools smoke-tested with --help and --sample
- End-to-end onboarding round-trip works (--defaults, --set, --reset)
- WCAG-fail hard refusal fires correctly on impossible color combos
- Classifier on the repo's own CLAUDE.md correctly returns
  needs-clarification (15 vs 13 — document signals tie with slides
  signals because of multiple --- HRs); orchestrator asks rather than
  silently chains.

Distinct from Anthropic's official Playground plugin (interactive
prompt-tuning controls with sliders/knobs/prompt-copy-back) and from
marketing/landing/ (landing-page generator from scratch).

https://claude.ai/code/session_01BK2KoQot1U7J5oSosrCQdc
2026-05-29 14:19:06 +00:00

5.2 KiB
Raw Permalink Blame History

Typography Pairing

Why this exists: The onboarding wizard offers 12 Google Fonts and asks the user to pick a heading + body pair. Most users don't have strong opinions on type. This document codifies the pairs that work without further thought, so the wizard can recommend confidently and the converters can render coherently.

Safe pairs

Pair Use for Reason
Inter + Inter Technical docs, dashboards Single family across heading/body — clean, neutral, OpenType-rich
Inter + Source Sans 3 Long-form reports Sans-on-sans pairing; Source Sans is more readable at body size
Source Serif 4 + Source Sans 3 Editorial / narrative Adobe's Source family — designed as a coherent system
Playfair Display + Lora Magazine-style Serif heading with personality; serif body that pairs
Merriweather + Open Sans Long-form reading Editorial serif + neutral sans body; oldest-and-safest pair
IBM Plex Sans + IBM Plex Sans Technical + brand Plex is designed for documentation; coherent across weights
JetBrains Mono + (Inter or Source Sans 3) Engineering notebooks Mono headings signal a coding/terminal context

Sources

1. Ellen Lupton — Thinking with Type (Princeton Architectural Press, 2010)

Foundational. The "stress, weight, and contrast" framework for pairing: the heading and body should share at least one of {stress angle, x-height, terminal style} and contrast in at least one of {weight, scale}. Every recommended pair above satisfies this.

2. Tim Brown — Combining Typefaces (Five Simple Steps, 2013)

The "concord / contrast / conflict" framework. Concord (same family) is always safe — hence the Inter+Inter and IBM Plex Sans+IBM Plex Sans pairs. Contrast is rewarding when done with intent (Playfair + Lora). Conflict is what users should avoid; the wizard's curated list rules out conflict pairs.

3. Erik Spiekermann — Stop Stealing Sheep & Find Out How Type Works (Adobe Press, 2013, 3rd ed.)

Argues that body type carries 95% of the visual weight in a document. The wizard prioritizes body font choice over heading font choice in the recommendation framing.

The 12 fonts in SAFE_FONTS are pulled from Google Fonts' own curated catalog, biased toward families with multiple weights and broad language coverage. All available under the SIL Open Font License — no licensing concerns.

5. IBM Design Language — Plex Family Documentation (ibm.com/design/language/typography/type-basics)

Documents the "designed as a system" pattern: Plex Sans, Serif, Mono share metrics and x-height, so any combination renders coherently. We surface Plex Sans for users who want IBM-style technical documents.

6. Adobe Fonts — Source Sans, Source Serif, Source Code (fonts.adobe.com/foundries/adobe-originals)

Same "designed as a system" idea: Source family was created by Adobe to be a coherent triple. We surface Source Sans 3 and Source Serif 4 (the current versions, with extended Cyrillic and Vietnamese coverage).

7. Marcin Wichary — The Hardest Working Font in Manhattan (figma.com/blog, 2023)

A case study on choosing Inter for the Figma marketing site. Reinforces Inter as a reasonable default for technical-yet-broad audiences.

What about display fonts, script fonts, decorative fonts?

Excluded from the wizard's options. Decorative fonts work for the first 200 words and exhaust the reader thereafter — they're a marketing-page choice, not a document choice. If the user wants a decorative heading, they can set typography.heading_font to any Google Font name manually after onboarding (the field accepts any string).

What about variable fonts?

Inter, Roboto, Source Sans 3, Source Serif 4, IBM Plex Sans, and JetBrains Mono are all available as variable fonts on Google Fonts. The converters use the wght@400;600 slice by default — sufficient for body + bold heading — to keep CDN payload small. Users who want a wider weight range can override the Google Fonts URL directly in the generated HTML.

Type scale

typography.scale_ratio (default 1.25 = major third) drives a modular scale: body = 1rem, h6 = 1rem × 1.25, h5 = 1rem × 1.25², etc. Defaults:

Ratio Name Effect
1.125 Major second Tight; good for dense reference docs
1.2 Minor third Standard for technical writing
1.25 Major third Default; balanced for long-form reading
1.333 Perfect fourth Editorial; pronounced hierarchy
1.5 Perfect fifth Magazine-style with bold headings

Each converter applies the scale based on this single ratio — no per-level overrides.

Applied to markdown-html

The converters emit a <link> to Google Fonts at document head and apply the typography choice via CSS:

:root {
  --md-font-heading: 'Source Serif 4', Georgia, serif;
  --md-font-body: 'Source Sans 3', system-ui, sans-serif;
  --md-scale: 1.25;
}
body { font-family: var(--md-font-body); }
h1, h2, h3, h4, h5, h6 { font-family: var(--md-font-heading); }

The system fallback in each font-family declaration means the document still reads well if Google Fonts is blocked.