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.
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.
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:
Click the word “Email”, not the box.
✗ Without
<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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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 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.
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.
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:
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.
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.
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:
<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.
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.
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.