$npx -y skills add dembrandt/dembrandt-skills --skill modular-scale-typographyTypography feels cohesive and intentional when font sizes follow a modular scale — a ratio-based sequence where every size is mathematically related to the others. Use when defining type scales, setting up design tokens, reviewing font size choices, or when typography feels incon
| 1 | # Modular Scale Typography |
| 2 | |
| 3 | Typography feels cohesive when all font sizes are related to each other through a single mathematical ratio. Without a scale, sizes get picked arbitrarily and the result feels visually noisy — headings that don't contrast enough, body text too close in size to captions, labels that blend into content. |
| 4 | |
| 5 | ## What Is a Modular Scale |
| 6 | |
| 7 | A modular scale starts from a **base size** and multiplies or divides by a **ratio** to generate every size in the system. |
| 8 | |
| 9 | ``` |
| 10 | size(n) = base × ratio^n |
| 11 | ``` |
| 12 | |
| 13 | Every size is thus a deliberate step away from the base — not a guess. |
| 14 | |
| 15 | ## Choosing a Ratio |
| 16 | |
| 17 | | Ratio | Name | Feel | Good for | |
| 18 | |---|---|---|---| |
| 19 | | 1.067 | Minor Second | Very tight | Dense data UIs, dashboards | |
| 20 | | 1.125 | Major Second | Subtle | Long-form reading, editorial | |
| 21 | | 1.200 | Minor Third | Balanced | Most UI applications | |
| 22 | | 1.250 | Major Third | Clear hierarchy | Marketing, landing pages | |
| 23 | | 1.333 | Perfect Fourth | Strong contrast | Display, hero sections | |
| 24 | | 1.414 | Augmented Fourth | Dramatic | Portfolios, branding | |
| 25 | | 1.500 | Perfect Fifth | Very dramatic | Use sparingly | |
| 26 | |
| 27 | **Default recommendation:** `1.25` (Major Third) — enough contrast between steps to feel intentional without being theatrical. |
| 28 | |
| 29 | ## Generating a Scale |
| 30 | |
| 31 | > **Recover an existing scale, don't reverse-engineer it by hand (dembrandt engine, optional).** If a brand already has type on the web, `get_typography` returns the real font sizes, weights, and line-heights computed off the live DOM — infer the underlying ratio from those, then regularise it with the method below, instead of guessing which sizes were intended. See [`extract-design`](../extract-design/SKILL.md). |
| 32 | |
| 33 | Starting from `base = 16px`, ratio `1.25`: |
| 34 | |
| 35 | | Step | Formula | Value | Rounded | Role | |
| 36 | |---|---|---|---|---| |
| 37 | | -2 | 16 ÷ 1.25² | 10.24px | 10px | Caption, label-xs | |
| 38 | | -1 | 16 ÷ 1.25 | 12.80px | 13px | Label, small | |
| 39 | | 0 | 16 | 16px | 16px | Body (base) | |
| 40 | | +1 | 16 × 1.25 | 20px | 20px | Body-lg, lead | |
| 41 | | +2 | 16 × 1.25² | 25px | 25px | H4 | |
| 42 | | +3 | 16 × 1.25³ | 31.25px | 31px | H3 | |
| 43 | | +4 | 16 × 1.25⁴ | 39.06px | 39px | H2 | |
| 44 | | +5 | 16 × 1.25⁵ | 48.83px | 49px | H1 | |
| 45 | | +6 | 16 × 1.25⁶ | 61.04px | 61px | Display | |
| 46 | |
| 47 | Round to whole pixels or rem — the ratio provides the intent, exact pixel rounding is fine. |
| 48 | |
| 49 | ## Design Tokens (CSS custom properties) |
| 50 | |
| 51 | ```css |
| 52 | :root { |
| 53 | --text-xs: 0.625rem; /* 10px — caption */ |
| 54 | --text-sm: 0.813rem; /* 13px — label */ |
| 55 | --text-base: 1rem; /* 16px — body */ |
| 56 | --text-lg: 1.25rem; /* 20px — lead */ |
| 57 | --text-xl: 1.563rem; /* 25px — h4 */ |
| 58 | --text-2xl: 1.938rem; /* 31px — h3 */ |
| 59 | --text-3xl: 2.438rem; /* 39px — h2 */ |
| 60 | --text-4xl: 3.063rem; /* 49px — h1 */ |
| 61 | --text-5xl: 3.813rem; /* 61px — display */ |
| 62 | } |
| 63 | ``` |
| 64 | |
| 65 | ## Tailwind Config |
| 66 | |
| 67 | ```js |
| 68 | // tailwind.config.js |
| 69 | fontSize: { |
| 70 | 'xs': ['0.625rem', { lineHeight: '1rem' }], |
| 71 | 'sm': ['0.813rem', { lineHeight: '1.25rem' }], |
| 72 | 'base': ['1rem', { lineHeight: '1.5rem' }], |
| 73 | 'lg': ['1.25rem', { lineHeight: '1.75rem' }], |
| 74 | 'xl': ['1.563rem', { lineHeight: '2rem' }], |
| 75 | '2xl': ['1.938rem', { lineHeight: '2.25rem' }], |
| 76 | '3xl': ['2.438rem', { lineHeight: '2.5rem' }], |
| 77 | '4xl': ['3.063rem', { lineHeight: '1.1' }], |
| 78 | '5xl': ['3.813rem', { lineHeight: '1' }], |
| 79 | } |
| 80 | ``` |
| 81 | |
| 82 | ## Why This Makes Typography Feel Cohesive |
| 83 | |
| 84 | Without a scale, designers and developers pick sizes by eye or habit (`14px`, `16px`, `18px`, `24px`, `32px`, `48px`). These feel subtly wrong because the intervals are uneven — the jump from 14→16 is small, 32→48 is large, and there is no underlying logic tying them together. The eye senses the inconsistency even when the viewer cann |