How Diagram-Design's Typography Hierarchy Works: A Complete Guide to Font Roles, Sizes & Constraints
Diagram-Design enforces a strict four-family, six-role typography hierarchy that all diagrams must follow, with Instrument Serif for titles, Geist Sans for node names, and Geist Mono for technical labels—governed entirely by style-guide.md.
Diagram-Design's design system uses a small, fixed hierarchy of font families, sizes, and weights that governs every typographic decision in generated diagrams. This hierarchy is defined as a single source of truth in the style guide and enforced through automated linting and pre-output checklists.
The Four Font Families in Diagram-Design
All typography in Diagram-Design rests on four carefully chosen font families, imported from Google Fonts and injected into every generated HTML file:
| Family | Google Font Import | Primary Usage |
|---|---|---|
| Instrument Serif | family=Instrument+Serif |
Page titles (title) and editorial callouts (callout) |
| Geist (Sans) | family=Geist |
Human-readable node names (node-name) |
| Geist Mono | family=Geist+Mono |
Technical sublabels, ports, URLs, field types (sublabel, arrow-label, eyebrow) |
| Noto Sans KR | family=Noto+Sans+KR |
Korean fallback for all Hangul text |
The complete font stack is injected via a single <link> element. In skills/diagram-design/references/style-guide.md (lines 85-88), this stack is automatically added to every diagram's HTML output.
The Six Typographic Roles
Diagram-Design's typography hierarchy consists of six distinct roles that map to the four families. These roles are ordered by visual importance and have fixed, non-negotiable specifications:
| Role | Family | Size | Weight | Usage |
|---|---|---|---|---|
title |
Instrument Serif | 1.75 rem | 400 | Page-level H1 |
node-name |
Geist (sans) | 12 px | 600 | Human-readable node labels |
sublabel |
Geist Mono | 9 px | 400 | Ports, protocols, URLs, field types |
eyebrow |
Geist Mono | 7–8 px | 500 (tracked 0.18 em, uppercase) | Type tags, axis labels |
arrow-label |
Geist Mono | 8 px | 400 (tracked 0.06 em) | Arrow annotations |
callout |
Instrument Serif italic | 14 px | 400 | Editorial asides |
These definitions appear in the Typography table of style-guide.md (lines 74-81) and are referenced throughout SKILL.md (lines 93-100) as the "Typography (summary)" block.
Hierarchy Rules and "Load-Bearing" Constraints
Diagram-Design's typography hierarchy follows four strict rules that maintain visual consistency across all diagrams:
-
Title prominence: The
titlerole is the most prominent element and must use Instrument Serif—even when a brand's primary typography is all-sans, the title retains Serif for contrast. -
Node name weight: The
node-namerole always uses Geist Sans at weight 600, making it immediately distinguishable from technical content. -
Monospace restriction: Geist Mono is reserved exclusively for technical content—ports, commands, URLs—and never used as a generic "developer aesthetic" font for body text (see
SKILL.md, lines 104-105). -
Callout distinction: The
calloutrole uses italic Instrument Serif to set editorial text apart from functional diagram elements.
The hierarchy is strictly enforced: you cannot substitute families between roles. The sole exception is Korean text, which must fall back to Noto Sans KR because the primary families lack Hangul glyphs (style-guide.md, lines 89-96).
Legibility Constraints and Minimum Sizes
Diagram-Design imposes hard constraints to ensure legibility across scripts:
- Minimum Hangul size: 12 px—any Korean label smaller than this is unreadable and must be enlarged or removed (
style-guide.md, lines 152-158) - No arbitrary scaling: The sizes listed above are the only permitted sizes; the design system does not allow custom scaling
- Mono isolation: Geist Mono never replaces Geist Sans for
node-nameunder any circumstance
How Typography Is Applied at Render Time
When Diagram-Design generates an HTML diagram, it executes a three-step typographic pipeline:
- Font injection: The Google Font link containing all four families is inserted into the document head
- Role mapping:
<text>elements receivefont-family,font-size, andfont-weightattributes matching their assigned role exactly - Linter validation: The
lint-skin.pyscript verifies that no forbidden families appear and all size/weight combinations match the hierarchy
Template files like template-full.html contain placeholder <text> elements that are replaced with role-appropriate values during rendering. Any violation—such as using Serif for a node-name—causes the linter to fail.
Practical Implementation Example
The following SVG demonstrates each typographic role with exact specifications from style-guide.md:
<!-- Font stack injected automatically by Diagram-Design -->
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&family=Noto+Sans+KR:wght@400;500;600&display=swap" rel="stylesheet">
<svg width="400" height="200" role="img" aria-labelledby="diag-title diag-desc">
<title id="diag-title">Sample Diagram</title>
<desc id="diag-desc">Illustrates the typography hierarchy</desc>
<!-- Title: Instrument Serif, 1.75rem, weight 400 -->
<text x="200" y="30" text-anchor="middle"
font-family="'Instrument Serif', serif"
font-size="1.75rem"
font-weight="400">
Architecture Overview
</text>
<!-- Node name: Geist Sans, 12px, weight 600 -->
<text x="100" y="100" text-anchor="middle"
font-family="'Geist', sans-serif"
font-size="12px"
font-weight="600">
Service A
</text>
<!-- Sublabel: Geist Mono, 9px, weight 400 -->
<text x="100" y="115" text-anchor="middle"
font-family="'Geist Mono', monospace"
font-size="9px"
font-weight="400">
http://api.example.com
</text>
<!-- Eyebrow: Geist Mono, 7px, weight 500, tracked, uppercase -->
<text x="100" y="80" text-anchor="middle"
font-family="'Geist Mono', monospace"
font-size="7px"
font-weight="500"
text-transform="uppercase"
letter-spacing="0.18em">
API
</text>
<!-- Arrow label: Geist Mono, 8px, weight 400, tracked -->
<text x="200" y="150" text-anchor="middle"
font-family="'Geist Mono', monospace"
font-size="8px"
font-weight="400"
letter-spacing="0.06em">
Calls
</text>
<!-- Callout: Instrument Serif italic, 14px, weight 400 -->
<text x="300" y="80" text-anchor="start"
font-family="'Instrument Serif', serif"
font-style="italic"
font-size="14px"
font-weight="400">
Note: This service is deprecated.
</text>
</svg>
For Korean labels, swap to: font-family="'Geist', 'Noto Sans KR', sans-serif' with a minimum 12px size.
Key Files Governing Typography
| File | Purpose |
|---|---|
skills/diagram-design/references/style-guide.md |
Central token table; defines the typography role hierarchy, font stacks, Korean fallback, and constraints |
skills/diagram-design/SKILL.md |
High-level documentation with pre-output checklist enforcing the hierarchy (§9) |
scripts/lint-skin.py |
Linter validating font family, size, and weight compliance |
assets/template-full.html |
HTML template with placeholder <text> elements for role-based replacement |
scripts/verify-geometry.py |
Geometry verifier checking width budgets for Unicode characters including CJK |
Summary
- Four families (Instrument Serif, Geist Sans, Geist Mono, Noto Sans KR) support six roles with fixed sizes and weights
- No family substitution: roles are locked to their assigned fonts; only Korean text triggers the Noto Sans KR fallback
- 12 px minimum for Hangul ensures legibility; smaller Korean labels are rejected
- Automated enforcement via
lint-skin.pyand the pre-output checklist inSKILL.mdprevents hierarchy violations - Source of truth: all specifications live in
skills/diagram-design/references/style-guide.md
Frequently Asked Questions
Can I use a different font family for the title if my brand requires sans-serif only?
No. According to SKILL.md, the title role must use Instrument Serif even when a brand's primary typography is all-sans. This creates necessary visual contrast and is non-negotiable in the hierarchy.
What happens if I use Geist Mono for node names instead of Geist Sans?
The lint-skin.py linter will reject the diagram. SKILL.md (lines 104-105) explicitly prohibits using monospace fonts for node-name, reserving Geist Mono exclusively for technical content like ports, URLs, and field types.
How does Diagram-Design handle Korean text in diagrams?
Korean text automatically falls back to Noto Sans KR at a minimum 12 px size. The primary font families (Instrument Serif, Geist) lack Hangul glyphs, so style-guide.md (lines 89-96) mandates this fallback. Any Korean label smaller than 12 px is considered unreadable and must be enlarged or removed.
Where can I modify the typography hierarchy?
The hierarchy is not user-configurable. All role definitions live in skills/diagram-design/references/style-guide.md and are enforced by the skill's architecture. To request changes, you would need to fork the cathrynlavery/diagram-design repository and modify the source files directly.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →