Typography × craft
Where your type system and spacing grid quietly disagree
Set your heading to WCAG AA contrast. Now check it at `font-weight: 300`. The ratio drops below 3:1 on most colour systems.
## Contrast depends on stroke weight
A light-weight heading at 4.6:1 might fail perceptually because thin strokes disappear against the background. A bold heading at 3.2:1 might pass because thick strokes hold their own.
Check contrast at the lightest weight you ship. Light text on dark backgrounds loses perceived contrast faster at thin weights.
## Heading hierarchy is two systems at once
Typography sees headings as a scale. Accessibility sees headings as an outline: h1 then h2 then h3, never skipping a level. Skip from h2 to h4 and the screen reader outline breaks.
Don't use an h4 because it "looks right at that size." Style the h3 to the size you need:
```css
.section-subhead {
font-size: var(--text-lg);
font-weight: 500;
}
```
## The 4px grid and type sizing
Type sizes should produce 4px-aligned line heights. 16px with `line-height: 1.5` produces 24px (multiple of 4). 18px with `line-height: 1.5` produces 27px (not a multiple of 4).
Choose type sizes where `size x line-height` lands on your grid, or accept the drift and align only spacing between blocks. Both are valid. Mixing approaches creates subtle vertical rhythm breaks.
```css
:root {
--text-sm: 14px; /* × 1.43 = 20px ✓ */
--text-base: 16px; /* × 1.5 = 24px ✓ */
--text-lg: 20px; /* × 1.4 = 28px ✓ */
--text-xl: 24px; /* × 1.33 = 32px ✓ */
}
```
## Button typography
**Optical centring.** Text inside a button with equal padding looks left-shifted because letterforms have asymmetric visual weight. Add 2-4px more padding on the trailing side:
```css
.button {
padding-left: 16px;
padding-right: 20px;
}
```
**Icon + text alignment.** Icons sit on a grid, text sits on a baseline. `align-items: center` often looks wrong because the icon's visual centre doesn't match its geometric centre. Nudge with `transform: translateY(-1px)` on the icon.
**Font size in buttons.** Buttons inherit the component's font size unless explicitly set. A `text-sm` button inside a `text-lg` section looks broken. Set button font size explicitly.
**Letterspacing on uppercase buttons.** All-caps labels without tracking look cramped. Add `letter-spacing: 0.05em` to uppercase button text.
Change the type scale, recheck the spacing grid. Change the heading weight, recheck the contrast ratio.