UI craft

The anatomy of polished UI

Polish is rarely one big idea. It is spacing that follows a scale, radii that agree with each other, shadows you barely notice, and states that answer every interaction. This note takes those decisions apart, one at a time.

Continue 20 16 r 14 STATES Default Hover Pressed Focus Disabled 4 · 8 · 12 · 16 · 20 · 24
On this page
  1. 01Spacing and alignment
  2. 02Radius and shape
  3. 03Shadows and elevation
  4. 04Borders and surfaces
  5. 05Typography and line height
  6. 06Component states
  7. 07Contrast and focus
  8. 08Before and after
  9. 09Quality checklist

When people call an interface polished, they rarely point at one thing. They are reacting to hundreds of small decisions that agree with each other: every gap from the same scale, every corner from the same set of radii, every state accounted for. When those decisions disagree, the interface feels slightly off, even to people who cannot say why.

This note takes the decisions one at a time. The examples are live HTML and CSS, so the buttons and fields respond to your pointer and keyboard.

01Spacing and alignment

Most interfaces that feel messy are not short of space; they use too many different amounts of it. Pick a scale, usually based on 4 or 8 pixels, and take every margin, padding and gap from it. Then align edges: text, inputs and buttons inside a container should share the same left edge.

Values picked by eye

Gaps of 5, 15 and 11 pixels. Three slightly different left edges.

Values from the scale

4 inside the heading pair, 16 between blocks, 24 padding. One left edge.

Figure 1. An 8-point scale with a 4px half-step, and the same card built with and without it.

A scale does not make every decision for you. It makes the decisions comparable. "Is this 16 or 24?" is a question a team can answer and a reviewer can check. "Is this 13 or 15?" is not.

02Border radius and shape consistency

Corner radius sets the personality of an interface, from sharp and technical to soft and friendly. Polish comes less from the size you choose than from using few sizes, and from nesting them correctly.

The rule for nested shapes: outer radius = inner radius + the padding between them. When a rounded element sits inside a rounded container with the same radius, the gap between their corners looks thicker than the gap along their sides.

Same radius inside and out

The corners bulge. The padding looks uneven although it is not.

Outer = inner + padding

Concentric corners. The gap is the same all the way round.

Figure 2. Nested radius with the same outer corner and 14px padding, and a five-step radius scale with the job of each step.

03Shadows and elevation

A shadow says "this surface is above that one." That is its only job. Strong, dark shadows say it too loudly, add visual noise, and make the interface look heavy. Soft shadows with low opacity do the job and then get out of the way.

Harsh

0 4px 10px rgba(0,0,0,.45). Dark, short and smudgy. It reads as a heavy outline.

Subtle

0 6px 18px -2px at 10%, plus a 1px contact shadow at 6%. Lift without weight.

Figure 3. Harsh and subtle shadows on the same tile, and a four-level elevation scale. Higher levels get larger blur and offset, not darker colour.

A few rules that keep shadows consistent:

  • Define elevation as tokens, three or four levels, and use them by meaning: a dropdown is always level 2, wherever it appears.
  • Keep one light source. Vertical offset only, or a consistent small offset in one direction. Shadows pointing different ways are what make a screen feel "off".
  • Use a negative spread to keep large blurs from bleeding sideways. It makes a soft shadow look like it comes from below, not from all around.
  • Tint shadows with your ink colour instead of pure black, so they blend with warm or cool backgrounds.
TokenValueUse
elevation.10 1px 2px / 6%, 0 1px 1px / 4%Cards, list items that can be dragged
elevation.20 6px 18px -2px / 10%Menus, popovers, tooltips
elevation.30 18px 40px -10px / 18%Dialogs, drawers

04Borders and surface contrast

There are three ways to separate a surface from its background: a border, a change of fill, or a shadow. Polished interfaces usually pick one main method per level and stick to it. Using all three on one card doubles the noise without adding information.

Borders also have an accessibility job. When a border is the only thing that shows where an input field is, it is part of how people identify the control. WCAG 2.2 success criterion 1.4.11 asks for a contrast of at least 3:1 for that kind of visual information. Decorative dividers can stay subtle. Field boundaries cannot.

Field border at about 1.5:1

Looks clean in a mock-up. On a bright screen, or for low-vision users, the field disappears.

Field border above 3:1

Still quiet, now clearly a control. Dividers elsewhere on the page can stay light.

Figure 4. The same field on white. Left: this site's line colour, about 1.5:1. Right: the site's original faint ink colour, about 3.8:1.

05Typography and line height

Small typographic settings decide whether text feels comfortable or cramped. The most important three are line height, line length and letter spacing.

Default settings everywhere

Body line height 1.15, heading at 1.5, proportional figures that do not line up.

Tuned settings

Body at 1.55, heading at 1.2 with slight negative tracking, tabular figures for amounts.

Figure 5. Same text, same size, same font. Only line height, letter spacing and number style differ.
  • Body text: line height around 1.5, and a line length of roughly 45 to 75 characters.
  • Headings: tighter, around 1.1 to 1.25, because large text with body line height falls apart into separate lines.
  • Letter spacing: slightly negative for large headings, slightly positive for small uppercase labels, zero for body text.
  • Numbers: tabular figures in tables and amounts, so digits line up in columns.

Leave room for users who change these settings. WCAG 2.2 success criterion 1.4.12 expects content to keep working when people increase line height and letter spacing, so avoid fixed heights on text containers.

06Component states: default, hover, pressed, disabled and focus

A component is not finished when its default state looks good. Every interactive element needs an answer for each state, and the answers should follow one logic across the system. The buttons below are real. Hover, press and tab to them.

Defaultaccent fill
Hoverdarker fill
Pressedinset, 1px down
Focus2px ring, 2px offset
Disabledneutral, no pointer
Try it
Figure 6. Five states of a primary button, shown side by side, then three live buttons. The demo buttons do nothing when clicked.
StateWhat changesWhy
HoverBackground one step darkerConfirms the element is interactive, for pointer users only
PressedDark fill, inset shadow, moves 1px downImmediate feedback that the click registered
FocusVisible ring outside the elementShows keyboard users where they are; never removed
DisabledNeutral fill, muted text, no hoverShows the action exists but is unavailable
LoadingLabel kept, spinner added, clicks blockedPrevents double submission without layout shift

Disabled buttons deserve a second thought. A greyed-out "Submit" with no explanation leaves people guessing what is missing. Often it is better to keep the button enabled and explain the problem when it is pressed, or to show the reason next to it.

07Accessible contrast and visible focus indicators

Polish that only works for some users is not polish. Two checks catch most problems: text contrast and focus visibility.

  • Text contrast: at least 4.5:1 for body text and 3:1 for large text under WCAG 2.2 level AA. Light grey placeholder text and muted labels are the usual failures.
  • Focus visible: success criterion 2.4.7 requires a visible focus indicator for keyboard users. Removing the browser outline without a replacement is one of the most common accessibility failures in polished-looking interfaces.
  • Focus not hidden: criterion 2.4.11, new in WCAG 2.2, requires that the focused element is not fully hidden behind sticky headers, banners or cookie bars.
Outline removedKeyboard users lose their place. Fails 2.4.7.
Default outlinePasses, but may clash or disappear on some backgrounds.
Designed ring2px accent ring with a 2px offset, so it never merges with the field border.
Tab through these
Figure 7. Three focus treatments, shown statically, and two live fields that use the designed ring. The live fields are not connected to anything.

The ring itself is a design token like any other: one colour, one width, one offset, used for every focusable element. CSS :focus-visible lets you show it to keyboard users without showing it on every mouse click.

08Putting it together: one component, before and after

Here is a small settings card with every decision from this note applied. The content barely changed: one line of context and a clearer button label. Everything else is a dozen values.

Before
After
Figure 8. Before: mixed radii, a heavy shadow, a box around every row, uneven padding and centred pill buttons. After: one radius family, elevation 1, dividers instead of boxes, padding from the scale, a right-aligned primary action and a toggle track that meets 3:1.

The refined version uses fewer visual effects than the original, not more. That is typical. Polish usually comes from removing inconsistencies, then letting a small set of values do the work.

09An interface-quality checklist

Before a screen goes to development
  • Every margin, padding and gap comes from the spacing scale.
  • Content inside a container shares one left edge.
  • Radii come from a small scale, and nested radii follow outer = inner + padding.
  • Shadows use elevation tokens by meaning, with one light source.
  • Each surface uses one main separation method: border, fill or shadow.
  • Field boundaries and icons that carry meaning reach 3:1 contrast; text reaches 4.5:1.
  • Body text has a line height around 1.5; headings are tighter; amounts use tabular figures.
  • Every interactive element has hover, pressed, focus, disabled and, where relevant, loading states.
  • Focus is always visible and never hidden behind sticky elements.
  • Nothing on the screen uses a value that is not in the system. If it must, the system gets a new token.