Dustin Johnson ← Design Primitives
Design Primitive Reference · Part IV · April 2026

Typography & Dimension

The typographic internals of button text — units, line-height, letter-spacing — and the width rules that govern how buttons grow, shrink, and hold their shape.

A1. The Unit Question: px vs rem vs em

Every button font-size, padding, and border-radius is expressed in a unit. The choice between px, rem, and em isn't cosmetic — it determines whether a button scales with user preferences, with its own font-size, or with nothing at all.

UnitRelative toStrengthsRisks
pxNothing (absolute)Pixel-perfect. Predictable everywhere. No inheritance surprises. Best for borders, shadows, and visual details that shouldn't scale.Ignores user font-size preferences. Fails WCAG if used for body text font-size. Requires manual adjustment at every breakpoint.
remRoot element (:root / html)Scales with user's browser font-size setting. Consistent across the page — 1rem means the same thing everywhere. The accessibility standard for font-size.Doesn't create component-internal proportionality. A 0.5rem padding doesn't know what font-size it's next to.
emThe element's own font-sizeCreates self-scaling components. If padding is in em, changing the font-size scales the entire button proportionally. The most elegant approach for button size variants.Can compound unpredictably in nested elements (though this doesn't affect buttons, which are leaf nodes). Requires mental math: 0.625em of what?

The Modern Consensus

Font-size in rem. This is the accessibility baseline. Setting button font-size in rem means that when a user increases their browser's default font size (for low vision, for preference, for comfort), button text scales with it. Using px for font-size overrides this preference — it's one of the most common accessibility failures on the web, and one of the easiest to avoid.

Padding in em. This is what makes button size variants elegant. If padding is expressed in em (relative to the button's own font-size), then changing the font-size automatically scales the padding. A single font-size change creates a proportionally larger or smaller button without touching any other property. This is the technique advocated by Ahmad Shadeed, Donnie D'Amato, and implemented in several modern token systems.

Border-radius in em or px. If radius is in em, it scales with the button — a larger font-size produces proportionally rounder corners. If radius is in px, it stays fixed regardless of button size, which can look oddly small on XL buttons or oddly large on XS buttons. The em approach is more internally consistent; the px approach gives you more explicit control.

Border-width in px. A 1.5px border should be 1.5px whether the button is small or large. Border-width is a visual detail, not a proportional dimension. Scaling it with font-size produces borders that look too thick on large buttons and disappear on small ones.

13px · 0.5em / 1em
14px · 0.625em / 1.375em
16px · same em ratios
18px · same em ratios
Four sizes from one set of em padding values — only font-size changes
/* The em-scaling pattern */ .btn { font-size: 0.875rem; /* 14px at default root */ padding: 0.625em 1.375em; /* scales with font-size */ border-radius: 0.5em; /* scales proportionally */ border: 1.5px solid transparent; /* stays fixed */ gap: 0.5em; /* icon gap scales too */ } /* Size variants: only font-size changes */ .btn-sm { font-size: 0.8125rem; } /* 13px */ .btn-md { font-size: 0.875rem; } /* 14px */ .btn-lg { font-size: 1rem; } /* 16px */ .btn-xl { font-size: 1.125rem; } /* 18px */

A2. Line-Height

Line-height in buttons behaves differently than in paragraphs. In body text, line-height creates spacing between lines. In buttons, there is (almost always) a single line of text — so line-height determines the internal vertical rhythm of the text block within the button, and it can affect the button's overall height if you're not careful.

line-height: 1
line-height: 1.2
line-height: 1.5

line-height: 1 (or 1em) makes the text block exactly the height of the font's cap-height plus descenders. This produces the tightest vertical centering and the most predictable button height. It's the most common choice in design systems because it gives padding full control over vertical space.

line-height: 1.2–1.25 adds a small buffer around the text. This can improve the vertical centering of certain fonts that have unusual ascender/descender ratios. It's a pragmatic middle ground.

line-height: 1.5 (the body text default) is usually too tall for buttons. It adds visible extra space above and below the text that makes padding values feel larger than intended. If your button inherits line-height from body text, the button will be taller than you designed it.

Set line-height explicitly on your button component — don't inherit it from body text. A value of 1 or 1.2 is appropriate for single-line button labels. If your button label might wrap to two lines (which it generally shouldn't — see Part B), then 1.3–1.4 provides enough interline spacing without inflating the button.

A3. Letter-Spacing

Letter-spacing (tracking) is the space between individual characters. In button text, it's primarily a tool for readability at small sizes and for controlling the visual density of uppercase labels.

-0.02em · Tight
0 · Normal
0.04em · Wide
0.08em · Uppercase

Normal (0) is the safe default for sentence-case button labels at 13–16px. The font's built-in spacing is almost always correct at these sizes.

Slight tightening (-0.01 to -0.02em) is used by some modern systems (Linear, Vercel) at medium and large button sizes. It gives the text a slightly more compact, "designed" feel. This only works with fonts that have generous default tracking — tightening a font that's already tight produces illegible results.

Wide tracking (0.04–0.12em) is required for uppercase labels. All-caps text at normal tracking looks cramped because uppercase letters have more uniform widths and less natural rhythm than mixed-case. The USWDS specifically recommends adding letter-spacing to uppercase button text. The wider the tracking, the more "editorial" or "architectural" the button feels.

Letter-spacing should always be specified in em, not px. This ensures the tracking scales proportionally with font-size — 0.08em of tracking on a 12px label is ~1px, which is appropriate; 0.08em on an 18px label is ~1.5px, which is also appropriate. Fixed-px tracking breaks at different sizes.

A4. Font Inheritance

One of the most common CSS gotchas: <button> elements do not inherit font-family from their parent by default. Browsers apply their own default font (typically a system sans-serif that may not match your design system's typeface). The fix is trivial but easy to forget:

button, input, select, textarea { font-family: inherit; font-size: inherit; /* also doesn't inherit by default */ line-height: inherit; /* normalize this too */ color: inherit; }

This reset should be in your global stylesheet. Without it, your button text will render in the browser's default font, not your design system's chosen typeface — a subtle but pervasive inconsistency that's easy to miss in development and obvious in production.

B1. How Wide Should a Button Be?

By default, a button is as wide as its text plus its horizontal padding. This is almost always the right behavior. But there are four common situations where you need to override it — and each one has a different CSS solution.

The Default: Content-Driven Width

The natural state of a button is display: inline-flex (or inline-block), with width determined by its content. This is correct for the vast majority of buttons in application UIs. The text label dictates the width; padding provides breathing room; the button is only as large as it needs to be.

Content-driven: width follows label length

The Problem: Short Labels

Content-driven width breaks down when the label is very short. A button labeled "OK" or "Go" with standard padding becomes a tiny, hard-to-click rectangle. The solution is min-width.

Without min-width
min-width: 120px

A min-width of 80–120px (or 5–7.5em) prevents buttons from becoming uncomfortably small while still allowing them to grow naturally with longer labels. This is the approach used by Carbon, SAP Fiori, and most enterprise design systems. Always combine min-width with horizontal padding — if only min-width is set and padding is removed, a long label could touch the button edges.

B2. Full-Width Buttons

A full-width button spans the entire width of its container. This is the standard treatment in three contexts: mobile forms (where the button becomes a full-width touch target at the bottom of the screen), modal footers (where the confirmation action spans the modal width), and stacked card actions (where each button fills the card width).

width: 100% — fills its container

The CSS is simple: width: 100% or display: flex on the parent with flex: 1 on the button. The design decisions are about context: full-width buttons work when there is one primary action and the container is narrow enough that the button doesn't look like a banner. On a 1200px desktop layout, a full-width button looks absurd. Use max-width to cap it:

width: 100% · max-width: 400px — capped and centered

The clamp() function is the modern alternative: width: clamp(200px, 100%, 400px) creates a button that is full-width in narrow containers, capped at 400px in wide ones, and never narrower than 200px.

B3. Equal-Width Button Groups

When two or more buttons sit side-by-side — "Cancel" and "Save," for example — their different label lengths create different widths. This is visually uneven and can feel unbalanced, especially in dialog footers.

Uneven: natural width difference

Two approaches to equalize:

Flexbox with flex: 1. Both buttons share equal space. The shorter label gets more padding; the longer label gets less. This is simple and works well for 2–3 buttons, but can produce uncomfortably wide buttons if one label is much shorter than the others.

flex: 1 — equal width, shared space

CSS Grid with 1fr columns. Similar result, but grid gives you more control over minimum column widths and is better suited for groups of 3+ buttons.

grid: repeat(3, 1fr) — three equal columns

In practice, the best approach depends on whether visual symmetry or natural proportion is more important to your design language. Some systems (Apple, shadcn/ui) prefer natural width with sufficient min-width. Others (Carbon, SAP) prefer equal-width groups for the cleaner alignment.

B4. Text Overflow and Truncation

What happens when the label is too long for the available space? This arises in three scenarios: the label was written too long (a content problem), the label was translated into a longer language (a localization problem), or the container is too narrow at a responsive breakpoint (a layout problem). There are three CSS responses:

Wrap (multi-line)
Truncate (ellipsis)
Clip (hidden overflow)

The Guidance

Wrapping (white-space: normal) lets the text flow to a second line. This preserves all the text but creates a taller button, which can break vertical rhythm and look inconsistent next to single-line buttons. It's the safest option for accessibility — no text is hidden — but it's the most visually disruptive. Use it as a last resort, and set a tighter line-height (1.2–1.3) to keep the wrapped button from becoming too tall.

Truncation (text-overflow: ellipsis) clips the text and adds "…" to signal that more text exists. This preserves the button's height and shape but hides potentially critical information. If you truncate, you should ideally provide a tooltip on hover that shows the full label. Truncation is best for situations where the button label is supplementary (like a file name in a "Download report-q3-2024-final-v2.xlsx" button), not where the label is the primary instruction.

Clipping (overflow: hidden without ellipsis) is the worst option — it hides text without any visual cue that text was hidden. Avoid this.

The real answer to "what do I do when the label is too long?" is: make the label shorter. Button labels should be 1–4 words. "Save" is better than "Save all changes." "Export CSV" is better than "Export all data to spreadsheet format." If a button needs more than 4 words, it's doing too much communication — consider moving the explanation to surrounding text and keeping the button label tight.

B5. The Width Decision Framework

When configuring width behavior for your button primitive, the decisions cascade:

1. Default to content-driven width. width: auto with horizontal padding. This handles 80% of button use cases.

2. Set a min-width. Prevent buttons from becoming too small with short labels. 80–120px (or 5–7.5em) is the standard range. Always keep horizontal padding present alongside min-width.

3. Offer a full-width variant. width: 100% for mobile forms, modals, and stacked layouts. Cap with max-width for desktop contexts where the container is wider than ~400px.

4. Set white-space: nowrap as the default. Button labels should not wrap. If they do, the label is too long. Handle overflow with truncation + tooltip rather than wrapping, unless wrapping is explicitly designed for.

5. In flexbox containers, add min-width: 0. This prevents flex items from overflowing their container when the label is long — a known flexbox behavior that D'Amato and Kevin Powell have documented extensively. Without it, a long label can push the button beyond its flex container.

/* The complete width system */ .btn { display: inline-flex; white-space: nowrap; min-width: 5em; /* prevents tiny buttons */ max-width: 100%; /* never exceeds container */ } /* Full-width variant */ .btn-block { display: flex; width: 100%; justify-content: center; } /* Capped full-width (for desktop) */ .btn-block-capped { display: flex; width: clamp(200px, 100%, 400px); justify-content: center; } /* Truncation safety net */ .btn-truncate { overflow: hidden; text-overflow: ellipsis; } /* Flexbox safety */ .flex-container .btn { min-width: 0; /* prevents flex overflow */ }

B6. How Many Words?

This isn't a CSS question, but it governs every width decision. The best button labels are 1–3 words. Some guidelines from current practice:

WordsExamplesGuidance
1Save, Send, Cancel, Delete, Share, SubmitIdeal for primary actions. Universally understood. No width issues. The gold standard.
2Get started, Sign up, Learn more, Add item, Export CSVThe most common label length. Clear, scannable, fits comfortably in most layouts.
3Save and close, Create new project, Go to dashboardAcceptable. Start watching for width issues in tight layouts and at small button sizes.
4+Save changes and continue, Download full report as PDFToo long. Refactor the label or move the explanation into surrounding copy. Four-word buttons often indicate that the button is trying to explain itself — the UI around it should be doing that work.

For internationalization, plan for 30–50% text expansion in translation. A label that fits perfectly in English may overflow in German, Finnish, or Greek. This is where min-width (preventing tiny buttons in short-word languages like Chinese) and truncation strategies (handling expansion in long-word languages) both become essential. The em-based padding system from Section A1 helps here — because the padding scales with font-size, and localized text is usually set at the same font-size, the proportions hold even as the label length changes.

Part IV of the Button Taxonomy. Parts I–III cover structure, contrast, construction, and hover behavior.
The best button text is the shortest text that's still clear.