Ui component typography
Skill jpoindexter/typography-skills/skills/ui-component-typography
Typography skills for AI coding agents — 20 focused skills + a /type router, extracted from 10 canonical typography books and reconciled against WCAG 2.2 and current browser behavior.
npx -y skills add jpoindexter/typography-skills --skill ui-component-typographyAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 10 days oldThe repository was created 10 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when setting type inside interface components — buttons, labels, inputs, form errors, navigation, badges, tooltips, tables, dense dashboards. Also use when numbers jitter in a table, labels wrap unpredictably, UI text is illegible at small sizes, or a component's text breaks in another language.
SKILL.md
10.1 KB, as published. Nobody here has run it
UI component typography
Interface text is scanned and acted on, not read continuously. Different failure modes from body copy, and different rules.
1. When to invoke
- Typography inside buttons, labels, inputs, nav, tables, badges, tooltips.
- Numbers misaligning or jittering in tables or dashboards.
- Labels wrapping, truncating, or overflowing.
- Dense product UI where vertical space is contested.
Do not invoke for running prose (body-text-and-reading) or editorial layout
(editorial-web-typography).
2. Required context
- The densest screen the component appears on.
- Longest realistic string for every label — in every supported language.
- Whether text is user-generated (unbounded length).
- Whether numbers are compared vertically (tables, ledgers) or read singly.
- Touch vs. pointer, and the minimum touch target.
- Whether the platform provides Dynamic Type / OS text scaling.
3. Invariant principles
- UI text is scanned, not read. Optimise for recognition speed and unambiguity, not reading comfort over long passages.
- Tabular figures are mandatory wherever numbers align vertically. Proportional figures have varying widths, so digits won't form columns. Lining + tabular is the combination for tables. (Lupton, Hochuli, Santa Maria)
1 l Iand0 Omust be distinguishable in any interface showing codes, IDs, quantities, or currency.- Every label must survive its longest translation. German and Finnish routinely run 30–40% longer than English.
- A label is not a placeholder. Placeholder-as-label disappears on focus and usually fails contrast.
- Text must survive the WCAG 1.4.12 spacing override — dense components are where this breaks first.
- Truncation is a last resort and must be recoverable — never truncate the only copy of information without a way to see it.
4. Context-dependent heuristics
Sizing. UI text usually runs smaller than body copy because it's scanned, not
read — but small type raises the cost of a poor typeface. At 12–14px, aperture and
counter behavior matter more than at 16px. Prefer a face with open apertures and a
generous x-height for UI (see typeface-anatomy). Don't drop below ~12px for
anything carrying meaning; never for anything a user must act on.
Line-height in components. Tighter than body text — a single-line label doesn't need 1.5. Around 1.2–1.35 for single-line UI strings, but note this is component line-height, not the paragraph rule. Any multi-line text inside a component reverts to body-text logic. WCAG 1.4.12 still requires the layout to survive a 1.5 override.
Figure styles — the four combinations.
| Tabular (uniform width) | Proportional (varying width) | |
|---|---|---|
| Lining (cap height, uniform) | Tables, dashboards, prices, data | Buttons, single values in UI |
| Oldstyle (ascenders/descenders) | Rare | Running prose, editorial text |
.table-numeric { font-variant-numeric: tabular-nums lining-nums; }
.prose { font-variant-numeric: oldstyle-nums proportional-nums; }
Full tag/property lookup in ../type/references/opentype-features.md.
Lining figures are usually cap height or slightly less and are normally set on a constant (en) body, which is what makes them tabular-capable (Hochuli).
Multiplexed typefaces solve the hover-reflow problem. In most families a heavier weight is also wider, so bolding a nav item or a tab on hover shifts everything after it. A multiplexed face is designed so every weight occupies exactly the same horizontal space — "a word set in a light weight will take up the exact same horizontal space as a word set in a heavy weight" (Stocks §31). Nothing moves.
Where a multiplexed face isn't available, the alternatives in order:
- A variable font's
GRAD(grade) axis — changes apparent weight without changing width. Seevariable-fonts. - Reserve the bold width up front (render the bold string invisibly to size the
container, or set
min-width). - Signal the state with something other than weight — colour, an underline, a background.
Never let a hover state reflow a navigation bar.
Labels and truncation. Design for the longest string first. Options, in order of preference: allow wrapping; allow the container to grow; shorten the copy; truncate with the full value available on hover/focus/detail. Truncating by default because the design assumed English is a localisation bug.
Alignment in tables. Numbers right-aligned (or decimal-aligned) so magnitudes compare; text left-aligned; headers matching their column's data alignment.
Case. Use text-transform: uppercase rather than typing capitals, so screen
readers, search, and translation receive the real string. Add tracking whenever you
do — see microtypography.
5. Failure patterns
| Pattern | Cause | Fix |
|---|---|---|
| Numbers jitter/shift between rows | Proportional figures | font-variant-numeric: tabular-nums |
| Prices don't line up | Not decimal-aligned | Right- or decimal-align |
| Button text wraps in German | Sized to English | Test longest locale; allow growth |
| Label vanishes when typing | Placeholder-as-label | Real <label> |
0 vs O confusion in codes | Ambiguous digits | Face with disambiguated digits |
| Dense table unreadable at 200% zoom | Fixed row heights | min-height, intrinsic sizing |
| Layout collapses under 1.4.12 override | Fixed heights in dense components | Let containers grow |
| Screen reader spells out a nav item | Literal capitals typed in source | text-transform |
| Error message truncated | Fixed-height container | Let it grow; errors are critical |
| Text illegible at 11px | Too small, closed apertures | Raise size; choose an open-aperture face |
6. Evaluation procedure
- Render each component with its longest realistic string in the longest supported locale.
- Render with the shortest string too — components often break empty.
- In any table: confirm
tabular-nums, and confirm numeric columns align. - Check
1 l I/0 Oin the shipping face at the shipping size. - Apply the WCAG 1.4.12 override; confirm nothing clips or overlaps.
- Zoom to 200%; confirm no loss of content or function.
- Check contrast for every state — placeholder, disabled, hover, error.
- Confirm touch targets meet minimum size independent of text size.
- Check any truncation has a recovery path.
7. Output format
Component: <name>
Size/line-height: <n>/<n> — rationale: <scan vs read, density>
Figures: <lining|oldstyle> + <tabular|proportional> — because <alignment need>
Longest string tested: "<string>" (<locale>) — <fits|wraps|truncates>
Digit disambiguation: <1lI ok?> <0O ok?>
1.4.12 override: <pass/fail> · 200% zoom: <pass/fail>
Contrast by state: default <n> · placeholder <n> · disabled <n> · error <n>
Truncation: <none|recoverable via …>
8. Examples
Dashboard table where "the numbers look wobbly".
The face defaults to proportional figures, so
1is narrower than8and the digits don't form columns — the wobble is real, not perceptual. Fix isfont-variant-numeric: tabular-nums lining-numson numeric cells plus right-alignment so magnitudes compare down the column. Verified the face actually ships atnumset (some don't, in which case the fallback is a monospace numeric face for those cells).
Primary button breaking in German.
"Save changes" → "Änderungen speichern" is 22 characters vs 12. The button has a fixed width sized to English, so the German string truncates. Fix in order: remove the fixed width and let the button size to content with a
min-width; allow two-line wrapping with a tighter component line-height; only then consider shorter copy. Verified at 200% zoom and under the 1.4.12 override — with the fixed width removed, both pass.
9. Counterexamples
- ❌ "Use the placeholder as the label to save space." — Disappears on input, usually fails contrast, and breaks screen-reader association.
- ❌ "Set the button width so the layout stays consistent." — Guarantees a localisation failure.
- ❌ "Body line-height is 1.5, so use 1.5 everywhere." — Single-line UI labels don't need paragraph leading; but the layout must still survive a 1.5 override.
- ❌ "Numbers are fine, the font looks aligned." — Verify
tabular-numsexplicitly; proportional figures can look aligned in one sample and drift in another. - ❌ "Truncate with an ellipsis, users can guess." — Truncation without recovery destroys information.
- ❌ Typing
SUBMITin the source to get uppercase. — Usetext-transform. - ❌ "10px is fine for secondary labels." — If it carries meaning, it must be legible.
10. Source citations
- Lupton, Thinking with Type 3e — the four figure styles: lining numerals uniform in height and resembling capitals; oldstyle/nonlining resembling lowercase with ascenders and descenders; tabular with uniform widths "ideal for tables and charts"; proportional with varying widths; lining and nonlining can each be tabular or proportional.
- Hochuli, Detail in Typography — lining figures usually cap height or slightly less, normally set on a constant (en) body so they can serve as tabular figures.
- Santa Maria, On Web Typography — tabular figures keep numbers lined up in vertical columns, making tables and spreadsheets easier to scan.
- Rutter, Web Typography — OpenType figure features; numerals and data tables.
- Lupton (ed.), Type on Screen — typography and data display; designing data tables; typography in interfaces.
- Brown, Flexible Typesetting — calibrated type for scanning complex information: tabular data, navigation, infographics, code.
- WCAG 2.2 §1.4.3, §1.4.4, §1.4.12.
- MDN —
font-variant-numeric.