Skip to story

Accessibility

Fitts's Law and Tap Target Sizing

8 min read · June 1, 2026 ★ Flagship

Reading level

The settings toggle that changed the wrong thing

A mobile settings screen had 12 toggle switches, each 20×20px, stacked 6px apart. Users were accidentally toggling the wrong settings. Support tickets poured in: "I turned off notifications but something else got turned off." The developer increased the visual toggle size. But the invisible hit area stayed 20×20px. The problem didn't go away.

The hit area is what Fitts's Law cares about — not the visual size of the element. A 44px minimum hit area means users can tap anywhere in a 44-pixel square and register a hit. A 20px visual element with a 20px hit area means every tap needs to be precise. On a moving phone, in a pocket, with a gloved hand — that precision fails constantly.

Fitts's Law quantifies pointing time: MT = a + b·log₂(1 + D/W), where D is distance to the target and W is target width. Doubling target size (W) reduces acquisition time by roughly 30% for distant targets. The 44px minimum isn't aesthetic preference — it's the point where most users' fingertip contact areas produce acceptable error rates.

WCAG 2.5.5 (Success Criterion, Level AA) mandates 44×44 CSS pixels for all interactive controls. WCAG 2.5.8 (Level AA, added in WCAG 2.2) addresses the additional constraint: targets need adequate spacing between them. Two adjacent 44px targets with 0px gap effectively reduce actionable area — the tap distribution overlaps the boundary.

Hit area vs visual size

The key insight: visual size and interactive hit area are separate things in CSS. You can show a 16×16px icon while giving it a 44×44px tap target by padding the containing element:

/* ❌ Small hit area — same as visual size */
.icon-btn {
  width: 20px;
  height: 20px;
}

/* ✅ 44px hit area, 20px visual icon */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0; /* hit area is the box, icon inside is visually small */
}

The min-width / min-height approach is safer than width / height — it lets the button grow if the text is longer while maintaining a minimum tap target.

Four patterns where small touch targets hide in real codebases:

  1. Checkbox/radio without label association: the input is 16×16px and the label isn't clickable. Fix: <label> wrapping both input and text with min-height: 44px; display: flex; align-items: center;.
  2. Icon buttons in toolbars: 16px or 24px SVG icons with no padding. Fix: platform-icon-btn pattern with min-width: 44px; min-height: 44px.
  3. Inline links in dense text: unavoidably small hit areas when links are mid-sentence. Fix: increase line-height to give vertical tap room; avoid clustering multiple links per line on mobile.
  4. Table row actions: edit/delete icon buttons at 24px. Fix: same padding approach, or switch to swipe-to-reveal actions on mobile.

The OS gesture zone is an underappreciated Fitts's Law violation. On iOS, the bottom ~34px is reserved for home indicator. On Android, the gesture navigation area at the bottom varies by device. Bottom navigation bars that extend to the edge of the screen without env(safe-area-inset-bottom) padding effectively make the bottom tab target unreachable under normal thumb movement:

.bottom-nav {
  padding-bottom: max(env(safe-area-inset-bottom), 8px);
}

This is especially critical for the first and last tab in a bottom nav — they're both at max distance from the ergonomic thumb zone on a large phone and competing with OS gestures.

The "Remove" link that deleted the wrong item

A mobile shopping cart had a small "Remove" link next to each item — 18px tall, rendered as a plain text link with 4px of vertical padding. The links were stacked 6px apart. In a week of production data, the support team saw a pattern: "I didn't mean to remove that item." Customers were accidentally tapping "Remove" when they were trying to tap the quantity selector or the item image above it.

On a 390px-wide iPhone screen held loosely while commuting, an 18px tap target that sits 6px from another interactive element is essentially a coin flip. Users were not making mistakes. Physics was making mistakes for them.

The developer who built the cart had sized the "Remove" link to match the design comp — which was a desktop mockup. On desktop, a mouse cursor can target a 16px element reliably. A fingertip contact patch is approximately 10mm wide, which at 163 DPI (standard iPhone) covers ~64 CSS pixels. An 18px tap target with 4px padding gives a 26px hit area — less than half a fingertip width. The visual design was correct for mouse; the CSS was incorrectly carried over to mobile without adaptation.

The impact was measurable: in the week before the fix, "Remove item" actions had a 22% follow-up "Add item back" rate — an industry proxy for accidental deletion. The expected accidental deletion rate for e-commerce carts is roughly 3–5%. The 22% figure represented hundreds of accidental cart clearings per day, some of which resulted in abandoned sessions rather than recovery. The support ticket volume was the visible tip; the conversion loss from cart-clearing was the unmeasured cost.

44px targets, 8px gaps, and an accidental-delete rate that collapsed

The fix converted the "Remove" text link to a button with min-height: 44px and enough horizontal padding to make it comfortable. The trash icon inside stayed visually small — 16px — but the tappable area expanded to meet the 44px minimum. The gap between the Remove button and the quantity stepper above it was increased from 4px to 8px.

Accidental "Add item back" rate fell from 22% to 4% in the first week. No user noticed a visual change — the icon looked identical. Only the invisible hit area changed. Support tickets about accidental removals stopped entirely within two weeks.

The implementation used the padding-extension technique: the button's visual icon is centered inside a larger clickable box using display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px. The outer box is the tap target; the inner icon is the visual. The gap between adjacent interactive elements was enforced with a design token (--touch-gap: 8px) applied via gap on the flex row containing quantity controls and remove button. This gap is not just spacing — it's a Fitts's Law buffer that prevents tap distributions from overlapping the boundary between targets.

The fix was also a WCAG compliance correction. WCAG 2.5.5 (Target Size, AA) requires 44×44 CSS pixels for interactive controls. WCAG 2.5.8 (Target Size Minimum, AA, added in WCAG 2.2) requires that when targets are smaller than 44×44, they must have adequate spacing to prevent overlap of their tap areas. The cart had been violating both. The design system team added automated axe-core audits to CI that flag interactive elements with reported offsetHeight or offsetWidth below 44px — catching this class of regression in PRs before they reach production.

Pattern at a glance

Annotated example: 24px text link vs 44px minimum button

❌ SMALL TARGET — 24px hit area

Running shoes Remove
Socks (3-pack) Remove

~24px hit area, 4px gap — frequent accidental taps

✅ LARGE TARGET — 44px min hit area

Running shoes
Socks (3-pack)

44px hit area, 8px gap — precise, WCAG 2.5.5 compliant

Try it: small vs large touch targets

Both modes show the same settings screen. The "Small" mode uses 20×20px hit areas. The "Large" mode uses 44×44px hit areas. Try to accurately tap the third toggle from the top in both modes — notice how much more careful you have to be with the small targets.

The demo measures your error rate (wrong toggle activated) across 5 attempts per mode. Small targets typically produce 20–30% error rates. 44px targets drop below 2%. Identical visual design, radically different tap accuracy.

Check the spacing between targets in both modes. The small mode clusters items at 6px apart; even if each target were enlarged, the gap would create a boundary conflict. The large mode uses 8px between 44px targets — safely separated without requiring surgical precision.

⚡ Interactive demo

Implementing 44px minimum touch targets

The padding-extension technique: make the visible element smaller than the tap area by padding the container button rather than setting explicit width/height on the icon.

/* ❌ Visual size = hit area — too small */
.icon-btn svg {
  width: 16px;
  height: 16px;
}

/* ✅ 44px hit area, small visual icon */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
}

.icon-btn svg {
  width: 16px;
  height: 16px;
  pointer-events: none; /* prevent SVG from swallowing clicks */
}

Key pitfall: setting width: 44px instead of min-width: 44px. Use min-width so the button grows if a label is added later without breaking the touch target floor.

Four hidden small-target patterns and their fixes in real codebases:

  1. Checkbox without label association. The input is 16×16px and clicking the label text doesn't check the box. Fix: wrap both in <label> with min-height: 44px; display: flex; align-items: center; gap: 8px. The entire label row becomes the tap target.
  2. Icon-only toolbar buttons. SVG icons at 24px with no padding. Fix: apply the .icon-btn pattern above to every icon button at the design system token level — not per-component.
  3. Table row actions. Edit/Delete icons at 24px in dense data tables. Fix: switch to swipe-to-reveal on mobile, or increase row height to accommodate 44px buttons.
  4. Bottom nav on iOS/Android. Tabs touching the gesture zone. Fix: padding-bottom: max(env(safe-area-inset-bottom), 8px) on the bottom nav container.
/* Safe area fix for bottom navigation */
.bottom-nav {
  padding-bottom: max(env(safe-area-inset-bottom), 8px);
}

/* Checkbox label as full tap target */
.checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  cursor: pointer;
}

At design system scale, enforce the 44px floor as a component-level constraint, not a per-instance responsibility. The IconButton base component should have min-width: 44px; min-height: 44px in its base styles — impossible to use with a smaller hit area without explicitly overriding it. For automated enforcement, add an axe-core custom rule or a Playwright accessibility check that queries all button, a, and input[type=checkbox] elements and asserts offsetHeight >= 44 && offsetWidth >= 44 in your E2E test suite. WCAG 2.5.5 (Target Size, AA) and 2.5.8 (Target Size Minimum, AA in WCAG 2.2) are both testable automatically — include them in your CI accessibility gate. Apple HIG specifies 44×44pt minimum; Material Design specifies 48×48dp (which maps to ~44px at standard density). Defaulting to 44px satisfies both platform HIGs and the WCAG spec simultaneously.

References

Remember

Key takeaways

  • Visual size and tap hit area are different things. Use min-width: 44px; min-height: 44px on interactive elements — the icon or text inside can be smaller visually.
    Four hidden small-target patterns: unlabeled checkboxes, icon-only toolbar buttons, inline text links, and table row action icons. All fixable with the same padding/min-size approach.
    Bottom navigation bars must account for OS gesture zones. Use env(safe-area-inset-bottom) to push content above the gesture area — otherwise bottom nav items compete with the OS swipe gesture.
  • The 44px minimum is WCAG 2.5.5 (AA). Spacing between targets also matters — WCAG 2.5.8 requires targets to not overlap each other's touch area.
    Fitts's Law: MT = a + b·log₂(1 + D/W). Bigger targets (W) reduce movement time logarithmically. For touch UIs where thumb travel is short, W is the dominant variable — target size matters more than position.
    When targets must be small (inline links, dense data tables), reduce the adjacent target count and increase the line-height to compensate with vertical space. There's no way to fully comply with Fitts's Law for mid-sentence links — keep them sparse.

Enjoyed this case?

Case 3 of 3 in Accessibility · 26 of 31 live

Keep going

Finish this takeaway, then continue the track — Casey saved your spot locally.

Sign in with email to sync progress across devices (beta).

Inside the Casebook

New cases every few weeks — patterns from production UI engineering. Double opt-in, easy unsubscribe.

No spam. Unsubscribe anytime. Emails sent via Buttondown.

RSS feed
Casey, junior (idle)
Casey · Junior

Hey! I'm Casey — scroll through the case and I'll chime in with hints.