This skill should be used when the user is selecting typefaces, defining font sizes, creating typographic scales, pairing fonts, setting line height or line length, building heading hierarchies, configuring web fonts, or implementing responsive typography. Covers modular type scales (Major Third, Perfect Fourth), the 2-3 font family limit, Bringhurst's line length rule (45-75 characters), and micro-typography.
npx claudepluginhub oborchers/fractional-cto --plugin visual-design-principlesThis skill uses the workspace's default tool permissions.
Typography accounts for 95% of web design (Oliver Reichenstein, iA). A correct type system makes every page look intentional. A broken one makes every page look amateur — regardless of color, imagery, or layout.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Typography accounts for 95% of web design (Oliver Reichenstein, iA). A correct type system makes every page look intentional. A broken one makes every page look amateur — regardless of color, imagery, or layout.
Limit every project to 2-3 font families maximum. More than three creates visual noise and increases page weight.
Pairing strategy — contrast, not similarity:
| Pairing Type | Heading | Body | Example |
|---|---|---|---|
| Serif + Sans-serif | Playfair Display | Inter | Editorial, marketing |
| Geometric + Humanist | Poppins | Source Sans Pro | SaaS, dashboards |
| Monospace + Sans-serif | JetBrains Mono | Inter | Developer tools |
| Single family | Inter (bold) | Inter (regular) | Minimal, utilitarian |
The single-family approach (one typeface, varied weights) is the safest choice for applications. It guarantees harmony and reduces load time.
Define font sizes using a mathematical ratio, not arbitrary values. Multiply the base size by the ratio for each step.
Base size: 16px (1rem). Never go below 16px for body text on screens.
| Scale Name | Ratio | Sizes from 16px base |
|---|---|---|
| Minor Third | 1.200 | 16 / 19.2 / 23.0 / 27.6 / 33.2 |
| Major Third | 1.250 | 16 / 20 / 25 / 31.3 / 39.1 |
| Perfect Fourth | 1.333 | 16 / 21.3 / 28.4 / 37.9 / 50.5 |
| Golden Ratio | 1.618 | 16 / 25.9 / 41.9 / 67.8 / — |
Selection guide: Use Minor Third for dense UIs (dashboards, admin panels). Use Major Third or Perfect Fourth for content-heavy pages. Use Golden Ratio sparingly — only for dramatic editorial layouts.
| Context | Line Height | Reason |
|---|---|---|
| Body text | 1.4–1.6 | Optimal readability per Bringhurst |
| Headings | 1.1–1.3 | Tighter to maintain visual weight |
| ALL CAPS text | 1.0–1.2 | Caps are uniform height; needs less leading |
| Small/caption text | 1.5–1.7 | Smaller text needs proportionally more space |
Add letter-spacing: 0.05em–0.1em to ALL CAPS text to compensate for reduced legibility.
Optimal: 45–75 characters per line. The ideal is 66 characters (Bringhurst). Lines shorter than 45 characters cause excessive eye jumps. Lines longer than 75 characters cause readers to lose their place.
Implementation: Set max-width: 65ch on text containers. The ch unit is based on the width of the "0" character, making it font-aware.
clamp()Replace media-query-based font scaling with clamp() for fluid sizing between breakpoints.
Formula: clamp(min, preferred, max) where preferred uses viewport units.
| Element | clamp() Value |
|---|---|
| Body | clamp(1rem, 0.95rem + 0.25vw, 1.125rem) |
| H3 | clamp(1.25rem, 1rem + 1vw, 1.75rem) |
| H2 | clamp(1.5rem, 1.2rem + 1.5vw, 2.25rem) |
| H1 | clamp(2rem, 1.5rem + 2vw, 3.5rem) |
| Display | clamp(2.5rem, 2rem + 3vw, 5rem) |
Optimize font delivery to prevent layout shift and invisible text:
font-display: swap — show fallback text immediately, swap when font loads<link rel="preload" as="font" type="font/woff2" crossorigin>| Anti-Pattern | Why It Fails | Fix |
|---|---|---|
| More than 3 font families | Visual noise, slow load times | Limit to 2-3; prefer single-family with weight variation |
| No type scale (arbitrary sizes) | Inconsistent hierarchy; sizes feel random | Adopt a modular scale ratio |
| Body text below 16px | Unreadable on mobile; fails accessibility | Minimum 16px (1rem) for body |
| Thin font weights (100-200) at small sizes | Disappears on low-DPI screens | Use 400+ for body, 300 minimum at 18px+ |
| Line length exceeding 75 characters | Readers lose their place between lines | Set max-width: 65ch on text containers |
No font-display strategy | Flash of invisible text (FOIT) | Always set font-display: swap |
Working implementations in examples/:
examples/type-scale-and-pairing.md — Complete modular type scale in CSS custom properties, Tailwind configuration, and React heading component with responsive sizingWhen reviewing or building typographic systems:
max-width: 65ch or equivalent)clamp() instead of breakpoint-only scalingfont-display: swap<head>