Chapter 01
Anatomy of a button
A button isn't a clickable rectangle. It's a promise of action, a small physical contract between finger and screen. We take it apart here, piece by piece.
There’s a scene in Ratatouille where the critic tastes the dish and for a moment becomes a child again. A well-made button does something similar: you don’t notice it, yet when you press it you remember that real things have weight.
In this first chapter we open the button the way you open a watch: what makes it feel pressable, when motion helps and when it gets in the way, why a spinning circle is almost always the wrong answer.
What makes a button “tactile”
Three things, in order of importance:
- A believable surface. A shadow that suggests lift from the page. Never flat, never floating mid-air.
- A reaction to touch. Pressing it must produce a change — not another color, but less space. It compresses, like a real soft surface would.
- An honest return. When you let go, it springs back with light inertia. Not instant (looks fake), not slow (looks broken).
Primary button · soft press
Three visible states: at rest (medium shadow, scale 1), on hover (slight lift + deeper shadow), on press (inset shadow + scale 0.97). The transition on transform and box-shadow uses the out-snappy curve for 180 ms — fast enough to feel responsive, slow enough to be perceived.
The secret is the press: scale drops to 0.97, not 0.95. A thousandth of a difference, but the first feels “pressed”, the second “broken”.
When the button becomes the action
The most tired pattern on the web is a button with a spinner inside it. It works, but it communicates the wrong thing: wait, I’m doing. More honest and more satisfying is to let the button itself become the action: it shrinks, works, and returns with an outcome.
Async button · morph & resolve
Three states: idle → loading → done. On the way to loading the button doesn’t show a spinner: it becomes a spinner — collapses into a circle, spins, then morphs into a check. The inline-size is animated with the out-snappy curve (280 ms): faster looks glitchy, slower looks clumsy.
The check path has an animated stroke-dasharray — the mark is traced in front of your eyes, not simply popped in. A free detail that changes the whole tone.
The art of asking for attention without demanding it
A page always has more things than it needs. The “real” button — the one asking for a decision — must emerge, but not shout. The magnetic pattern does exactly this: it sits there normally, but when the cursor approaches, it comes alive.
Magnetic button · gentle attention
The pull is dosed: strength: 0.4 — the button shifts by 40 % of the distance between its center and the cursor. More than that feels “sticky”, less and the effect is invisible. The snappy preset gives the return a small mechanical inertia.
The real trick isn’t the movement: it’s the halo. It activates on hover and suggests a magnetic field even before the cursor arrives. At rest, it’s just a button — it’s the relationship with the cursor that lights it up.
Confirmations that don’t break the flow
Opening a modal for “are you sure?” is an act of violence. It dims everything, blocks scrolling, hijacks focus. For a simple destructive action — deleting a note — it’s too much. The confirmation can live inside the same button.
In-place confirmation · no modal
Click the idle button → the pill transforms revealing Are you sure? + two inline actions. Red survives only on the verb Yes, delete: attention follows the critical action, not the whole container.
Auto-cancel after 4 seconds: if the user changes their mind, time itself does the work. No “sticky states”, no forced decisions. The confirmation is a window of intent, not a modal menu.
Tooltips that grow from the button, not stick next to it
Tooltips that “pop up” from a magic point near the cursor are a tired pattern. More satisfying is to let them grow from the button itself — transform-origin anchored to the edge, scale from 0.6 to 1, opacity 0 → 1. Visually, the tooltip seems to have come out of the button, not appeared next to it.
Icon button · growing tooltip
Trigger: hover with 280 ms delay (users who graze the button don’t want a tooltip) + immediate on focus (keyboard users need the label now). No close delay: it must disappear before the next hover.
The tooltip is purely decorative. The real label lives in aria-label: screen readers must always read the action, not wait for a hover that will never come.
Simulated haptics
The phone vibrates when you tap it; the browser doesn’t. But the brain accepts a visual vibration as a substitute — if it’s brief enough. A micro-bounce on the icon (320 ms, from 1 to 1.45 to 1) plus a radial impact wave is enough to make the click feel like a physical event, not a color change.
Favorite toggle · visual haptic feedback
Three things happen in the same instant: the icon’s fill soaks with red, the icon bounces with scale 1 → 1.45 → 0.9 → 1, and a radial trail expands behind it. Three redundant signals, but the brain integrates them into one sensation of touch.
Bonus: navigator.vibrate(8) calls hardware vibration on mobile when the browser exposes it. An eighth of a millisecond: perceptible but not annoying. The micro-bounce keeps working on desktop too, where vibration isn’t available.
A button isn’t a clickable rectangle. It’s a small physical contract between finger and screen. Honoring it is the start of everything else.