Writing · UI

The interface guidelines, played

Rauno Freiberg's Web Interface Guidelines, every rule built twice: once without it and once with it, side by side, so you feel the difference instead of reading about it. 14 minutes, 8 demos.

Three rules, felt

Run down the list, clicking between the rows.

✗ Without

Nothing picked · missed 0

margin between the rows

✓ With

Nothing picked · missed 0

padding inside each row

Space between rows looks like part of the list, so clicks land in it, and miss. Put the space inside each row as padding instead: the list looks the same, and every pixel of it picks something.

Rule
On this page

01 · The idea

Rules you can feel

Rauno Freiberg keeps a list called the Web Interface Guidelines: around fifty small rules for interfaces that feel right. A label that focuses its field. A menu that opens when you press, not when you let go. Numbers that don’t shiver as they count. None of them is big. Together they’re the difference between an interface that works and one that feels made with care.

Read as a list, they’re easy to nod at and forget, because each one describes a feeling, and a feeling doesn’t come across in a sentence. So here every rule is built twice: once without it, once with it, side by side, for you to try. The demo at the top has three to start with. Click between the rows of the first list, and then of the second.

Each demo also says how this site does it, including where it doesn’t follow a rule, and why.

02 · Interactivity

Forms and controls that do what they look like

Most of these rules come down to one idea: a thing should work the way it looks. A word next to a field looks like part of it, so clicking it should focus the field. A field in a form looks submittable, so Enter should send it. Start with forms:

Forms: each rule without, and with

Click the word “Email”, not the box.

✗ Without

Email

<span>Email</span>

✓ With

<label for="email">Email</label>

A label tied to its field makes the word part of the target: clicking it focuses the field (or ticks the checkbox), and a screen reader reads it out when the field is reached. A span beside the field only looks like a label.

On this site: Every pencil switch is wrapped in its <label>: click a switch's words and it flips.

Rule

One more for forms: when a field must be filled in, or must look like an email, the browser’s own required and type checks are often enough, with no validation code to write.

Then the controls. A switch, a list, a segmented picker: each has a shape that promises something, and the rule is to keep the promise:

Controls: each rule without, and with

Run down the list, clicking between the rows.

✗ Without

Nothing picked · missed 0

margin between the rows

✓ With

Nothing picked · missed 0

padding inside each row

Space between rows looks like part of the list, so clicks land in it, and miss. Put the space inside each row as padding instead: the list looks the same, and every pixel of it picks something.

Rule

03 · Typography

Type that holds still

Type rules are mostly about stillness: words that don’t shift under the pointer, numbers that don’t shiver as they change, strokes that don’t break up.

Typography: each rule without, and with

Run the pointer along each row of links.

✗ Without

font-weight: 600 on hover

✓ With

colour on hover

Bolder letters are wider, so the word grows and shoves its neighbours along: the row jitters under the pointer. Change the colour instead, which takes no room.

On this site: Hover changes colour or opacity here, never weight. The one exception is the home page's name, which swells letter by letter on purpose.

Rule

The rest can’t be shown side by side, because they’re settings for the whole page. Smooth the font’s rendering, ask for legibility over speed, and stop iPhones from enlarging the text when they turn sideways. Then load only the characters you use: a font subset to the languages on the page can be a fraction of the full file.

globals.css
html {
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  -webkit-text-size-adjust: 100%;
}

Headings read best at a medium weight, 500 or 600, and can grow with the screen through clamp(); the responsive article has a graph you can drag for that one.

04 · Motion

Fast, small, and only when it's news

The motion rules all ask one question: does this animation earn its time? A menu opened twenty times a day should answer instantly; a big, rare moment can take longer. And an animation nobody can see shouldn’t run at all.

Motion: each rule without, and with

Open and close each menu a few times.

✗ Without

450ms

✓ With

150ms

An answer to a click should arrive within about a fifth of a second; slower, and the interface seems to be thinking it over. Long, deliberate motion belongs to rare, big moments, not to a menu opened twenty times a day.

On this site: Hover, focus and press take 150ms. Only the page changes and the intro take seconds, on purpose: they're seen a few times, not all day.

Rule

These are the rules where this site most often chooses its own way, and the demos say so: a sketchbook visited a few times can afford a slower page change than a tool used all day. For how the curves themselves work, there’s a guide to easing and springs.

05 · Touch

A finger isn't a pointer

A phone’s browser does its best to treat a finger like a mouse, and the gaps show: hover that sticks after a tap, a page that zooms into a small field, a drag the browser takes over as a scroll. These demos are best tried on a phone.

Touch: each rule without, and with

On a phone: tap each button, then look at the one you tapped.

✗ Without

a bare :hover

✓ With

@media (hover: hover)

A phone has no hover, but it fakes one on a tap, and keeps it until you tap somewhere else: the button stays lit as if still pointed at. Put hover styles behind the hover media query, which is what Tailwind's hover: already does.

On this site: All the hover here is Tailwind's, so it only shows where there's a pointer.

Rule

Two more that need a phone and a real page to show. Don’t focus a field by itself when a page opens on a touchscreen: the keyboard jumps up and covers half of what the visitor came to see. And a video meant to play by itself on an iPhone needs muted and playsinline, or it won’t start, or it opens full screen.

video.html
<video src="/loop.mp4" autoplay muted loop playsinline></video>

06 · Performance

Spend frames where they show

Smoothness is a budget: about 16 milliseconds a frame, for everything the page does. The performance rules are about not spending it where nobody’s looking.

Performance: each rule without, and with

Move the pointer over each pad (or click into it and use the arrow keys).

✗ Without

React renders: 0

useState on every move

✓ With

React renders: 0

a motion value, written straight to the element

A value that changes every frame doesn't need React to show it. Put in state, each move runs the component again, and everything inside it; held in a ref or a motion value, it's written straight to the element and React never hears about it.

On this site: Every value that follows the pointer or the scroll here is a motion value.

Rule

The rest are judgement calls. will-change and other tricks that move an element onto the graphics card help only something that’s really animating, and each one costs memory, so add them for a heavy animation and take them off when it stops. Pause videos the visitor has scrolled past; an iPhone that keeps decoding them all slows to a crawl. And let the page do less for a device that asks for less:

adapt.js
const lowData = navigator.connection?.saveData;
const lowPower = navigator.hardwareConcurrency <= 4;
if (lowData || lowPower) document.documentElement.dataset.lite = "";

This site learned the first of these the hard way: the performance detail measures what each layer cost, and what came off.

07 · Accessibility

Reachable by every hand

Accessibility rules are the ones a mouse user never notices breaking. Each of these is about someone reaching the interface another way: a keyboard, a screen reader, or a hand that holds a button down a moment too long.

Accessibility: each rule without, and with

Click “All”, then Tab to the Next field button. Then try ← and →.

✗ Without

every option a Tab stop

✓ With

one Tab stop, the arrows inside

A row of options is one control. Tab should step over it in one go, and the arrow keys move within it, as in a native radio group; otherwise a keyboard user tabs through every option of every group to get anywhere.

On this site: The pencil choices in these demos work this way.

Rule

One rule here has aged: the advice to draw focus rings with box-shadow. Browsers caught up, and an outline now follows rounded corners everywhere, so the demo shows the two side by side and says why the outline is the better choice today. Rules like these are written at a moment; it’s worth checking which still hold. Another I couldn’t make fail: the advice to reset gradient text when it’s selected. Selected in Chrome today, it stays readable as it is, so there’s no demo for it; check it in the browsers you support.

The rest, in short. A tooltip holds a few words, never a link or a button: it disappears when the pointer leaves, and so does anything inside it. Show images with <img> and an alt, so they can be read aloud and copied, and give a picture built from HTML a name with role="img" and aria-label. And a favicon can follow the visitor’s light or dark setting, as an SVG with its own media query:

icon.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  <style>
    path { fill: #0b0b0b; }
    @media (prefers-color-scheme: dark) { path { fill: #f5f3f1; } }
  </style>
  <path d="…" />
</svg>

08 · Design

The interface as a conversation

The last rules are about how an interface talks back: how fast it answers, where it answers, and what it says when there’s nothing to show.

Design: each rule without, and with

Click Save in each. Then switch the server to fail and click again.

✗ Without

waits for the server

✓ With

changes at once, undone if it fails

Most requests succeed, so show the result straight away and let the network catch up. When one does fail, put things back as they were, and say so beside the thing that changed.

Rule

One rule here can’t be shown in a box: when a page needs the visitor to sign in, send them to the sign-in page from the server, before anything renders. Done in the browser, the page they asked for flashes up first and then jumps away.

  • Make things work the way they look: a label focuses its field, a form submits on Enter, an icon in a field is part of the field.
  • Keep text still: no weight changes on hover, tabular figures for numbers that change.
  • Keep motion fast and small: under about 200ms for anything you click, scale in proportion, and none at all for what’s done all day.
  • Stop what nobody sees: loops off screen, videos scrolled past, re-renders for values that only need to move.
  • Reach everyone: one Tab stop per control, a name on every icon, no reason hidden in a tooltip.
  • Answer where the eye is, and straight away: optimistic changes, feedback on the button pressed.
  • Check the rules too: some were right when written and aren’t now.