Best practices for UI design: A web checklist with tests

Contents
Good UI design comes down to a short list of practices you can check on a screen: a visible hierarchy, enough contrast, consistent components, clear feedback and forgiving forms. This checklist gives each practice a do/don't pair and a way to verify it before release, with WCAG 2.2 criteria named where they apply.
The checklist at a glance
Most UI defects that reach production can be caught before release with a short, checkable list. The usual misses are body text under 4.5:1 contrast, tap targets too small to hit, placeholder text standing in for labels, and components that drift from the design system.
We recommend building a design system component library with design tokens before auditing individual screens, because each rule below is cheaper to fix once in a shared component than on every page.
A one-off marketing site is the exception, since a per-screen review is enough there.
If you want an outside pass, Netguru's UX review service audits screens against these rules.
The checklist below maps each practice to what a reviewer sees and how to verify it against WCAG 2.2:
| Practice | On screen: do / don't | How to check |
|---|---|---|
| Visual hierarchy | Do: one primary action per view, ranked by size, weight and spacing. Don't: three equal-weight buttons. | Squint test on a blurred screenshot; five-second test |
| Contrast ratio | Do: 4.5:1 for body text (SC 1.4.3, AA) and 3:1 for input borders and icons (SC 1.4.11, AA). Don't: ship the brand palette unchecked. | Contrast checker run against the token values |
| Target size | Do: at least 24 by 24 CSS px (SC 2.5.8, AA). Don't: 16 px icon buttons packed edge to edge. | DevTools measurement; automated audit |
| Labels and inline validation | Do: visible label, error message beside the field (SC 3.3.1, A). Don't: placeholder as the only label. | Keyboard-only pass; screen reader pass |
| Focus visibility | Do: keep focused elements clear of sticky headers (SC 2.4.11, AA). Don't: let a fixed bar cover the focus ring. | Keyboard tab-through with the sticky header in view |
| Consistency | Do: take components and values from the library and tokens. Don't: hard-code one-off hex values. | Lint for hard-coded values; diff Figma tokens against CSS |
| Feedback | Do: state why a button is disabled. Don't: grey out a submit button with no reason. | Moderated usability test on the primary task |
UI design principles: Start with the primary task
Decide the primary task of each screen first, then judge every later UI design principle by whether it helps users finish that task. On a checkout screen the task is paying; on a dashboard it might be spotting the one metric that moved.
The primary task sets visual hierarchy. The element that completes it gets the largest size, strongest contrast, and most space, and everything else yields. Do: write the task as a verb phrase in the ticket ("confirm and pay"). Don't: give a "Save draft" link the same weight as the pay button.
User control protects that task when users slip: a visible way out of every step and an undo for reversible actions. It maps to two of Nielsen's 10 usability heuristics, user control and freedom, and flexibility and efficiency of use.
Check it with a five-second test: ask participants what the screen is for. If answers split, two elements are competing for first attention.
How do you build a clear visual hierarchy?
Build a clear visual hierarchy by ranking every element against the screen's primary task, then giving each rank one distinct treatment in size, weight, contrast, and spacing. Lower ranks still need to meet the contrast minimum covered in the next section.
Two checks catch most hierarchy errors, and each has a blind spot:
- Squint test: blur the screen and see which elements still stand out. It shows dominance but hides illegible low-contrast text and says nothing about whether labels make sense.
- Five-second test: ask what the screen is for and what users would click first. It measures first impressions, not task completion or repeat use.
Cover the gaps with a short moderated usability session, described in the testing section.
Icon-only buttons are a common failure. They look tidy but hide the action, so the squint test passes them while the five-second test shows users guessing. Add a visible label unless the icon is universal (NN/g on icon usability), and keep icons at the 3:1 contrast minimum for UI parts.
Progressive disclosure vs power-user tools
Progressive disclosure helps when a screen carries rare or advanced controls, such as a signup form with optional company fields collapsed by default. It hurts when users repeat the same task daily, because an accordion adds a click to every pass.
We recommend keeping frequent actions visible and collapsing only the rare ones. Persist each user's expanded state, and add keyboard shortcuts for repeat work.
To check it, time returning users on their most common task. If a collapsed control slows them, move it out of the accordion.
How much contrast does a UI need?
A UI needs a contrast ratio of at least 4.5:1 for body text and 3:1 for component boundaries, icons and focus states. Both thresholds are Level AA in WCAG 2.2 (SC 1.4.3 and SC 1.4.11). Large text (24 CSS px, or 18.66 px bold) only needs 3:1.
The 60-30-10 rule (neutral base, secondary color, accent) is a layout heuristic, not part of any standard. It says nothing about contrast, and the 10% accent is where brand palettes most often fail.
A worked example, assuming a brand accent of #FF6B35 on a white background. Ratios come from the WCAG relative-luminance formula, rounded to one decimal:
| Shade | Hex | Ratio on white | Text (4.5:1) | UI parts (3:1) |
|---|---|---|---|---|
| Brand accent | #FF6B35 | 2.8:1 | Fail | Fail |
| Darker step | #EA580C | 3.6:1 | Fail | Pass |
| Darkest step | #C2410C | 5.2:1 | Pass | Pass |
The original accent fails both thresholds, so it cannot be a link color, an input border, or a button fill that must stand out from the page. #EA580C works for borders and icons. Text links and white button labels need #C2410C.
We recommend storing the passing shade as its own design token, such as color-accent-text, and keeping the original for large decorative areas. Verify it with a contrast checker, such as a browser DevTools color picker or an automated audit in CI, and test hover, disabled, and focus states separately.
How should you set typography and spacing?
Set typography with a modular type scale, one or two font families and 16 CSS px body text, then space everything on an 8-point grid. Those defaults are heuristics, not standards, and dense interfaces bend each of them.
Defaults to start from:
- Type scale: pick one ratio (1.25 is a common starting point) and 5-6 steps. Store them as design tokens so components never hard-code a size.
- Line length: 45-75 characters is the usual typographic range. SC 1.4.8 (Level AAA) caps it at 80.
- Line height: 1.5 for body copy.
Where each heuristic bends in dense dashboards:
| Heuristic | Where it breaks | Adjustment |
|---|---|---|
| Two font families | Data tables need monospace IDs and aligned figures | Keep the second family for headings and body. Add font-variant-numeric: tabular-nums before adding a third family |
| 8-point grid | Compact rows and 16 px icons sit awkwardly on 8 px steps | Allow a 4 px half-step in a compact density token. Take line heights from the type scale, not the grid |
| 16 px body | Dense tables often run at 14 px | Accept it only if the zoom check below passes |
Two checks under WCAG 2.2 catch what the grid hides:
- Zoom to 200%. SC 1.4.4 (Level AA) requires text to resize to 200% with no loss of content or function.
- Apply the text-spacing override. SC 1.4.12 (Level AA) sets line height to 1.5, paragraph spacing to 2x font size, letter spacing to 0.12em and word spacing to 0.16em. Nothing may clip or overlap.
Grid-locked fixed heights are the usual failure. Rows and buttons sized to exactly 32 px with overflow: hidden clip under the override, so use min-height and let padding come from tokens.
How do you keep components consistent across a product?
Keep components consistent by making design tokens the only source of visual values and enforcing them in the component library and CI. Drift then fails a build before it reaches design review.

Nielsen Norman Group separates internal consistency, where the same action looks and behaves the same across your product, from external consistency, where you match conventions users know from other products. Tokens and components enforce the first. The second is a design decision: follow platform patterns such as Material Design 3 for controls users already understand, and diverge only for deliberate brand expression.
The enforcement chain, in order of work:
- Single token source. Store tokens as JSON in the W3C Design Tokens Community Group format, in one repo. Style Dictionary compiles them to CSS custom properties, and a sync step (Tokens Studio or the Figma Variables API) generates Figma variables from the same file. Value changes go through pull requests, never through Figma alone.
- Tiered naming. Material Design 3 splits tokens into reference, system, and component levels. The Microsoft Fluent design system uses global and alias tokens. Components should reference only the semantic tier (color.text.primary, not blue-600), so a rebrand or dark theme changes one mapping.
- Component library. Ship buttons, inputs, and dialogs from one design system component library documented in Storybook, with visual regression tests and an axe accessibility check on every story. Product teams compose components and do not restyle them.
- Lint rules. Stylelint with the declaration-strict-value plugin rejects raw hex colors and px spacing in properties like color and margin. An ESLint rule bans inline style props. Both run in CI and block the merge.
A token source that designers and developers both treat as the record ends most arguments about which blue is correct. The Atlas design system Netguru built for Joystream, an open-source video platform behind several apps, runs on the same idea: 175 design tokens covering colour, typography and animation sit under 190+ global components, documented across 100+ pages.
This setup is too heavy for one team shipping one app. Start with CSS custom properties and the Stylelint rule, and add the Figma sync once several teams or designers edit the same components. Counting raw-value lint violations per release gives you a drift metric you can track.
What makes navigation clear?
Navigation is clear when users can predict where a label leads, see where they are, and reach any primary task within two menu levels. The two-level limit is our recommendation. Deeper trees are defensible for large catalogs or admin consoles, where search should back them up.
WCAG 2.2 sets the floor. SC 3.2.3 Consistent Navigation (Level AA) keeps repeated navigation in the same relative order across pages. SC 3.2.6 Consistent Help (Level A) keeps help links in the same place. The Apple Human Interface Guidelines reserve tab bars for top-level destinations, never for actions.
Three rules make it checkable in a design review:
- Labels: name the destination in the user's words ("Billing", not "Resources"). Two labels that could hold the same task mean the structure needs merging.
- Location cues: style the active item distinctly, expose it with
aria-current="page", and add breadcrumbs below two levels. SC 2.4.8 Location is Level AAA, so treat it as a target. - Depth: put primary tasks within two levels and let search handle the long tail.
Verify with a first-click test on a prototype. Ask where participants would click to update a payment method, and log the first click for each task. An illustrative target, not a benchmark, is 80% correct first clicks per task. Rename the label for any task below it, then retest. Run a tree test beforehand if the structure itself is in doubt.
User interface design best practices for buttons, forms and inputs
Buttons, forms and inputs pass review when every control is at least 24 by 24 CSS px, keeps a visible label and shows errors beside its field.
The three target size thresholds side by side:
| Source | Level | Minimum size | Unit | Notes |
|---|---|---|---|---|
| WCAG 2.2 SC 2.5.8 Target Size (Minimum) | AA | 24 x 24 | CSS px | Smaller targets pass if a 24 px circle centered on each does not overlap another target |
| WCAG SC 2.5.5 Target Size (Enhanced) | AAA | 44 x 44 | CSS px | Stricter, with fewer exceptions |
| Apple Human Interface Guidelines | Guideline | 44 x 44 | pt | Native iOS unit, not a CSS value |
We recommend 44 CSS px for primary touch actions and 24 px as the floor for dense desktop UI, such as table row actions. Store both as design tokens so the component library enforces them.
Do and don't, with the check for each:
- Labels. Do keep a visible label above every field. Don't use the placeholder as the label: it vanishes once the user types, and light grey placeholder text often fails the contrast ratio. SC 3.3.2 Labels or Instructions (Level A) expects labels wherever input is required. Check by tabbing through the form with a screen reader.
- Inline validation. Do validate on blur and put a message naming the fix next to the field. Don't flag errors on every keystroke, before the user has finished typing. Check with a moderated usability test on mobile.
- Disabled buttons. Don't gray out a submit button without saying why. Keep it enabled and show errors on submit, or state the missing requirement in text beside it. Check by asking a tester what they would do next.
Offer more than one input path where the platform allows it, such as keyboard, touch, voice dictation and typed text, so users keep control of how they interact. Check by completing the form with the keyboard alone.
How should an interface give feedback?
Interface feedback means a defined loading, success and empty state for every component, with status changes announced without moving focus. WCAG 2.2 requires that announcement at Level AA under SC 4.1.3 Status Messages.

Put these specs in the design system component library, so each component ships with its states and announcement pattern and teams don't improvise them per screen.
| Component | Loading | Success | Empty |
|---|---|---|---|
| Button | Label changes to "Saving", spinner added, stays focusable via aria-disabled="true" |
Label resets, confirmation sent to a status region | Not applicable |
| File upload | Determinate <progress> with file name and percent |
"Upload complete" announced | Drop zone states accepted formats and size limit |
| Form field | Inline validation runs on blur, not per keystroke | Check icon plus text, never color alone | Not applicable |
| List or table | Skeleton rows matching the final layout | New row highlighted, item count announced | Explains why it's empty and offers one primary action |
Render the live region container in the DOM before you inject text, because many screen readers skip regions created together with their content. Use role="status" for polite messages and role="alert" for errors. For upload progress, announce milestones such as start, completion and failure, since announcing every percent floods the user.
A success message for a reversible action should carry an Undo control. Keep it on screen long enough for keyboard users to reach it.
To verify, throttle the network in browser dev tools and check that each loading state appears. Then run a screen reader pass (NVDA or VoiceOver) to confirm every message is spoken while focus stays put.
How do you prevent and recover from errors in the UI?
Prevent errors with inline validation that fires on blur, and recover with messages that name the fix. WCAG 2.2 makes both testable through SC 3.3.1, 3.3.3, 3.3.7 and 3.3.8.
Four failure modes cover most form problems:
- Validating on every keystroke. A field that flags "invalid email" after the first character punishes users mid-thought. Validate on blur, then re-validate on keystroke only once the field shows an error, so the message clears as soon as the input is fixed.
- Error copy with no fix. SC 3.3.1 Error Identification (A) requires the error in text. SC 3.3.3 Error Suggestion (AA) requires the correction when it is known. "Invalid date" fails; "Enter the date as DD/MM/YYYY, for example 04/09/2026" passes. Tie the message to its field with aria-describedby.
- Re-asking for data. SC 3.3.7 Redundant Entry (A) means prefilling or providing information already entered in the same process, such as a billing address copied from shipping.
- Cognitive-test logins. SC 3.3.8 Accessible Authentication (Minimum) (AA) bars steps like remembering a password unless an alternative exists. Allow paste and password-manager autofill, and offer passkeys or email links.
Undo and redo cover the errors validation cannot catch. A reversible action with an undo toast lets users explore without fear of permanent mistakes, whether the cause was a slip or a technical failure. We recommend reserving confirmation dialogs for irreversible actions.
To check this work, run a usability session where participants make a deliberate error and time how long recovery takes. Then do a screen reader pass on every error state. Finally, add a design system rule: each field component ships with blur validation, an error slot and the aria-describedby wiring. Keystroke validation still suits username availability and password-strength meters, where users want live guidance.
UI design accessibility best practices for WCAG 2.2
Accessible UI beyond contrast requires visible, unobstructed keyboard focus, large enough pointer targets, and a non-drag alternative for every drag interaction. WCAG 2.2 makes each of these testable at Level AA.
Three failure modes show up most often in component libraries:
| Failure mode | Success criterion | Early sign | Check |
|---|---|---|---|
| Sticky header or cookie banner covers the focused element | 2.4.11 Focus Not Obscured (Minimum), plus 2.4.7 Focus Visible | Tabbing down a long form, the focus ring disappears under the header | Tab through every page at 100% and 200% zoom |
| Drag-only sorting, sliders or kanban cards | 2.5.7 Dragging Movements | No move-up/move-down buttons or tap-to-place mode | Complete each task with keyboard only |
| Icon buttons smaller than the minimum | 2.5.8 Target Size (Minimum) | A 16px icon with no padding | Inspect computed size, or run axe DevTools |
For the sticky header, set scroll-padding-top on the scroll container to the header height. Assuming a 72px header:
html { scroll-padding-top: 72px; }
Put the value in a design token so the header and the offset change together.
Automated scanners such as axe DevTools can flag undersized targets and missing names. They rarely judge whether tab order follows the visual reading order, or whether a banner covers the focused control. Both need a manual keyboard tab-through before release.
How do you make a UI responsive?
A responsive UI sets breakpoints where the content breaks, not at named device widths, and treats touch and pointer input as separate design decisions. WCAG 2.2 sets the pass line: SC 1.4.10 Reflow, Level AA, requires content to work at a 320 CSS px viewport width without two-axis scrolling.
The order of work:
- Start at 320 CSS px and widen the viewport slowly. Add a breakpoint at the width where line length grows unreadable, a card grid leaves orphans, or the navigation wraps.
- Build padding, gaps and component sizes on the 8-point grid, so spacing scales in predictable steps across breakpoints. Use 4 px half-steps only inside dense components.
- Gate touch behavior on input type with
@media (pointer: coarse), which enlarges hit areas to meet the target size from the previous section. Gate hover-only affordances with@media (hover: hover). A narrow desktop window and a tablet with a keyboard both break width-based assumptions.
To check, zoom a 1280 px browser window to 400%, which equals 320 CSS px. Confirm there is no horizontal scrollbar on the page itself. Data tables, maps and code editors are exempt and may scroll in two dimensions. Then repeat on a real phone, because emulators don't reproduce thumb reach.
For thumb-zone and gesture patterns, see our guide to mobile UX best practices.
How to test UI design before launch
Test UI design before release with five checks, each catching failures the others miss. They are a checklist review, an axe DevTools scan, a five-second test, a first-click test and a design token lint.

Automated tools check only a subset of WCAG 2.2 success criteria, so a clean scan is a minimum.
The five methods side by side:
| Method | Catches | Misses |
|---|---|---|
| Review checklist against the design system component library | Off-pattern components, missing states | Anything the checklist author did not think of |
| axe DevTools scan | Missing labels, computed contrast failures, invalid ARIA | Whether alt text is meaningful, whether focus order makes sense |
| Five-second test | Weak visual hierarchy, unclear page purpose | Task completion, anything below the fold |
| First-click test | Misleading navigation labels, wrong entry points | Later steps in a flow |
| Token lint (a Stylelint rule that flags hard-coded values) | Colors and spacing that bypass design tokens | Whether the token chosen is the right one |
Retain moderated usability sessions for what these five miss: whether people finish the task. We recommend 15-20 minutes per session and 5-10 tasks, which keeps participants engaged. Include one task that forces a mistake and an undo, since the recovery path is the part scans never exercise.
Run the token lint and axe scan in CI on every pull request. Reserve the five-second, first-click, and moderated tests for changes to layout or navigation.
UI design best practices FAQ
What are the best practices for UI design?
How many fonts should a UI use?
What is the minimum touch target size under WCAG 2.2?
How do you test UI design?
Turn the checklist into a release gate
Turn the checklist into a release gate by encoding each check in your component library and design tokens. A pull request then fails when a component breaks a rule.
Start with the contrast and target-size tokens, then run an automated accessibility scan and a keyboard pass in CI before merge.
Our UI design team builds components this way, using the Silk design system through our Experience & Design practice. To scope a gate for your product, get an estimate.
