Touch Target Sizes
Make controls easy to tap — WCAG target size guidance, 44×44 CSS px heuristics, spacing, and icon-button layout tips.
- accessibility
- touch-target
Tiny tap targets frustrate everyone and block users with motor impairments. WCAG 2.2 adds Target Size (Minimum) (2.5.8, AA) and has Target Size (Enhanced) (2.5.5, AAA). Platform HIG also recommend ~44×44 pt/px minimums.
Docs: WCAG 2.5.8, 2.5.5, Apple HIG.
Practical targets
| Guidance | Size |
|---|---|
| Common platform heuristic | 44×44 CSS px minimum |
| WCAG 2.5.8 AA | 24×24 CSS px with spacing exceptions |
| WCAG 2.5.5 AAA | 44×44 CSS px |
Design systems should aim for 44×44 on primary touch UIs; 24×24 AA is a floor, not a goal for comfortable mobile UX.
Implement with padding, not only font size
.icon-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 44px;
min-height: 44px;
padding: 10px;
}
.icon-button svg {
width: 20px;
height: 20px;
}
The hit area can be larger than the visible icon via padding/pseudo-elements.
Spacing
Crowded icon rows fail even if each is 24px — users hit the wrong control. WCAG 2.5.8 allows undersized targets if there’s enough spacing so a 24px circle centered on the target doesn’t intersect others. Prefer larger targets over measuring exceptions.
.toolbar {
display: flex;
gap: 8px; /* better: 12–16px for dense icons */
}
Desktop vs mobile
Desktop mouse users benefit from larger targets too. Responsive components shouldn’t shrink critical actions below the floor on small viewports.
Exceptions (don’t abuse)
- Inline links in body text (paragraph reflow)
- Targets determined by the user agent
- Essential presentation where size is required smaller (rare)
Still improve when you can.
Testing
- Real device thumbs
- Browser device mode is imperfect
- Playwright can assert bounding boxes for primary CTAs
const box = await button.boundingBox();
expect(box?.height).toBeGreaterThanOrEqual(44);
Footguns
- 16×16 icon buttons with no padding.
- Adjacent delete/edit icons almost overlapping.
- Cookie banners with tiny dismiss controls.
- Relying on hover-only enlarge.
Interview out-loud answer
“I size interactive controls to at least 44×44 CSS px where possible, expand hit areas with padding, and space dense icon groups. WCAG 2.2 AA allows 24×24 with spacing rules, but comfortable mobile UI aims higher.”
Nested targets
A list row that’s clickable with an inner icon button creates overlapping targets. Give the row a clear primary action and make the icon button large enough with its own name (“More actions for {item}”) so users aren’t forced into pixel precision.
Extra practice
Write a minimal demo in a scratch file or the playground: one happy path, one failure path, and one boundary input. If you cannot exhibit a bug that the pattern prevents, you do not own the concept yet — re-read the primary docs linked below and tighten the example until the failure is obvious.
Notes from real codebases
Teams that succeed here keep the rules mechanical: lint where possible, CI for the rest, and a short human checklist for what automation cannot see. Document exceptions with an owner name and a removal date so “temporary” escapes do not become permanent architecture.
Related on this site
- Keyboard accessibility checklist
- Focus visible styles
- WCAG principles POUR
- Accessible forms errors
- Color contrast requirements
Further reading
Related guides
- Accessible Combobox PatternBuild or evaluate comboboxes with APG keyboard behavior, aria-expanded/activedescendant, and filterable listbox pairing.
- Accessible Forms ErrorsLabel inputs, associate errors with fields, announce failures, and avoid placeholder-only forms that break accessibility.
- Accessible Menus PatternAPG menu and menubar keyboard model — arrow navigation, escape to close, aria-expanded, and when a disclosure is enough.
- Accessible Modals PatternsModal dialogs that work: focus trap, Escape, return focus, aria-modal, and why native dialog or APG patterns beat div soup.
- Accessible Names ComputationHow browsers compute accessible names from content, labels, aria-label, and labelledby — and how that powers Testing Library queries.