Accessibility Recipe No. 02
The invisible hit area
A 24px target without changing how the button looks. One transparent pseudo-element does it. Here is what it fixes, what it does not, and the places it backfires.
Reviewed by Khushwant Parihar, CPACC
Updated
- Serves
- Icon buttons, chips and close buttons
- Prep
- One pseudo-element
- Pairs with
- 2.5.8 Target Size (Minimum), 2.5.5 Target Size (Enhanced) (AAA)
- Measure with
- Target size checker
Inspect the targets
A formatting toolbar of 16px icons, 4px apart, the size a lot of editors ship. Turn the hit area on and the painted boxes show what actually takes the tap, while the icons stay exactly as they were. The verdicts underneath apply the real rules, including the spacing exception and the fact that overlapping area does not count.
Try 24px with the 4px gap, then widen the gap to 8px. Then try 44px and see why AAA is so hard in dense toolbars.
Visible 16px · target 16px · centres 20px apart · inset 0
2.5.8 Target Size (Minimum), AAFail
The targets are 16px and only 20px apart centre to centre, so 24px circles on them intersect.
2.5.5 Target Size (Enhanced), AAAFail
The targets are 16px. 2.5.5 needs 44px and has no spacing exception.
Why the size matters: the tap test
Fingers do not land exactly where people aim. Each simulated tap aims at the centre of the icon and lands a little off, more so for a shaky hand, a bumpy bus or a cold thumb. Raise the scatter and watch the 16px icon miss.
- Taps so far
- 0
- Landed on the 16px icon
- -
- Landed on a 24px target
- -
- Landed on a 44px target
- -
You can also tap or click on the pad yourself. It is drawn four times actual size.
The recipe
Ingredients
position: relativeon the button.- One empty
::before, absolutely positioned. - A negative
insetof (target minus visible size) divided by two.
Method
- Work out the inset: a 16px icon needs 4px on every side for 24px, or 14px for 44px.
- Check the neighbours. Hit areas may touch but must not overlap; overlapping area does not count.
- Make sure no parent clips it. Anything with overflow hidden needs room around the button.
- Skip links inside sentences. They are exempt, and growing them steals taps from the text around them.
/* The invisible hit area: a 16px icon button, a 24px target */
.icon-button {
/* The hit area is measured from the button. */
position: relative;
}
.icon-button::before {
content: "";
position: absolute;
/* (24 - 16) / 2: reach 4px past the button on every side. */
inset: -4px;
/* Keep the target the same shape as the button. */
border-radius: inherit;
}Serving notes
- If the button already uses
::beforefor an icon, use::after, or put the hit area on a wrapper. - The pseudo-element takes no space in the layout, so the design does not move. That is the whole point, and also why most automated checkers miss it.
- Keyboard users never hit targets; they need a visible focus ring. See recipe No. 01.
/* Any visible size, any target: inset = (target - visible) / 2 */
.icon-button--20::before { inset: -2px; } /* 20px icon, 24px target */
.icon-button--aaa::before { inset: -14px; } /* 16px icon, 44px target */Where it backfires
The trick changes one thing: the area that accepts the pointer. These five details decide whether that helps or hurts. The painted areas in each example are the hit areas, and the small examples are drawn two and a half times actual size.
Detail 01
Hit areas that overlap their neighbours
Two 16px buttons 4px apart, each given a 44px hit area. The painted areas overlap, and a tap in the overlap goes to whichever button comes later in the page, here Archive, even when the person was aiming at Delete. WCAG does not count overlapping area toward target size, so this fails 2.5.8 as well as being risky. Tap the space just right of Delete in each version.
Backfires
Last tap went to: Nothing yet
Fixed
Last tap went to: Nothing yet
24px hit areas and an 8px gap: the areas meet but never overlap.
Detail 02
A container that clips the hit area
The hit area lives outside the button, so a parent with overflow: hidden cuts it off. A close button tucked into a corner keeps only a quarter of its extra area. Give the container padding at least as wide as the inset, or move the button in from the edge.
Backfires
A dismissable notice with overflow: hidden.
Fixed
A dismissable notice with overflow: hidden.
Detail 03
A focus ring that ignores the hit area
Keyboard focus still draws its ring around the visible 16px button, not the area that takes the tap. That is fine for 2.4.7, but a ring drawn on the ::before shows the real target and is easier to see. Tab to each button.
Backfires
Fixed
Detail 04
Most automated checkers cannot see it
axe, and most target size checkers, measure the element's own box and do not add its ::before. A button that really takes taps across 24px is reported as 16px, so expect a flag and verify by hand. Our target size checker does count the pseudo-element, unless it overlaps a neighbour or a container clips it. If every tool's report has to come back clean, use padding with min-width and min-height instead (W3C technique C42). Tools can measure that, but unlike the pseudo-element it takes up space in the layout.
Backfires
Real target 24px. axe sees a 16px box; our checker sees 24px.
Fixed
The same look, padded to a 24px box that every tool can measure.
Detail 05
Links inside a sentence
Inline links are exempt from both target size criteria, and for good reason: growing a link in a paragraph makes its hit area cover the lines above and below, so a tap meant for the text, or for another link, opens this one. Leave inline links alone and give the text a generous line height instead.
Backfires
Our returns window is thirty days from delivery. Read the returns policy before you send anything back, and keep the original packaging where you can.
Fixed
Our returns window is thirty days from delivery. Read the returns policy before you send anything back, and keep the original packaging where you can.
Measure before you ship
- Turn on a temporary outline for ::before in DevTools and look at every icon button: the painted box is the target.
- Run the target size checker at phone width. It counts pseudo-element hit areas; axe does not, so confirm its flags by hand.
- Where icons sit close together, check that hit areas meet but never overlap.
- Tap through on a real phone, including the corners of cards and the edges of dialogs.
- Tab through with the keyboard: every icon button still needs a visible focus ring.
- Leave inline links alone, and give paragraphs a line height of at least 1.5.
Questions
Does an invisible hit area count for WCAG 2.5.8?
Yes. WCAG 2.2 defines a target as the region of the display that will accept a pointer action, and a click or tap on a button's ::before is a click on the button. W3C's Understanding document for 2.5.8 itself notes it can help to increase the active target area without increasing the visible size. Overlapping areas do not count, so keep neighbouring hit areas apart.
Why not just leave 8px between 16px icons?
For AA that works: 16px icons whose centres are 24px apart pass 2.5.8 through the spacing exception. But each icon is still a 16px thing to hit, the exception does not exist in 2.5.5 (AAA), and it does nothing for a lone small button sitting against other controls. The hit area makes the target itself bigger.
Does it work for touch, pen and mouse?
Yes. Pointer events on a pseudo-element go to the element it belongs to, whatever the pointer. Keyboard users are not affected by target size at all; what they need is a visible focus indicator.
Why does my accessibility checker still flag the button?
Most automated checkers, axe included, measure the element's own box and do not add its pseudo-elements. Our target size checker does count an absolutely positioned ::before or ::after, unless it overlaps another target or a container clips it. Elsewhere, verify the real target by hand, or use padding with min-width and min-height (W3C technique C42), which every tool can measure but which takes space in the layout.