# Small tap targets packed too close together

Source: https://easeweb.dev/learn/target-size
Topics: Buttons and links > Targets are big enough to hit (WCAG 2.5.8)
The fix: Give every target room. Either make the buttons at least 24 by 24 CSS pixels, or keep the 16-pixel look and extend each button's hit area with a pseudo-element, or keep them small and space them so their centres are at least 24 pixels apart.
Test with: target measurement, touch, mouse, zoom
References: https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html https://www.w3.org/WAI/WCAG22/Understanding/target-size-enhanced.html https://developer.mozilla.org/en-US/docs/Web/CSS/::before

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A shopping basket shows each item on one line: "Linen napkins, Qty 2", then three icon buttons to decrease the quantity, increase it, and remove the item. The buttons are drawn at 16 by 16 pixels and sit 2 pixels apart. On a phone, a tap aimed at plus lands on remove often enough that the item disappears. With a mouse, the same row needs a careful, steady hand.

## Why it fails

WCAG 2.5.8 Target Size (Minimum) asks for pointer targets of at least 24 by 24 CSS pixels. A smaller target still passes if a 24-pixel circle centred on it does not intersect another target or the circle of another undersized target. Here each button is 16 pixels and their centres are only 18 pixels apart, so the circles overlap and neither route passes. None of the exceptions apply: these are not links inside a sentence, there is no larger equivalent control on the page, and the size is a design choice, not something essential.

## Who is affected

People with tremors or limited fine motor control, people using a head pointer, eye tracking or a mouth stick, and anyone tapping with a thumb on a moving bus. Older users and people with low vision also hit small targets less often. A missed tap that does nothing is annoying; a missed tap that triggers the neighbouring action, like removing the item, costs work and trust.

## What automation and AI miss

Size and spacing are geometry, so a scanner can measure them: axe-core's `target-size` rule flags this row. What it can't judge are the exceptions: whether a larger control for the same action exists elsewhere on the page, or whether the small size is essential. It measures the element's box, so it doesn't see a hit area extended with a pseudo-element. Automated click tests hit the exact centre of an element, so they never slip onto a neighbour. A generated design often shrinks icon buttons to match the text size, and the result looks tidy in a screenshot. A scan finds the small targets; measuring the hit areas and tapping with a real finger confirms the fix.

## Before: 16-pixel buttons, 2 pixels apart

```html
<div class="line-actions">
  <button type="button" aria-label="Decrease quantity">−</button>
  <button type="button" aria-label="Increase quantity">+</button>
  <button type="button" aria-label="Remove Linen napkins">×</button>
</div>
```

```css
.line-actions {
  display: flex;
  gap: 2px;
}
.line-actions button {
  width: 16px;
  height: 16px;
  padding: 0;
}
```

Each target is 16 by 16 pixels and the centres are 18 pixels apart. A 24-pixel circle around each button overlaps its neighbour's circle, so the row fails both the size and the spacing route.

## After: bigger buttons

```css
.line-actions {
  display: flex;
  gap: 4px;
}
.line-actions button {
  flex-shrink: 0;
  min-width: 32px;
  min-height: 32px;
  padding: 0;
}
```

Each button is now 32 by 32 pixels, comfortably above the 24-pixel minimum, and the visible button is the whole hit area. People can see how much room they have, and the HTML does not change. `flex-shrink: 0` stops a narrow screen from squeezing the buttons back down.

## After: a larger hit area, when the visible size can't change

```css
.line-actions {
  display: flex;
  gap: 12px;
}
.line-actions button {
  position: relative;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  padding: 0;
}
/* Part of the button, so a tap on it presses the button. */
.line-actions button::before {
  content: "";
  position: absolute;
  inset: -6px;
}
```

The buttons still look 16 pixels wide, but each one's pseudo-element extends its hit area to 28 by 28 pixels. A click on a pseudo-element goes to its button, so the target is the larger box. The 12-pixel gap is what keeps the extended areas from overlapping: with 6 pixels on each side, neighbouring areas meet but do not cross.

## After: small buttons, spaced apart

```css
.line-actions {
  display: flex;
  gap: 10px;
}
.line-actions button {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  padding: 0;
}
```

The buttons stay 16 pixels, but their centres are now 26 pixels apart. A 24-pixel circle centred on each one no longer reaches its neighbour's circle, so the row passes through the spacing exception. A tap that misses now lands on empty space, not on the wrong action.

The circle reaches in every direction, not only sideways. In a basket the next item's buttons sit directly below these, so the rows also need their buttons at least 24 pixels apart, centre to centre; give each line enough height or padding. Any other link or control within 12 pixels of a button breaks the exception too.

## Which option to use

Use bigger buttons. They do the same job as the larger hit area with less CSS and nothing invisible to break, and they also make the target easier to see, a step towards the 44-pixel size of 2.5.5. Keep the larger hit area for when the visible size is fixed, for example by a design system you can't change: it passes, but people still aim at a small shape, and because the area can't be seen, a later change to the gap can make areas overlap without anyone noticing. Spacing is the smallest change and stops wrong presses, but it is not enough on its own: the target stays small, so people with tremors still miss it more often, and the pass breaks as soon as a row or another control moves within reach of the circle.

## Implementation decisions

Measure the hit area in CSS pixels, not the icon inside it: padding and pseudo-elements count, and margins do not. A pseudo-element that runs outside a parent with `overflow: hidden` is clipped, and so is its hit area. Do not let extended hit areas overlap a neighbour or a nearby link; where two areas overlap, the one on top wins, and the other target shrinks.

The criterion has exceptions: links inside a sentence, controls whose size the browser sets and you have not changed, and targets with an equivalent larger control on the same page. Do not rely on an equivalent hidden in a menu. In component libraries, set the minimum on the shared icon button, not on each use, so a new row cannot shrink it again.

## Verify the fix

1. Measure every target in the row with the browser's inspector, including any pseudo-element, and record its width and height.
2. For each target under 24 pixels, check that a 24-pixel circle centred on it reaches no other target and no other small target's circle, in every direction, including the lines above and below.
3. On a real phone, tap each control several times with a fingertip and confirm that its neighbour never fires.
4. Zoom to 200%, narrow the window to 320 pixels, and check that the targets keep their size and their hit areas still do not overlap.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared button styles change.

## Limits

This is a starting pattern, not a guarantee for every application. 24 pixels is the minimum at level AA; 44 pixels, as in 2.5.5, is easier for most people. The guide covers icon buttons in one row; grids, sliders and drag handles need their own checks. Test with the people and devices your users actually have.
