UI/UX Best Practices
Clarity beats cleverness
Users don't come to learn your UI; they come to get something done. Creative but opaque interactions kill function.
Button labels should carry verb + noun ('Download report' > 'Continue'). Icons alone are ambiguous; pair with text labels. Prefer everyday language over jargon.
'Confirm delete' instead of 'OK', 'Cancel' instead of 'Discard' clarifies intent.
Use progressive disclosure
Showing everything at once creates cognitive load. Hiding options the user doesn't need right now preserves focus.
Hide advanced settings behind 'More'. Focus each step of a multi-step form on one goal. Keep power-user features behind shortcuts or menus.
Linear: task creation is tiny; the expanded textarea reveals priority/assignee/label fields.
Don't neglect empty states
A blank page makes new users feel lost. A good empty state teaches and triggers the first action.
Illustration + short description + primary action. Use 'Create your first project' instead of 'No data yet'. Offer one-click sample data.
Notion: new workspaces present 'Start from a template' and 'Blank page' to drive users to first value fast.
Show skeletons or progress for loading states
Users who think the screen 'froze' refresh or leave. Uncertainty is more painful than the actual wait.
Show skeleton/spinner for operations > 100ms. Show % progress for > 1s. Optimistic UI: reflect the action immediately, reconcile when the server responds.
GitHub: PR merge button becomes 'Merging...' with animation; after ~15s it flips to 'Merged'.
Error messages should guide, not blame
'Invalid input' tells the user nothing. A good error names the cause and suggests the fix.
Inline validation; focus the offending field. Message should include 'what's wrong' + 'how to fix'. Avoid 'Something went wrong'; write context-specific text. Log 4xx silently, alert on 5xx.
Wrong: 'Invalid password.' Right: 'Password must be at least 8 characters and include a number.'
Give visual feedback within 100ms
Interactions under 100ms feel instant; above that, delayed. Without visible acknowledgement, users click again.
Every clickable needs :active + hover styles. On submit, disable the button + show spinner. Use debounce/throttle before triggering heavy work.
Search box: 300ms debounce as the user types; don't fire a request per keystroke.
Design mobile-first — small screen isn't an afterthought
60% of global traffic is mobile. Designs that start on desktop get cramped on mobile and miss the real audience.
Prototype at 360px first. Touch targets ≥44×44px. Mind safe areas (iPhone notch, home indicator). Responsive type via clamp().
Bump a 40px hamburger in the navbar to 44px and add 12px padding to ease tapping.
Establish visual hierarchy — nothing can be equally important
With 5 CTAs, 3 colors, and 4 font weights on screen, the eye has nowhere to land. Without hierarchy, the UI is noise.
Exactly ONE primary action per screen; secondary/tertiary fade visually. Scale: heading ≥ subheading ≥ body ≥ meta. Whitespace is half the hierarchy.
In a modal, 'Yes, delete' is a solid red button; 'Cancel' is a ghost. Don't use two filled buttons.
Reduce choices (Hick's Law)
Decision time rises logarithmically with the number of options. 20 menu items are much slower than 3 grouped clusters.
Group menu items under categories. Push advanced options back. Remove anything that doesn't deliver immediate value in onboarding. A/B test.
VS Code command palette: one search box + fuzzy search to reach hundreds of commands without menu diving.
Balance undo and confirm
Confirm every action and friction explodes; confirm none and you'll cause accidental deletions. The right choice depends on reversibility.
For reversible actions (archive, move), show an undo toast (Gmail-style 5 sec). For irreversible ones (permanent delete, payment), require explicit confirm + countdown or typed keyword.
GitHub: deleting a repo requires typing the repo name; you can't click 'delete' by mistake.
Don't forget keyboard users — shortcuts for power users
Heavy users migrate to the keyboard. A product without shortcuts feels 'toy-like'; you lose the premium segment.
Global search: Cmd/Ctrl+K. Save: Cmd/Ctrl+S. Help: ?. Surface shortcuts in button tooltips. Test clashes against browser defaults.
Linear: G+I for Inbox, G+A for Active issues, C for new issue — keyboard-first throughout.
Support dark mode as a system rule
80%+ of users prefer dark mode. An app out of sync with system preference is labelled 'eye-hurting'.
Start from prefers-color-scheme. Persist the choice in localStorage with a manual override. Define color tokens in two modes keeping contrast ratios.
CSS custom properties: --color-text, --color-bg as primary tokens; values differ under [data-theme="dark"].