Patterns
Component decision guide - when to reach for which piece, with live examples of each pattern in context.
Select vs. Dropdown Menu#
Both open a list on click - but they model different interactions. Select carries a value (part of a form, trigger echoes the selection). Dropdown Menu triggers actions (commands run, trigger stays fixed).
Click an option - the trigger updates to echo your choice. Correct for plan, country, sort order, or any form field.
The trigger always reads "My account". Clicking an item runs a command - nothing is stored in the trigger.
Click any option. The trigger stays "Plan" - users can't tell what's selected, and the value can't be read as a form field.
Quick smell test: should the trigger echo whatever was picked? Use Select. Should it stay the same label regardless? Use Dropdown Menu.
Dialog vs. Toast#
Dialog demands a decision - it blocks the page until the user acts. Toast is a passive observer - it confirms what already happened and dismisses itself.
A destructive action needs explicit confirmation. The page blocks until the user decides - cancel or delete.
The save already happened. Toast confirms it without interrupting the user's flow - it disappears on its own.
Never use a Toast to report an error the user must act on - those get missed. Use a Dialog, or inline validation next to the field instead.
Tooltip - and when to escalate#
Tooltips are supplementary labels, not content containers - they follow the WAI-ARIA tooltip role strictly. Hover the toolbar: the bubble glides between triggers via TooltipGroup.
Hover any button. Labels clarify icons without adding visual weight to the layout.
- Never put a link, button, or form inside a Tooltip - screen readers won’t reach it and pointer users can’t reliably click it.
- If the overlay needs to be clickable, use a Dropdown Menu (supports hover open).
- If it needs a form or a destructive action, escalate to a Dialog.
Details that make interfaces feel better#
Sourced from Details that make interfaces feel better by Jakub Krehel. Worth reading in full.
No single detail here is a feature - each one is a fraction of a second, a pixel, a rendering quirk. Stacked, they’re the difference between a UI that was built and one that was designed.
Proportional
Tabular
Watch the left counter: proportional digits shift width as values change (narrow 1 vs wide 9). The right stays locked. font-variant-numeric: tabular-nums makes every digit equal-width.
Concentric ✓
Mismatched ✗
outer radius − gap = inner radius. With p-2 (8px) and a rounded-2xl (16px) wrapper, the inner surface wants rounded-lg (8px). Mismatching makes corners look accidental.
Opacity only
Opacity + scale
Hover each button. Opacity-only pops the icon in abruptly. Adding scale makes the entry feel physical - the icon grows into place rather than materialising out of nowhere.
Solid border
Layered shadows
Layered box-shadows with rgba transparency adapt to any background. Solid borders assume a fixed surface colour; shadows cooperate with whatever is underneath.
Without
With 1px inset outline
A 1px inset outline at 10% opacity defines the edge of images and gradients against any background - especially on light-on-light or dark-on-dark surfaces where the boundary would otherwise dissolve.
Default
Ship better products faster
text-balance
Ship better products faster
Distributes a heading's text evenly across lines, preventing a short orphaned word on the last line. Pair with text-wrap: pretty on body copy.
Ag
subpixel
Ag
antialiased
-webkit-font-smoothing: antialiased switches macOS from subpixel to grayscale rendering - slightly thinner, crisper strokes. Apply once on the root; already set in globals.css.
Keyframe - hover out mid-way
Transition - hover out mid-way
Hover out of each bar mid-way. The keyframe bar snaps back to zero - it can't be interrupted. The transition bar reverses smoothly from wherever it is.
Items cascade in with 75 ms delays - enter takes 350 ms. Exit fires all at once in 180 ms with no stagger. Exits should be faster and quieter than entrances.
A trailing arrow shifts the button's visual weight right - equal padding (24/24) looks off-balance. Reducing the right side to 18px restores the feel. Toggle Show Padding to measure.
Built on the principles of make-interfaces-feel-better, the skill by Jakub Krehel that we recommend installing alongside Koala.