Typography is a derivation, not a series of choices

Most typography feels hard because it is being done as forty independent decisions. It is closer to three decisions and a set of consequences. This manual is organised around what derives from what.

The one rule that reduces the others

Set your body size, your largest size, and your number of levels. Everything else — the ratio, every intermediate size, line heights, container widths, the gaps between elements — falls out of those three numbers plus a small number of relationships. When a project feels like it is starting from zero, it is because those three numbers were never fixed.

The seven dials

Personality in type does not come from the typeface. It comes from where these seven sit. Change the typeface and hold the dials, you get a variant. Hold the typeface and change the dials, you get a different design.

  1. Ratio — how far apart the levels sit
  2. Hierarchy axis — whether size, weight, colour, case, space or family carries the structure
  3. Leading — line height relative to size
  4. Measure — the width of the reading line
  5. Tracking — letter spacing, mainly at the extremes
  6. Density — the ratio of ink to whitespace
  7. Case and alignment
How to use this manual

Every rule here is a default you break knowingly, not a law. The value of stating them as numbers is that a number can be argued with. A feeling cannot.

01

Text roles

A text role is a distinct job a piece of text does for the reader, named by that job. Not by its size, not by where it appears. "Caption", "input label", "section heading".

A role is a locked bundle of seven properties: size, weight, line height, tracking, colour, maximum measure, and the space below it. If you have only assigned a size, you have not yet made a role. The bundle is the unit you style once and reuse everywhere.

The full inventory

Around forty roles exist across all of interface and editorial work. A working system needs ten to fourteen. The collapsing is the design work.

Structural

  • Display / hero
  • Page title
  • Section heading
  • Subsection heading
  • Eyebrow / kicker
  • Deck / standfirst
  • Nav item
  • Tab label
  • Breadcrumb
  • Contents entry

Reading

  • Body
  • Lead paragraph
  • List item
  • Blockquote
  • Pull quote
  • Term & definition
  • Caption
  • Credit / byline
  • Timestamp
  • Footnote

Interface

  • Button label
  • Input label
  • Placeholder
  • Helper text
  • Validation message
  • Inline link
  • Standalone link
  • Tooltip
  • Badge / tag
  • Status label
  • Menu item
  • Table header
  • Table cell
  • Tabular figure
  • Metric value
  • Empty state
  • Toast
  • Modal title
  • Settings group label
  • Keyboard shortcut
  • Inline code
  • Code block

Meta

  • Legal / fine print
  • Copyright
  • Counts & metadata
  • Skeleton text

Collapsing them

  • Create a role when a new job appears. Never when a new screen appears.
  • If two roles always render identically, they are one role. Merge and rename to the broader job.
  • Use size for level changes, weight for peer-level distinction. Two things at the same level should share a size and differ in weight or colour.
  • Pick one or two signals per role. Size plus weight is enough. Size plus weight plus colour plus caps plus tracking is noise.
  • Don't bind semantic heading level to visual size. Keep h1h6 for document structure and let a separate visual role decide how it looks.
  • Don't use placeholder text as a label. It disappears the moment someone types.
  • Don't let a role exist that has never been used twice.

A ten-role starter set

If you need somewhere to begin, this covers most products before any specialisation:

displaytitleheadingsubheadingbodybody-smlabelcaptioncodemetric

02

The scale

A modular scale multiplies a base size by a ratio, repeatedly. The formula is trivial. The mistake is choosing the ratio first.

size = base × r

Pick your endpoints instead — body size, largest display size, and how many steps you need — then solve for the ratio:

r = (max ÷ base) ^ (1 ÷ steps)

Choosing r blind is how you end up with a 182px heading you will never use. A ratio of 1.5 compounds faster than intuition expects.

Build one

Live scale generatorSet the endpoints. The ratio is computed.
Hand-tune both ends

Pure geometry produces 12.8px and 13.9px at the bottom, which are indistinguishable, and absurd numbers at the top. Let the small end go linear — 12, 14, 16 — and the large end stay geometric. Round everything to values a human would type. Switch the control above to raw geometry to see why this matters.

Ratios and what they feel like

Ratio is the loudest single dial. This is the whole usable range.
RatioNameCharacterUse for
1.125Major secondDense, technical. Levels barely separate, so weight and colour must carry hierarchy.Dashboards, data tools
1.2Minor thirdNeutral workhorse. Calm, unremarkable, safe.General UI
1.25Major thirdBalanced, quietly confident.Product default
1.333Perfect fourthClear voice, obvious levels.Editorial, marketing
1.414Augmented fourthAssertive. Starts eating vertical space.Landing pages
1.5Perfect fifthBold, magazine-like. Survives four or five steps at most.Editorial display
1.618GoldenDramatic. Needs three levels maximum.Fashion, poster, brutalist

Rules for the relative gaps

  • Skip steps in use. A 1.25 scale with every step used reads as mush. Use every other step and you get an effective 1.56 between roles while keeping the in-between sizes available for edge cases.
  • Cap at five or six levels. If you need more, the content structure is the problem, not the scale.
  • Shift the ratio by viewport, not the roles. 1.2 on mobile, 1.333 on desktop, same content, same role names. This is what clamp() is actually for.
  • Give metric numbers their own scale. A KPI at 40px above a 12px label is a legitimate 3.3× jump inside a 1.2 system. It works because the number is data, not a heading.
  • Don't set two adjacent steps against each other. Close-but-different reads as an error, not a hierarchy.
  • Don't go below 12px for anything a user is required to read, and hold a 15px floor for body in dense contexts.
Adjacent versus skippedSame 1.2 scale, both sides
Adjacent steps
Quarterly summary
The two sizes are close enough that the eye reads a rendering fault rather than a relationship.
One step skipped
Quarterly summary
The gap is now unambiguous. Nothing else changed — same family, same weight, same colour.
03

Weight

Weight is the second hierarchy axis and the one that does the work when size cannot. It behaves differently from size: it is not perceptually linear, and small increments are invisible.

  • 200 units minimum between weights. 400 against 500 looks like a rendering bug. 400 against 600, or 400 against 700, reads as intentional.
  • One body weight across the whole system. Variation lives in the roles around body, never inside it.
  • Bold for emphasis inside a paragraph, italic for titles and voice. Not the reverse.
  • Reversed text — light on dark — needs slightly less weight and slightly more leading than the same setting on white.
  • Don't use weights below 400 under 14px. Thin strokes disappear at small optical sizes and on low-density displays.
  • Don't adopt a family that only ships two weights. You will want 400, 500 or 600, and 700 at minimum.
Weight separationIBM Plex Sans, all at 19px
400 → 500+100reads as an accident
Available capacity   Available capacity
400 → 600+200the working minimum
Available capacity   Available capacity
300 → 700+400editorial contrast
Available capacity   Available capacity
04

Leading

Line height is inverse to size and proportional to measure and x-height. Large text needs less leading because the eye does not need help finding the start of the next line. A wide column needs more, because the return sweep is longer and easier to lose.

Working targets. Set as a unitless multiplier so it scales with the size.
RoleSizeLeadingNote
Fine print12–13px1.4–1.5Small text needs air to stay legible
Body16–18px1.5–1.61.6 for serif, 1.5 for sans
Lead paragraph19–22px1.45–1.5
Subheading22–28px1.25–1.3
Heading30–44px1.15–1.2
Display48px+1.0–1.1Below 1.0 only in deliberate lockups
Table cell13–14px1.3–1.4Density constraint overrides comfort
Leading against measureMove both. Notice they trade off.

A wide column with tight leading is the most common readability failure in interface work. The eye finishes a line, sweeps back to the left edge, and has to guess which line comes next. Widen the measure and the return sweep grows longer; the leading has to grow with it or the guess starts failing. Narrow the measure and generous leading starts to look loose and disconnected instead of comfortable. The two dials are bound together, which is why neither has a single correct value.

  • Set leading unitless — line-height:1.55 — never in px, so it survives every size change.
  • Large x-height faces need more leading than small x-height faces at the same nominal size.
  • Don't chase strict baseline grids in component-based work. Vertical rhythm is a good idea that rarely survives real components. It is not a hill worth dying on.
05

Tracking

Letter spacing corrects for what the type designer could not know: your size. Most faces are drawn for a specific optical size, and you are almost always using them at a different one.

  • Positive below 13px — roughly +1% to +3%. Small type crowds.
  • Negative above 40px — roughly −1% to −3.5%. Large type gaps.
  • All caps always needs +5% to +10%. Capitals were never designed to sit together.
  • Use an optical-size axis instead if the variable font offers one. It is a better correction than tracking.
  • Don't track lowercase body text in either direction. It is already correct.
  • Don't leave the middle of the scale alone-adjusted. Between 14px and 36px, do nothing.
Tracking at the extremesIBM Plex Sans
Untracked
Return sweep
Storage utilisation
Corrected
Return sweep
Storage utilisation
06

Measure and containers

Container width is a consequence of measure, and measure is set per role, not per size. Size is only the input that converts one to the other.

width ≈ targetch × font-size × 0.5

The 0.5 is the average character advance for most sans faces — use 0.45 for condensed, 0.55 for wide, 0.6 for monospace. So 66 characters lands at roughly 528px at 16px, 594px at 18px, and 660px at 20px. This is why "600px is the ideal column" is only true at one size.

Targets by role

Set these in ch or em on the text itself, never in px on a wrapper.
RoleMeasureWhy
Display15–25chMeant to be seen as a shape, not read line by line
Page title20–35chTwo or three lines maximum
Section heading30–45chHeadlines lose impact past ~40 characters
Deck / standfirst45–60chNarrower than body so it reads as a bridge
Body60–75ch66 is the classic target
Blockquote50–60chInset from body to signal the change of voice
Caption, helper40–60chShort text, short line
Table cell30–45chThe tightest measure you will set legibly
Tooltip30–45chBeyond this it should be a panel
Button label12–20chHard cap. Longer means it is not a button.
Legal / fine print60–80chSmall size permits a longer line
Measure rulerGreen band marks 45–75 characters
20ch≈ 66ch120ch

Measure is the width of the reading line expressed in characters, and it is the single most reliable readability control you have. It is stated in characters rather than pixels because the eye is counting glyphs, not measuring distance. A line that is comfortable at sixteen pixels becomes a strain at twenty-four in the same container, and a column that works for a serif at seventy characters can feel loose for a sans at the same count.

The units that matter

Set max-width in ch or em, padding in em for anything that scales, and line height unitless. Then a single size change propagates correctly instead of breaking three other decisions.

07

Grid, margins and padding

Decouple the grid from the measure

The grid column is a layout constraint. Measure is a reading constraint. Trying to make one serve both is where most layouts go wrong. Let the grid place the block, then give the text its own max-width inside that cell. A twelve-column article layout can span eight columns for its background and images while the paragraphs sit at 66ch inside it.

That said, it is worth tuning the grid so one common span lands near your body measure. On a 1200px container with twelve columns and 24px gutters, columns are 78px wide, and six columns span 588px — about 73ch at 16px. Half the grid is your measure. That is a good grid.

Left-aligned text in a wider container beats centred narrow text, because images and asides can then break out to the full column while the reading line stays fixed.

Margins, gutters, padding

ValueMobileTabletDesktopRule
Page margin16–2432–4848–80Or fluid, clamped at both ends
Gutter16–242424–32Never wider than the page margin
Panel padding16–202424–32At least 1.5× the body line height
Button padding12 vertical / 24–28 horizontalHorizontal 2–2.5× vertical
  • The proximity check. If a container has no background or border, its internal padding must be smaller than the gap to its siblings, or the grouping inverts and the padding reads as separation. With a background, padding can safely exceed the gap.
  • Optical top padding is always less than the number. The line box includes half-leading above the cap height, so 32px of visual space needs about 28px set. text-box-trim fixes this properly where support allows.
  • Padding in em for buttons, badges and inputs, so small and large variants stay proportional automatically.
  • Don't use the type scale for spacing. Spacing runs on its own 4/8 scale.
  • Don't set multi-column unless each column can hold 40ch or more.
08

Spacing between roles

Space belongs to the element below it. That single principle resolves most spacing questions, because it forces you to think about which relationship the gap is describing.

The relationship ladder

Derive the gap from the strength of the relationship, on a 4/8 scale.
RelationshipExampleGap
Bondedeyebrow → title, label → input, number → unit4–8
Attachedheading → its paragraph, input → helper text8–12
Siblingparagraph → paragraph, field → field16–24
Groupedsubsection → subsection, card → card32–48
Separatedsection → section64–96
Page-levelmajor region breaks96–160

The cleaner derivation for headings

space-above ≈ 1 – 1.5× the heading's own size space-below ≈ 0.25 – 0.5× the heading's own size

A 32px heading gets roughly 40px above and 12px below. The three-to-four times asymmetry falls out on its own, larger headings automatically get more air, and you never maintain a separate spacing table per level.

Proximity, inverted and correctIdentical type. Only the gaps move.
Space above the heading is too small

The previous section ends on this line and then continues past it.

Retention policy

The heading now floats between two paragraphs and belongs to neither.

Above is 3× below

The previous section ends on this line and then continues past it.

Retention policy

The heading is now clearly attached to the paragraph it introduces.

  • White space around a block is a stronger grouping signal than any border you can draw. Reach for space before you reach for a line.
  • Scale section spacing with the viewport. 96px of air between sections is generous on desktop and oppressive on a phone.
  • Don't use the same gap between paragraphs as between sections. It destroys scannability faster than any other single mistake.
09

Lockups

A lockup is two or three roles that always travel together with a fixed internal relationship. You style the lockup once, not the roles separately. This is the level at which pairing questions actually get answered.

The five rules that hold for every lockup

  • Internal gap smaller than external gap. If the eyebrow sits closer to the previous section than to its own title, the lockup does not exist.
  • One dominant element. Two equals is not a lockup, it is two things.
  • Differ on exactly two axes. Size and colour, or size and weight, or case and colour. Three is noise.
  • Share one edge. Same left alignment or same baseline. Never both drifting.
  • The subordinate must survive removal. If deleting the eyebrow breaks comprehension, it was never an eyebrow.

The recurring catalogue

LockupWhere it appearsRatioGap
Eyebrow + TitleHeroes, section openers0.7–0.8×4–8
Title + DeckArticles, landing pages1.2–1.5× body12–16
Heading + BodyThe base unit of everything1.5–2.5×8–12
Label + ValueSpecs, profiles, metadata0.75–0.85×4–8
Value + Unit + DeltaMetrics, KPIs0.5–0.6×2–12
Label + Input + HelperEvery form0.85×6 / 6
Title + MetadataBylines, timestamps, read time0.8×8
Quote + AttributionTestimonials, pull quotes0.75–0.85×16–24
Caption + CreditImages, figures0.85×4
Icon + LabelButtons, nav, menus1.0–1.25× cap6–8
Term + DefinitionGlossaries, documentation1.0× / bold4
Card title + Body + ActionEvery card grid1.25×8 / 16
Avatar + Name + RoleComments, lists, teams0.85×2 / 12
Price + PeriodPricing tables0.4–0.5×4
Empty stateHeading + body + action1.4×8 / 20

Live gallery

Five lockups, one familyAll IBM Plex, so structure is the only variable
Infrastructure
Regional failover moves to active-active
Both regions now serve live traffic, and the cutover window drops from eleven minutes to zero.
Regioneu-west-2
Replicas6
Last failover14 Mar 2026
Days before archived logs are deleted.
The failure was never the outage. It was that nobody could tell which region was serving.
Priya Raman, Reliability lead
Migration checklist
28 Aug 20267 min readUpdated
Icon optical size matches cap height, not font size. Both share a vertical centre, not a baseline.

How many popular versions are there?

Fewer than it looks. The structure of each lockup is essentially constant. What varies is the surface treatment of the subordinate element, and there are usually three or four of those in circulation.

Eyebrow + Title, the four versions you will actually encounter.
VersionTreatmentReads as
EditorialSmall caps, tracked, tertiary greyConsidered, print-derived
ProductSentence case, semibold, brand colourFriendly, contemporary
TechnicalUppercase monospaceSystematic, machine-adjacent
SequenceA number, only if the content really is a sequenceProcedural
Lockups are components, and components cost maintenance

If you define sixteen lockups, you have defined sixteen components. Lockups sprawl faster than roles do. Start with five — heading + body, label + value, label + input + helper, title + metadata, icon + label — and promote a sixth only after it has been used three times in different contexts.

10

Vibes

A vibe is a coordinated setting of the seven dials. The panel below runs the same content through each one so you can see how much of the personality is coming from the dials rather than the typeface.

Vibe switcherIdentical copy in every setting

The catalogue in words

VibeSettingFeels
SwissNeutral grotesque, ratio 1.2, hierarchy on weight, flush left ragged right, leading 1.4Objective, engineered
EditorialOld-style serif body, ratio 1.333–1.5, leading 1.6, measure 60–66ch, italic subheadsAuthored, unhurried
BrutalistRatio 1.6+, two or three levels, leading 1.0–1.1, full-bleed measure, no optical correctionRaw, deliberately unpolished
LuxuryDidone display at huge size, everything else tiny and tracked, leading 1.8+, near-zero ink coverageExpensive, because it wastes space
TechnicalGrotesque or mono, ratio 1.125, hierarchy on colour and weight, tabular figures, leading 1.35Precise, dispassionate
WellnessHumanist sans, low weight contrast, leading 1.7–1.8, lowercase headings, warm off-blackCalm, non-demanding
ConsumerGeometric or rounded sans, ratio 1.25, hierarchy on weight and colour, leading 1.5Friendly, unthreatening
InstitutionalTransitional serif or safe sans, ratio 1.2, moderate everythingCredible, slightly dull
NewspaperCondensed headline face, big ratio, 35–45ch columns, leading 1.3, hairline rulesUrgent, dense
AcademicOne family throughout, small caps, ratio 1.15, minimal levels, footnotes at 0.75×Rigorous, self-effacing
TerminalMono only, one or two sizes, hierarchy entirely on colour and indentationMachinic
ZineUnrelated families, inconsistent alignment, mixed scales, visible imperfectionHuman, anti-institutional
PosterOne element at 10–20× body, everything else at a single small size. Two levels.Declarative

Three things worth knowing about vibe

  • Whitespace does more work than the typeface. Set the same page in one family twice, once at leading 1.35 with tight margins and once at 1.7 with wide ones, and people will describe them as different brands.
  • Match the vibe to content density, not to the mood board. Luxury typography collapses the moment there is real information to convey. Newspaper density feels oppressive on a marketing page.
  • Commit to one hierarchy axis. Split hierarchy across size, weight and colour at once and everything reads as equally important, which is identical to nothing being important.
11

Dashboards

A dashboard is not a vibe. It is a constraint set that produces a narrow band of workable vibes. The constraint is that everything on screen is nominally important, so the classic hierarchy move — make one thing big — is unavailable. You lose your loudest dial before you start.

What the constraint forces

DialWhere it landsBecause
Ratio1.125–1.2No hero exists, and vertical space is scarce
Hierarchy axisColour and weightNearly everything sits at 12–16px. This is the one context where colour-as-hierarchy is correct rather than lazy.
Leading1.3–1.4Anything looser pushes the table below the fold
Measure30–45chPanels and cells are the tightest measure you set legibly
Grey rampThree tiersPrimary value, secondary support, tertiary chrome
FiguresTabular, alwaysProportional figures in a numeric column is a defect, not a preference
Metric scaleSeparate32–48px numbers above 12px labels break the type scale legitimately
The metric lockupToggle the failure modes
Request pipeline

Where dashboard typography actually goes wrong

  • Panel titles competing with the data. If the title is as loud as the metric, users read chrome instead of content. Titles want to be small, grey and deliberately quiet.
  • Units and deltas set at the value's size. "142.7", "ms" and "+12%" are three different roles. Unit at 0.5–0.6× the value, baseline-aligned rather than centred.
  • Truncation with no strategy. Long labels wrap, truncate, or tooltip. Choosing one per role is a real decision. Most dashboards default to whatever happens.
  • Density set once. A monitoring wall and an analyst's laptop need different densities. A compact/comfortable toggle should scale the spacing scale, never the type scale.
  • Delta colour with no second signal. Red and green alone fail for roughly one in twelve men. Add an arrow or a sign.

Table typography

  • Header at 11–12px, uppercase, tertiary grey. Cell at 13–14px, primary. They should differ in colour and case, not size, so column widths stay stable.
  • Numeric columns right-aligned with tabular figures. Text columns left. Never centre either.
  • Row separation by hairline or by zebra, never both.
Which dashboard are you building?

Monitoring is glanceable: few numbers, large, read from a distance, colour doing most of the signalling. Analysis is dense: many values, small, read up close, with sorting and comparison as the primary action. These are different type systems wearing the same name, and picking one is the first decision.

12

Typefaces

Why monospace fails for prose

Monospace forces every glyph into an identical advance width, so i and l get padded out while m and w get squeezed. Word shapes lose their distinctive silhouettes, which is what the eye is actually matching at reading speed. Fixation efficiency drops and reading slows measurably past a sentence or two. Mono also consumes more horizontal space, so either the measure runs long or the size has to shrink.

Same text, same size, same measureRead both. Notice where you slow down.
Proportional

A definition should be read once and understood. Proportional spacing preserves the outline of each word, so the eye can recognise whole shapes instead of assembling them letter by letter.

Monospace

A definition should be read once and understood. Proportional spacing preserves the outline of each word, so the eye can recognise whole shapes instead of assembling them letter by letter.

Where mono does earn its keep: character-level precision (code, IDs, hashes, hex values), vertical alignment (columns of numbers, diffs, logs), and machine-voice signalling. A short label or a five-word tag in mono is fine. A paragraph of definition text is not.

The fair caveat

Much of the "mono is unreadable" claim is inherited from terminal-era faces with poor letterfit. Modern humanist monospaces are considerably better than that reputation suggests. It is still worse than a proportional face for prose — just not disastrously so.

Faces worth knowing, by job

All available on Google Fonts unless noted.
JobFacesNote
UI body, sansInter, Public Sans, Source Sans 3, IBM Plex SansLarge x-height, tested at small sizes
Accessibility-firstAtkinson HyperlegibleGenuinely better for low vision, not just marketed as such
Long-form serifSource Serif 4, Literata, Charter/Charis, Newsreader, SpectralLiterata is drawn for screen reading
Display with personalityFraunces, Instrument Serif, Bricolage Grotesque, Archivo ExpandedVariable axes give you range without extra files
High-contrast displayPlayfair DisplayNever below ~28px. The hairlines vanish.
MonospaceJetBrains Mono, IBM Plex Mono, Commit Mono, Berkeley Mono (paid)Plex Mono is the one with a proportional sibling
SpecimensSame string, same size, same measure
IBM Plex Sanshumanist grotesque
Handgloves & 0123456789
Archivogrotesque, wide range
Handgloves & 0123456789
DM Sansgeometric
Handgloves & 0123456789
Source Serif 4transitional serif
Handgloves & 0123456789
Literatascreen-reading serif
Handgloves & 0123456789
Playfair Displaydidone — display only
Handgloves & 0123456789
Instrument Serifdisplay serif
Handgloves & 0123456789
Bricolage Grotesquedisplay, variable
Handgloves & 0123456789
JetBrains Monomonospace
Handgloves & 0123456789

Pairing

The reliable move is a superfamily: IBM Plex Sans with Plex Serif and Plex Mono, or Source Sans with Source Serif. Same skeleton, different voices, guaranteed harmony, and the decision is permanently removed. This manual is set that way.

If you want more contrast than a superfamily gives you, the rule is that the two faces must differ obviously. Serif against sans. Condensed against normal. High contrast against low. Two grotesques that are only slightly different is the worst possible outcome, because it reads as a mistake rather than a choice.

  • Two families plus a mono, maximum.
  • Check the family ships the weights, italics and figure sets you need before falling in love with it.
  • Test at your smallest real size on your worst target display before committing.
  • Don't set ultra-geometrics for body. The circular o and e make word shapes too uniform and reading gets tiring over a page.
  • Don't use a face with a very low x-height at UI sizes.
13

Colour and contrast

  • 4.5:1 minimum for body. 3:1 for text above 24px, or bold above 19px.
  • Three tiers of grey, no more. Primary, secondary, tertiary. A fourth tier is always an unresolved hierarchy question in disguise.
  • Use a near-black rather than pure black on white. Pure black on pure white is harsher than it needs to be, and slightly reduced contrast reads as more comfortable over long sessions.
  • Links need a non-colour signal too — underline, weight, or icon.
  • Don't let colour carry hierarchy alone outside dense data contexts. Grey text is a de-emphasis signal, not a level.
  • Don't encode meaning in colour without a second channel: an icon, a weight change, or a label.
14

Micro-typography

These are small, cheap, and the fastest way to make work look considered.

MarkCorrectWrongUse
Apostropheit’sit'sCurly, never the prime
Quotes“so”"so"Curly pairs
Hyphenwell-formedCompounds only
En dash2019–20262019-2026Ranges
Em dasha break—herea break - hereParenthetical breaks
Prime6′ 2″6' 2"Feet and inches only
Multiply1440×9001440x900Dimensions
Ellipsis...One character
  • Non-breaking space before units and after short numbers, so 90 days never breaks across a line.
  • Tabular figures wherever numbers stack vertically: font-variant-numeric: tabular-nums.
  • Hanging punctuation on pull quotes where the layout allows it.
  • One space after a period.
  • Widows and orphans matter in headings. In body text they matter much less than people claim.
  • Flush left, ragged right, as the default. Justify only with hyphenation enabled and a measure wide enough to avoid rivers.
  • Don't centre paragraphs. Centring works for one to three lines and fails past that, because the left edge moves and the eye loses its return point.
  • Don't set all caps beyond two or three words.
15

Stress tests

Every role must be tested in its worst case before it ships, not after. Four cases catch nearly everything.

CaseTestCatches
LongestThe longest realistic string, not a placeholderTruncation with no strategy, broken alignment
EmptyNull, zero, no data yetCollapsed layouts, orphaned labels
WrapForce two and three linesLeading that only worked at one line, broken lockup gaps
TranslatedAssume +30% length; German and Finnish are the usual worst casesFixed-width buttons, nav overflow, tab labels
The same button role, four waysFixed width fails on the third

Padding in em and no fixed width means all four survive. The empty case is why button labels need a fallback string, not an empty state.

16

The full checklist

Everything above, compressed. These are defaults you break knowingly.

Size and scale

Body 16–18px on screen. 15px floor for dense contexts. Never below 12px for required reading.

Set endpoints first, then solve for the ratio.

Don't use adjacent scale steps against each other. Skip one.

Cap at five or six levels.

Metric numbers get their own scale.

Round geometric output to values a human would type.

Shift the ratio by viewport; keep the roles fixed.

Weight

200+ units of separation or it reads as a bug.

No weights under 400 below 14px.

Bold for emphasis in a paragraph, italic for titles and voice.

One body weight across the whole system.

Leading

Inverse to size: body 1.5, subhead 1.25, display 1.05–1.15.

Proportional to measure and to x-height.

Unitless, never px.

Never below 1.0 except in intentional display lockups.

Reversed text: slightly more leading, slightly less weight.

Measure and containers

Body 45–75ch, 66 as the target.

Headings 25–40ch. Dashboard cells 30–45ch.

Set max-width in ch or em, not px.

Multi-column only if each column holds 40ch or more.

Grid places the block; the text sets its own measure inside it.

Spacing

Space belongs to the element below it.

Above a heading ≈ 1–1.5× its size; below ≈ 0.25–0.5×.

Spacing runs on its own 4/8 scale, independent of type.

White space groups more strongly than any border.

Unbordered container padding must be smaller than the sibling gap.

Optical top padding is less than the number you set.

Scale section spacing with viewport.

Tracking, case, alignment

Positive below 13px, negative above 40px, nothing in between.

All caps always needs +5–10%, and never past three words.

Never track lowercase body.

Sentence case for UI labels and buttons.

Flush left ragged right by default. Centre only 1–3 lines.

Colour

4.5:1 body, 3:1 for large or bold.

Three tiers of grey, no more.

Near-black, not pure black.

Colour never carries meaning alone.

Typefaces

Two families plus a mono, maximum.

Superfamily is the safest pairing; otherwise make contrast obvious.

Mono for precision and alignment, never for prose.

Tabular figures wherever numbers stack.

Verify weights and figure sets before committing.

Structure

Semantic heading level and visual size are independent.

One role per job. New job, new role. New screen, no new role.

A role is a locked bundle of seven properties.

Lockup internal gap always smaller than external gap.

One dominant element per lockup; two axes of difference.

Stress-test longest, empty, wrapped, translated.

Micro

Curly quotes and apostrophes.

En dash for ranges, em dash for breaks, hyphen for compounds.

Non-breaking space before units.

One space after a period.

Widows matter in headings, less in body.

The three most often broken by good designers

Adjacent scale steps. Spacing attached to the wrong element. Hierarchy split across three axes at once.