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
5.2 KiB
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.
4. Google Fonts — Pairings and Featured Pairs (fonts.google.com)
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.