Book a callGitHub1

Craft03/04

Interface details that add up

Updated · Edit on GitHub

Most of these details go unnoticed one at a time. Together they decide whether an interface feels careful. When something works exactly as people expect, they carry on without a second thought, and in the aggregate the small details become visible. (STD-visual-details-50)

Each rule below is an OpenDesigner house standard or one of its accessibility floors, with its source named. The drawings use this site's own type and colors, and the measurements in them are real.

Letter spacing and line height change with size

Never use one letter spacing for every size. Tighten large headings, for example to -0.02em. Give small text slightly more space, and leave body text near 0. Letters look further apart as text grows. (STD-visual-details-05)

Give capitals more room. Uppercase labels need looser letter spacing than the same words in sentence case, or they look cramped. (STD-visual-details-04)

Set line height inversely to size. Keep it tight on large headings, for example 1.05, and looser on body text, for example 1.5. (STD-visual-details-06) Build hierarchy from weight, size and line height together, not from size alone. (STD-visual-details-07)

Keep lines of body text to about 65 characters, so they stay comfortable to read. (STD-visual-details-01)

1
Quarterly review
2
Status · In progress
3
Revenue grew in every region this quarter. The largest gain came from renewals, which rose for the third quarter in a row.
  1. 1
    Large text: tighter. Letter spacing -0.02em, line height 1.05. Big letters drift apart, so they are pulled in.
  2. 2
    Capitals: looser. Wider than the same words in sentence case. This label uses 0.07em.
  3. 3
    Body: neutral. Letter spacing near 0, line height 1.5, lines no wider than about 65 characters.
One set of letter spacing does not fit every size. Large headings are tightened and set close, small capital labels are opened up, and body text stays near zero with looser lines and a width limit of about 65 characters.

Source: the apple-design skill in emilkowalski/skills, and Emil Kowalski's essay Agents with taste for capitals and line length.

Numbers that change in place

Use tabular digits for price columns and for numbers that update, like timers and counters. Tabular digits all have the same width. In CSS that is font-variant-numeric: tabular-nums. Columns then line up, and a changing number does not shift as it updates. (STD-visual-details-02)

Digits of different widths

The narrow 1s pull some amounts to the right, so the decimal points no longer line up and the column is hard to scan.

proportional digits
Hosting$1,204.50
Design tools$611.11
Contractors$8,410.00
Travel$1,111.19

Tabular digits

Every digit takes the same width, so the decimal points sit on one line and a changing number does not jiggle.

font-variant-numeric: tabular-nums
Hosting$1,204.50
Design tools$611.11
Contractors$8,410.00
Travel$1,111.19
The same expense column twice, with a dashed line where the decimal points should sit. The left column is drawn with narrow 1s, the way many typefaces set digits by default, and the amounts drift. With tabular digits they line up, and counters and timers stop shifting as they change.

When a number animates, such as a counter or a price, use the NumberFlow library instead of re-rendering the text. (STD-visual-details-59)

Source: Emil Kowalski's Agents with taste and the animation-vocabulary and pick-ui-library skills in emilkowalski/skills.

Small habits in text

Write the ellipsis as one character (…), never as three periods. (STD-visual-details-03)

Underline only links. Emphasize other text with weight or color. Then an underline always means "you can click this". (STD-visual-details-09)

Emphasize interface text with weight, not italics. Italics belong to citations and stress in long prose; in an interface they read like print. (STD-visual-details-08)

Print habits in an interface

Three periods instead of an ellipsis, an underline on words that are not a link, and italics for emphasis.

Saving...three periods
Sign-in needs a second stepunderline, not a link
Recommended for adminsitalic emphasis

Interface habits

One ellipsis character, weight for emphasis, and underlines kept for links, so an underline always means “you can click this”.

Saving…one character: …
Sign-in needs a second stepweight for emphasis
Learn about passkeysunderline = link
Three small text habits. Use the single ellipsis character instead of three periods, emphasize interface text with weight instead of italics or underlines, and underline only links, so an underline stays a promise that the words can be clicked.

Source: Emil Kowalski's Agents with taste.

Soft edges, not hard lines

Separate surfaces with a semi-transparent shadow rather than a solid, opaque border. A solid border where a shadow belongs is one of the small mistakes that add up to an interface that is not great. (STD-visual-details-14)

A solid, opaque border

A flat, opaque gray line draws the same hard box around each card, whatever sits behind it.

Starter plan3 projects · 2 seats
Team planUnlimited projects · 10 seats
border: 1px solid #808793

A semi-transparent shadow

A faint ring of the ink color plus a soft drop. The edge is partly see-through, so it blends with whatever is behind it.

Starter plan3 projects · 2 seats
Team planUnlimited projects · 10 seats
box-shadow:
  0 0 0 1px rgb(34 36 40 / 0.13),
  0 1px 2px rgb(34 36 40 / 0.08),
  0 10px 24px -12px rgb(34 36 40 / 0.34);
The same two cards on a tinted background. A solid gray border boxes them in; a semi-transparent ring and a soft shadow in the ink color separate them from the page and let the background show through the edge.

Under a sticky header, fade the edge instead of drawing a permanent line. Show a short blur or gradient only where content actually passes under the header. (STD-visual-details-15)

A hard line under the header, always

The 1 px divider is there even when nothing is underneath, and it cuts the scrolling content with a sharp edge.

A soft edge where content passes under

The header is translucent and a short fade appears only where content slides beneath it. No permanent line.

A list scrolled under a sticky header. A permanent 1 px divider cuts the content with a hard edge; a translucent header with a short fade where content passes beneath separates the two only where they overlap.

Never place one light, see-through surface on another. Text on stacked translucent layers becomes hard to read. (STD-visual-details-18)

Source: the README and the apple-design skill in emilkowalski/skills.

Targets and focus, measured

Make every touch target at least 44 by 44 points on iOS, 48 dp on Android and 44 px on the touch web. When the drawn control is smaller, grow its hit area with a pseudo-element on the web, or hitSlop in React Native, never by growing the drawing. (STD-mobile-touch-09)

No web target, for a mouse or a finger, is smaller than 24 by 24 CSS pixels. This is an accessibility floor, from WCAG 2.2 2.5.8 Target Size (Minimum).

Every focusable element shows a focus ring: 2 px wide, 2 px from the element, with at least 3:1 contrast against what is around it. This is also a floor, from 2.4.7 Focus Visible and 1.4.11 Non-text Contrast.

Set the text in inputs to 16 px or more. Below that, iOS Safari zooms the page when the field gets focus and does not zoom back out. Never turn off pinch zoom to stop it. (STD-mobile-touch-11, STD-accessibility-motion-27)

1 20 · 24 · 44 px
2Continuering 2 px · gap 2 px
3Searchtext 16 px
  1. 1
    Icon button. The icon is drawn at 20 px. Its hit area (dashed) grows to 44 px for touch with a pseudo-element, never by growing the icon. 24 by 24 px (green) is the floor for every web target.
  2. 2
    Focus ring. 2 px wide, 2 px from the edge, and at least 3:1 against what is around it.
  3. 3
    Input text. 16 px or larger, so iOS Safari does not zoom the page when the field gets focus.
Three measurements drawn at actual size: a 20 px icon whose tap area grows to 44 px for touch, over the 24 px floor for every web target; a 2 px focus ring with a 2 px gap; and input text at 16 px, the size below which iOS Safari zooms in.

Source: the mobile-native, animate-expo and apple-design skills in emilkowalski/skills, and Emil Kowalski's Agents with taste.

Text on colored fills

Text on a fill needs at least 4.5:1 contrast, or 3:1 when it is large. OpenDesigner measures with the WCAG 2 formula and never rounds up, so 4.49:1 fails. This is an accessibility floor, from 1.4.3 Contrast (Minimum).

Put color on solid layers, not on see-through ones. Over a translucent surface, use higher-contrast text, a slightly heavier weight and a little more letter spacing. (STD-visual-details-21, STD-visual-details-13)

Text that sinks into its fill

Dark text on bright cobalt, white on a pale tint, white on mid gray. Measured, none reaches 4.5:1, the floor for body text.

Publishdark ink on cobalt
#222428 on #2338ff
2.28:1
Draftwhite on a pale tint
#ffffff on #e3ecff
1.18:1
Archivedwhite on mid gray, large text only
#ffffff on #808793
3.61:1

Each fill with its own text color

Each fill gets a text color chosen for it: white on the strong fill, a dark shade of the same hue on the pale ones. Every pair clears 4.5:1.

Publishwhite on cobalt
#ffffff on #2338ff
6.81:1
Draftdark cobalt on a pale tint
#1c31ce on #e3ecff
7.44:1
Paiddark green on a pale green
#00652c on #ebf7ed
6.57:1
Text on colored fills, from this site's own palette, measured with the WCAG 2 formula and never rounded up. Dark text on bright cobalt, white on a pale tint and white on mid gray fail the 4.5:1 floor for body text; pairing each fill with its own text color passes.

Source: the apple-design skill in emilkowalski/skills, for the rules on translucent surfaces.

Build from the tokens

Tokens are the named values of a design system, such as a color or a spacing step. See Design tokens.

Build from the project's existing tokens, and extend them when something is missing. Never hand-type or approximate a value, and never start a second, parallel set. (STD-visual-details-26)

Make every spacing, timing and alignment value a choice you can defend. (STD-visual-details-27) Do not ship jittery scrolling, misaligned icons or layouts that break when the device rotates; they read as carelessness. (STD-visual-details-28)

Source: the prototype, animate and review skills in emilkowalski/skills, and the apple-design skill.

Where these rules come from

The house standards and their sources are listed in the standards file, and the floors on the Accessibility floors page. Who OpenDesigner learns from describes every source. OpenDesigner is not affiliated with or endorsed by the people it cites.