skip to content

[PRINCIPLES] /principles

These are my principles. Things I’ve learnt.

Each card is one rule, written the way I say it to myself. Every one has a live demo you can play with, one line on why, and where I learnt it. Most demos have the same controls: a Before and After switch, a slider and Replay.

Turns every demo into its still, calm version. If your system already asks for less motion, it is on for you.

[I] #motion

Motion feel

How things move decides whether an interface feels quick or sluggish, calm or fussy. These are the rules I check first.

go deeper:Ease Racetrack ↗

M1

I ease out by default.

300ms

ease-in

sluggish start

Menu ▾
Open
Rename
Delete

ease

fine

Menu ▾
Open
Rename
Delete

ease-out

answers at once

Menu ▾
Open
Rename
Delete

whyEase-out starts fast and lands soft, so the thing answers the moment you ask and still settles gently.

source: emilkowal.ski ↗ (opens in a new tab)

M2

I pick the ease by what the thing is doing.

700ms

A panel arrives

A panel leaves

A dot moves across

A progress bar fills

A loader spins

0 of 5 fit the job.

whyArriving eases out, going away eases in, moving across the screen eases both ways. Linear is only for loops and progress.

source: nngroup.com ↗ (opens in a new tab)

[+] more to play with
linear
ease-out-quint
ease-in-out-cubic
ease-in (avoid for UI)
spring-ish overshoot
Same distance, same duration. Only the curve changes. Press play and watch which ones feel like UI.

M3

My durations grow with distance.

90%
fixed 300ms
scaled 466ms

Short hops at 300ms feel slow; long ones feel rushed. I use about 160ms plus a bit per unit of distance.

whyA fixed time makes long moves look rushed and short ones look lazy. The speed should feel the same.

source: nngroup.com ↗ (opens in a new tab)

M4

I use springs for anything you can grab.

220
grab me

Resting on the left. Fling it, then grab it again before it lands. The spring stops in your hand.

whyA spring keeps the speed of your hand, so a flung card carries on and a caught card stops. A fixed curve can do neither.

source: joshwcomeau.com ↗ (opens in a new tab)

[+] more to play with
spring
ease-out
settles in
1150ms
overshoot
19.1%
A spring has no duration: stiffness and damping decide how long it takes and whether it overshoots. The bezier gets the same time, so only the feel differs. Low damping wobbles; high damping feels heavy.

M5

I stagger in small steps, with a cap.

60ms

13ms a step. The last tile lands at 900ms.

whyAround 30 to 50ms between items reads as one gesture. A cap on the total means the last item never keeps you waiting.

source: gsap.com ↗ (opens in a new tab)

[+] more to play with
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

last tile lands at 1320ms: too slow, people wait for it

Stagger turns 24 things appearing into one gesture with a direction. Past about 60ms per item the whole group takes too long to arrive; center-out draws the eye to the middle.

M6

My motion explains. It doesn’t decorate.

shop

cart0

Grey wool scarf

The item travels to the cart and the count bumps: you see where it went.

whyGood motion shows where something went or what changed. If it tells you nothing, it is in the way.

source: nngroup.com ↗ (opens in a new tab)

M7

I don’t animate things you do a hundred times a day.

300ms

Opened 0 times. Time spent watching it: 0.0s. Try ten times with ⌘K or Ctrl+K.

whyA 300ms opening is charming once and a tax the hundredth time. Keyboard actions should feel instant.

source: emilkowal.ski ↗ (opens in a new tab)

M8

I animate transform and opacity, nothing else.

60

paused · layout for 60 bars, every frame. A fast laptop hides it; a phone won’t.

whyThose two skip layout and paint, so the GPU can do the work. Width, top or margin make the browser redo the page every frame.

source: web.dev ↗ (opens in a new tab)

[II] #blur

Blur

Blur is focus, speed and depth. It is also one of the most expensive things a browser can draw, so I use it on purpose.

go deeper:Blur Lab ↗

B1

I add motion blur to fast moves.

700ms
01

Slide 1 of 5. Peak blur on the last move: 0px. Slow it right down to see the blur follow the speed.

whyA little blur along the direction of travel tells your eye it moved fast, like a camera would. When it slows, the blur goes.

source: MDN ↗ (opens in a new tab)

B2

I blur text in instead of only fading it.

10px
fade

Things I’ve learnt

slide

Things I’ve learnt

blur in

Things I’ve learnt

The blur-in has no travel, so it still feels calm when someone asks for less motion.

whyText that sharpens into place feels like it comes into focus. It reads sooner than a fade and needs no travel.

source: emilkowal.ski ↗ (opens in a new tab)

B3

I blur the background to show what matters.

4px

The page is still there, just out of the way.

whyA light blur behind a dialog says “only this, for now” without hiding where you were. Too much and you lose your place.

source: MDN ↗ (opens in a new tab)

B4

My frosted glass always has something to blur, and a tint.

70%
Frosted headerMenu

Busy content

Scroll this panel. The header blurs whatever passes under it.

Header text over white, worst case: 7.17:1, passes AA.

whyBackdrop blur over a flat colour does nothing. Over busy content it needs a tint, or the text on top fails contrast.

source: nngroup.com ↗ (opens in a new tab)

B5

I budget blur, because it is expensive.

6

paused · 6 real blurs re-sampled every frame.

whyEvery blurred pixel samples its neighbours, every frame. A few big blurred layers can drop a phone below 60fps, so I keep a cheap fallback.

source: web.dev ↗ (opens in a new tab)

[III] #transitions

Transitions

A transition is the bridge between two states. A good one tells you how they are related, so you never lose your place.

go deeper:Transition Deck ↗

T1

I crossfade swaps and slide for direction.

260ms

Details

tabs: crossfade

step 1/3

Your name

steps: slide by direction

whyTabs are siblings, so they swap in place. Steps in a flow go forward and back, so they slide that way.

source: nngroup.com ↗ (opens in a new tab)

T2

I keep your place with shared elements.

550ms

whyWhen a card grows into its detail view, you never lose what you tapped. FLIP makes it cheap: measure first, measure last, invert, play.

source: gsap.com ↗ (opens in a new tab)

T3

I use view transitions between pages.

450ms

/work

Linen shirt
Canvas bag

The browser snapshots both states and morphs the square and the title between them.

whyThe browser snapshots both views and morphs what they share. It is built in, and where it isn’t supported you get a plain swap.

source: MDN ↗ (opens in a new tab)

T4

I design the exit as well as the entrance.

320ms

    Add a toast. It leaves on its own after three seconds.

    Enter 320ms, exit 224ms. The exit is quicker: going away should never hold you up.

    whyThings that vanish in a blink feel like errors. A short exit, quicker than the entrance, says where it went.

    source: MDN ↗ (opens in a new tab)

    T5

    My transitions start where you clicked.

    350ms

    whyA menu that grows out of its button looks like it came from there. From the centre it looks like it came from nowhere.

    source: MDN ↗ (opens in a new tab)

    [IV] #scroll

    Scroll

    Scroll belongs to the person scrolling. I can react to it, but I never take it away, and I never let it hide what they came for.

    S1

    I choose scroll-linked or scroll-triggered on purpose.

    Scroll this panel ↓ then back up.

    01

    Arrive

    02

    Orient

    03

    Explain

    04

    Show

    05

    Prove

    06

    Act

    Linked: the cards follow your scroll both ways. The bar on top is linked too.

    whyLinked follows your scroll both ways, which suits progress. Triggered plays once, which suits reveals. CSS can now do linked with no JavaScript.

    source: gsap.com ↗ (opens in a new tab)

    [+] more to play with
    0% read

    Scroll down ↓

    01

    Arrive

    02

    Orient

    03

    Explain

    04

    Show

    05

    Prove

    06

    Act

    Scroll inside the panel, not the page. The bar is a pure function of scroll position, so it is exact in both directions; the reveals are an IntersectionObserver rooted on the panel. Toggle 'once' to see why re-hiding on scroll-up feels fidgety.

    S2

    I smooth the scroll, but I never hijack it.

    0.12

    01 / 06

    One

    Use a mouse wheel or trackpad over this panel.

    02 / 06

    Two

    Use a mouse wheel or trackpad over this panel.

    03 / 06

    Three

    Use a mouse wheel or trackpad over this panel.

    04 / 06

    Four

    Use a mouse wheel or trackpad over this panel.

    05 / 06

    Five

    Use a mouse wheel or trackpad over this panel.

    06 / 06

    Six

    Use a mouse wheel or trackpad over this panel.

    Smoothed: every scroll counts, eased at 0.12 a frame. Lower is floatier.

    whySmoothing keeps your input and softens it. Hijacking throws your input away and plays its own. People hate the second.

    source: nngroup.com ↗ (opens in a new tab)

    S3

    My reveals never hide content.

    card 01

    Content you came here to read.

    card 02

    Content you came here to read.

    card 03

    Content you came here to read.

    card 04

    Content you came here to read.

    card 05

    Content you came here to read.

    card 06

    Content you came here to read.

    card 07

    Content you came here to read.

    card 08

    Content you came here to read.

    card 09

    Content you came here to read.

    card 10

    Content you came here to read.

    card 11

    Content you came here to read.

    card 12

    Content you came here to read.

    card 13

    Content you came here to read.

    card 14

    Content you came here to read.

    card 15

    Content you came here to read.

    card 16

    Content you came here to read.

    Press Fling. It jumps the panel 3000px in one go, like a hard flick on a phone.

    whyIf a fast fling skips the trigger, hidden stays hidden. Content is visible by default and motion is only an extra.

    source: MDN ↗ (opens in a new tab)

    S4

    I give heavy scenes a still image and remove them off screen.

    Still image showing. The live scene starts when it is on screen and the page has room for it.

    whyA still shows at once and costs nothing. The live scene starts when it is in view and stops when it isn’t, so the page stays quick.

    source: MDN ↗ (opens in a new tab)

    R1

    My reduced motion removes the travel, not the feedback.

    What reduced motion keeps and removes
    Full motionReduced
    Slides in from belowFades in
    Parallax and scroll scrubStatic, final layout
    Looping backgroundOne composed still
    Button press scaleColour and label change
    SpinnerKept: it is feedback

    whyPeople who turn motion down still need to know something happened. I keep fades and state changes and drop the flying.

    source: MDN ↗ (opens in a new tab)

    [+] more to play with
    A card that enters with motion, or with a fade when motion is reduced.
    With reduced motion on, the card fades instead of flying. Same information, no vestibular cost.

    R2

    Anything that loops, you can stop.

    60px/s

    Frames drawn: 0. Frozen: off screen.

    whyWCAG asks for a pause on anything that moves for more than five seconds. I also stop loops the moment they leave the screen.

    source: w3.org ↗ (opens in a new tab)

    [V] #visual

    Visual design

    The rules I check before any motion goes in. Each one is a still you can read in a second. If a page fails these, no animation will save it.

    V2

    I set type from a scale, sized for reading.

    SCALE 16PX × 1.25

    • 39Reading comes first
    • 31Reading comes first
    • 25Reading comes first
    • 20Reading comes first
    • 16Reading comes first
    • 13Reading comes first

    MEASURE 60 TO 70 CHARACTERS

    Body text sits at 17px with 1.6 line height. A line that runs past about seventy characters makes your eye hunt for the start of the next one, so I cap the measure.

    whyA fixed ratio keeps sizes related, and a capped line length keeps reading easy.

    source: typescale.com ↗ (opens in a new tab)

    V4

    I measure contrast, I don't guess it.

    WCAG RATIO ON #080808

    • Body text in ink18.4:1AAA
    • Body text in soft9.2:1AAA
    • Body text in dim5.8:1AA
    • Body text in too dim2.7:1FAIL
    • Body text in steel1.4:1FAIL

    Body text needs 4.5:1. I check the number, not my screen.

    whyScreens and eyes differ. The ratio doesn't, so it's the one I trust.

    source: WCAG 2.2 · contrast ↗ (opens in a new tab)

    V5

    I use space to group things.

    EVEN GAPS

    12px in, 12px out. Which label goes with which box?

    INNER < OUTER

    4px in, 20px out. Each label hugs its box.

    whyThings close together read as one. Less space inside a group than between groups does the grouping for free.

    source: nngroup · proximity ↗ (opens in a new tab)

    V6

    I use depth to show what floats.

    THREE LEVELS

    A page, a card raised one level with a soft shadow, and a menu raised two levels with a lighter surface and a deeper shadow.

    whyShadow and a lighter surface say this sits above the page, so people know what they can dismiss.

    source: material 3 · elevation ↗ (opens in a new tab)

    [VI] #ux

    Laws of UX

    Twelve findings about how people use screens, each one something you can try right here. A law of UX is a pattern that research keeps finding, not a rule someone made up. Play the demo first, then read why.

    U1

    I make targets big and close (Fitts).

    Press START, then hit the target as fast as you can.

    SMALL+FAR -- · BIG+CLOSE --

    whyThe time to hit a target grows with distance and shrinks with size. Small and far is always slow.

    source: lawsofux · Fitts's law ↗ (opens in a new tab)

    U2

    I cut the choices to speed up the decision (Hick).

    I’ll name one command. Find it and click it.

    FLAT -- · GROUPED -- · TYPED --

    whyEvery extra option adds decision time. Grouping or search turns 24 choices into a few.

    source: lawsofux · Hick's law ↗ (opens in a new tab)

    U3

    I put things where people already look (Jakob).

    A shop page will appear. Find the cart.

    MY IDEA -- · WHERE PEOPLE LOOK --

    whyPeople spend most of their time on other sites. They bring those habits to mine.

    source: lawsofux · Jakob's law ↗ (opens in a new tab)

    U4

    I answer within 400ms (Doherty).

    900ms

    900ms you notice the wait

    • Apple
    • Apricot
    • Banana
    • Blackberry
    • Cherry
    • Damson
    • Fig
    • Gooseberry
    • Grape
    • Lemon

    UP TO DATE · slide under 400ms and it feels like the page is keeping up

    whyUnder about 400ms people stay in flow. Past a second, their mind wanders off.

    source: lawsofux · Doherty threshold ↗ (opens in a new tab)

    U6

    I make the one thing I want seen different (von Restorff).

     Basic£4
    MOST PICKEDPlus£9
     Pro£19

    Only black and white. The difference does the work, not colour.

    whyThe odd one out is the one people remember. It works in black and white, no colour needed.

    source: lawsofux · von Restorff effect ↗ (opens in a new tab)

    U8

    I join steps with a line and keep shapes simple (Prägnanz).

    1. Cart
    2. Address
    3. Pay
    4. Done

    FOUR ODD THINGS

    whyConnected things read as one path, and the eye prefers the simplest shape it can find.

    source: lawsofux · Prägnanz ↗ (opens in a new tab)

    U9

    I chunk long strings (Miller).

    I’ll show a 12-digit code for 4 seconds. Then type it back.

    LONG STRING -- · CHUNKS --

    whyWorking memory holds only a few things at once. Four groups of four is far easier than twelve loose digits.

    source: lawsofux · Miller's law ↗ (opens in a new tab)

    U10

    I take on the complexity so you don't have to (Tesler).

    Enter: 12 Harbour Street, Northgate 4012

    0 KEYS NOW · YOU DO IT -- · I DO IT --

    whySome complexity can't be removed, only moved. I'd rather the system carry it than the person.

    source: lawsofux · Tesler's law ↗ (opens in a new tab)

    U11

    I show progress and leave a nudge (goal gradient, Zeigarnik).

    Same five tasks. B counts the two you already did.

    A · FROM ZERO

    0 of 5 · 0%

    B · HEAD START

    2 of 7 · 29%

    B starts at 29%. People push harder when the end looks close.

    whyPeople speed up near the finish, and an unfinished task keeps tugging at them.

    source: lawsofux · goal gradient ↗ (opens in a new tab)

    U12

    I put key items first and last, and polish what matters (serial position).

    A menu of nine shows for 3 seconds. Then tick the ones you remember.

    Most people keep the first and the last. So Home goes first and Contact goes last.

    whyThe ends of a list stick best. And a design that looks good is judged easier to use, so polish is not decoration.

    source: lawsofux · serial position ↗ (opens in a new tab)

    [VII] #states

    States and heuristics

    A screen is never just its happy path. It is also loading, empty, broken and waiting for you. These are the checks I run on every state, starting with Nielsen’s ten heuristics: rules of thumb for spotting usability problems.

    H1

    I audit every screen against Nielsen's ten.

    ERR_0x3F: PURGE ENTITY?

    See docs section 4.2.

    Proceed

    SCORE 0/5

    ALL TEN HEURISTICS
    1. Visibility of system status
    2. Match between the system and the real world
    3. User control and freedom
    4. Consistency and standards
    5. Error prevention
    6. Recognition rather than recall
    7. Flexibility and efficiency of use
    8. Aesthetic and minimalist design
    9. Help users recognise, diagnose and recover from errors
    10. Help and documentation

    whyTen plain questions catch most problems before anyone tests the screen. Fix the dialog and watch the score.

    source: nngroup · 10 heuristics ↗ (opens in a new tab)

    H2

    I give every press feedback.

    1200ms

    NOTHING

     

    PRESS ONLY

     

    PRESS + BUSY + DONE

     

    Watch the press count. With no feedback people press again.

    whyIf nothing moves, people press again. A pressed state, a busy state and a done state stop the double order.

    source: nngroup · response times ↗ (opens in a new tab)

    H3

    I make things look like what they do.

    Tap the ones you think you could click. Then reveal.

    Dashed border after reveal means a wrong guess.

    whyAn affordance is a hint about what a thing does. If it looks clickable, it should click, and the other way round.

    source: nngroup · clickable elements ↗ (opens in a new tab)

    H4

    I use skeletons, not spinners.

    1500ms
    • AnaShipped the new onboarding. Three steps instead of seven.
    • TheoThe pricing table now reads well on phones.
    • MiaI cut the hero video. The page loads in half the time.

    The page has its shape already. The wait feels shorter.

    whyA skeleton shows the shape of what's coming, so the page feels closer to done.

    source: nngroup · skeleton screens ↗ (opens in a new tab)

    H5

    I design empty and error states as real screens.

    No projects yet

    Projects keep your files together. Make your first one in a minute.

    The error says what to do, and your typing survives.

    whyAn empty screen is the first thing new people see. An error should say what happened, what to do, and keep what they typed.

    source: nngroup · empty states ↗ (opens in a new tab)

    H6

    I make it feel fast first (optimistic UI).

    • Book the venue
    • Send the brief

    It shows up now and saves behind the scenes. If it fails, I say so and give it back.

    whyOptimistic means I show the result before the server confirms, then own up if it fails. Most saves succeed, so most waits vanish.

    source: smashing · optimistic UI ↗ (opens in a new tab)

    [✓] #checklist

    My checklist

    Every rule on this page, plain. I run down it before I call something done. Copy it into a pull request or a note.

    I. Motion feel

    • I ease out by default.
    • I pick the ease by what the thing is doing.
    • My durations grow with distance.
    • I use springs for anything you can grab.
    • I stagger in small steps, with a cap.
    • My motion explains. It doesn’t decorate.
    • I don’t animate things you do a hundred times a day.
    • I animate transform and opacity, nothing else.

    II. Blur

    • I add motion blur to fast moves.
    • I blur text in instead of only fading it.
    • I blur the background to show what matters.
    • My frosted glass always has something to blur, and a tint.
    • I budget blur, because it is expensive.

    III. Transitions

    • I crossfade swaps and slide for direction.
    • I keep your place with shared elements.
    • I use view transitions between pages.
    • I design the exit as well as the entrance.
    • My transitions start where you clicked.

    IV. Scroll

    • I choose scroll-linked or scroll-triggered on purpose.
    • I smooth the scroll, but I never hijack it.
    • My reveals never hide content.
    • I give heavy scenes a still image and remove them off screen.
    • My reduced motion removes the travel, not the feedback.
    • Anything that loops, you can stop.

    V. Visual design

    • I give each view one big thing.
    • I set type from a scale, sized for reading.
    • I keep it mostly neutral with one accent.
    • I measure contrast, I don't guess it.
    • I use space to group things.
    • I use depth to show what floats.
    • I keep corners concentric and radii few.

    VI. Laws of UX

    • I make targets big and close (Fitts).
    • I cut the choices to speed up the decision (Hick).
    • I put things where people already look (Jakob).
    • I answer within 400ms (Doherty).
    • I end on a high (peak-end).
    • I make the one thing I want seen different (von Restorff).
    • I group with space, shape and regions (Gestalt).
    • I join steps with a line and keep shapes simple (Prägnanz).
    • I chunk long strings (Miller).
    • I take on the complexity so you don't have to (Tesler).
    • I show progress and leave a nudge (goal gradient, Zeigarnik).
    • I put key items first and last, and polish what matters (serial position).

    VII. States and heuristics

    • I audit every screen against Nielsen's ten.
    • I give every press feedback.
    • I make things look like what they do.
    • I use skeletons, not spinners.
    • I design empty and error states as real screens.
    • I make it feel fast first (optimistic UI).