Target Size and Pointer Accessibility: Meeting 2.5.8 Without Redesigning
A design system ships 16-pixel icon buttons in a dense toolbar, an audit flags every one of them against WCAG 2.5.8, and the proposed fix — make all the icons bigger — would break the visual density the toolbar was designed for. The fix is unnecessary. The criterion does not measure how big something looks; it measures how big the region that accepts a pointer action is, and CSS lets those two numbers diverge. This page covers the two target-size criteria, the exceptions that legitimately let a control stay small, and the two implementation techniques for growing a hit area. It sits under Accessibility in CSS Animations alongside the motion rules in WCAG Motion Success Criteria; this one is about the pointer rather than the animation.
What this page settles:
- The exact wording and level of 2.5.8 and 2.5.5
- Why a visually small control can still pass
- Padding versus a
::afteroverlay, and when each is right - How the spacing exception is measured
Two criteria, two levels
2.5.8 Target Size (Minimum) is Level AA, introduced in WCAG 2.2. It requires that the size of the target for pointer inputs is at least 24 by 24 CSS pixels, except where one of five exceptions applies.
2.5.5 Target Size (Enhanced) is Level AAA. It requires at least 44 by 44 CSS pixels, and it carries a shorter exception list. Before WCAG 2.2 this criterion was simply called "Target Size"; the rename to "Enhanced" happened when the AA minimum was added, which is why older audit reports sometimes cite 2.5.5 at 44 pixels as though it were the binding rule. For an AA target it is not.
The unit matters. A CSS pixel is not a device pixel and is not affected by the user's browser zoom in the way people often assume — the criterion is evaluated in the CSS coordinate space, so a width: 24px box satisfies the measurement regardless of the display's pixel density. It is also not affected by transform: scale() in the way a naive reading suggests, because a scaled element's rendered target does change; if you shrink a control with a transform, you have shrunk its target too.
What "target" means
The target is the region of the display that will accept a pointer action for that control. For a normal element that is its border box — content plus padding plus border — and anything else that is hit-testable on top of it, such as an absolutely positioned pseudo-element child. Margin is not part of the target; margin is empty space that belongs to no one, which is exactly why it is useless for this purpose and padding is not.
That single definition is what makes dense interfaces salvageable. The icon glyph, the SVG artwork, the visible background — none of those are what gets measured. The diagram below shows the three situations that matter: a control whose border box is the glyph and therefore fails, the same glyph inside a padded box that passes, and two undersized targets that pass on spacing alone.
The demo makes the same point interactively. Both close buttons draw a 16-pixel glyph; hover each row and the tinted area shows how much of the screen actually responds.
The five exceptions to 2.5.8
A control smaller than 24 by 24 CSS pixels still conforms if any one of these holds:
| Exception | What it covers |
|---|---|
| Spacing | Undersized targets positioned so that a 24-pixel-diameter circle centred on each one does not intersect another target, or the circle of another undersized target. |
| Equivalent | Another control on the same page performs the same function and does meet the size requirement. |
| Inline | The target is in a sentence, or its size is otherwise constrained by the line-height of non-target text. |
| User agent control | The size is determined by the user agent and has not been modified by the author. |
| Essential | A particular presentation of the target is legally required or essential to the information being conveyed. |
The Spacing exception is the one that rescues dense toolbars, and it is worth stating its geometry carefully: the test is on 24-pixel diameter circles — radius 12 — centred on the bounding box of each undersized target. If those circles stay clear of each other, and clear of any other target, the targets pass. In practice this means roughly 24 pixels of centre-to-centre separation, which you buy with gap or margin rather than by growing anything.
The Inline exception is why body-copy links do not need 24-pixel-tall hit areas. A link in the middle of a paragraph is constrained by the surrounding text's line box, and forcing it taller would break the paragraph. Note this covers links in text — it does not cover a row of small standalone links styled to look like a nav, which is a common misreading.
The Equivalent exception is genuinely useful for icon-only shortcuts: a tiny "duplicate" icon in a table row is fine if a full-size "Duplicate" item also exists in that row's menu.
Complete working implementation
Two techniques, in one file. The first grows the border box; the second leaves the box alone and overlays a larger hit region.
<div class="toolbar">
<button class="tool" type="button" aria-label="Bold">B</button>
<button class="tool" type="button" aria-label="Italic">I</button>
<button class="tool" type="button" aria-label="Underline">U</button>
</div>
<p class="meta">
Updated 5 minutes ago —
<a class="chip" href="#history">history</a>
<a class="chip" href="#revert">revert</a>
</p>
/* --- Technique 1: padding grows the border box -----------------------
The glyph still renders at 16px. Four pixels of padding on every side
makes the border box 24 x 24, and the border box IS the target.
min-width/min-height guard against a narrow glyph (like "I") shrinking
the box below the minimum. */
.tool {
font: 600 16px/1 system-ui, sans-serif;
padding: 4px;
min-width: 24px;
min-height: 24px;
border: 0;
background: transparent;
color: inherit;
cursor: pointer;
}
/* Spacing is independent of size: this gap also keeps the Spacing
exception available if a control ever drops below 24px. */
.toolbar {
display: flex;
gap: 8px;
align-items: center;
}
/* --- Technique 2: a pseudo-element overlay ---------------------------
Use when the visible box must not grow — here the chips sit on a tight
line and padding would push the line-height around. The ::after is a
hit-testable child, so it extends the target without changing layout. */
.chip {
position: relative; /* containing block for the overlay */
font-size: 0.8rem;
}
.chip::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
/* Never shrink below the element itself: max() keeps wide chips fully
clickable while lifting narrow ones up to 24px. */
width: max(100%, 24px);
height: 24px;
/* Transparent, but still receives pointer events. */
background: transparent;
}
/* Overlays on neighbours must not overlap, or the later one in the
painting order silently steals clicks from the earlier one. */
.chip + .chip {
margin-inline-start: 12px;
}
/* The indicator must follow the visible box, not the enlarged target,
or the ring will look detached from the control. */
.tool:focus-visible,
.chip:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
The second technique is easier to see than to read. Both rows below are the same markup; only the second one carries the overlay.
The technique that makes it work
width: max(100%, 24px) on the overlay is the detail that makes the pseudo-element approach safe to apply globally. A fixed width: 24px overlay on a chip whose text is 80 pixels wide would shrink the effective target — the overlay paints on top and captures the pointer, so the exposed area becomes the overlay's, not the element's. max() makes the overlay at least as wide as the element it belongs to and at least 24 pixels, so it can only ever add area. The same reasoning applies to height when your control has a taller line box than the minimum.
The second half of the technique is the containing block. position: absolute resolves against the nearest positioned ancestor, so without position: relative on the element itself the overlay would size and place itself against some ancestor far up the tree, producing a giant invisible click-catcher over half the page. That failure is silent — nothing looks wrong — which is why it is worth testing overlays by temporarily giving them a visible background.
Variation: sizing up for coarse pointers
24 pixels is a conformance floor, not a usability target. On touch devices the practical comfortable minimum is closer to 44, and you can raise it selectively rather than everywhere by testing the pointer's precision, a technique covered in Pointer and Hover Media Queries:
/* Coarse pointer = finger or stylus. Lift the same controls to the
2.5.5 (AAA) figure without changing the desktop toolbar's density. */
@media (pointer: coarse) {
.tool {
min-width: 44px;
min-height: 44px;
padding: 14px;
}
.chip::after {
width: max(100%, 44px);
height: 44px;
}
}
Because the padded technique keeps the glyph at 16 pixels, this scales the target without scaling the artwork — the toolbar looks the same and simply becomes easier to hit.
Browser support
Everything here is long-established CSS. max() in a length context is supported across all current engines, and the translate property used for centring the overlay is available in Chrome 104, Edge 104, Firefox 72 and Safari 14.1. If you need to support engines older than the standalone translate property, transform: translate(-50%, -50%) is an exact substitute with universal support. Media queries for pointer are likewise supported everywhere in current use.
FAQ
What is the minimum target size under WCAG 2.2? Success Criterion 2.5.8 Target Size (Minimum) is Level AA and requires targets to be at least 24 by 24 CSS pixels, unless one of its five exceptions applies. The enhanced version, 2.5.5 Target Size (Enhanced), is Level AAA and asks for 44 by 44 CSS pixels.
Does a 16 pixel icon automatically fail 2.5.8? No. The criterion measures the target, which is the region that accepts the pointer action, not the painted artwork. A 16 pixel glyph inside a button with 4 pixels of padding on every side has a 24 by 24 target and passes.
When can a target be smaller than 24 by 24 pixels? Five exceptions apply: sufficient spacing between undersized targets, an equivalent control of adequate size elsewhere on the page, targets inline in a sentence or block of text, targets whose presentation is determined by the user agent, and cases where the specific size is essential.
Should I use padding or a pseudo-element to grow a hit area? Prefer padding when the element can grow without disturbing the layout, because the border box is unambiguously the target. Use an absolutely positioned pseudo-element overlay when the visual size must stay fixed, and check that overlays on adjacent controls do not overlap.
Related
- Accessibility in CSS Animations — the parent guide for accessibility work on this site.
- WCAG Motion Success Criteria — the motion criteria that sit beside these pointer ones.
- Pointer and Hover Media Queries — detecting coarse pointers before you resize anything.
- Creating Accessible Focus Indicators — keeping the ring attached to the visible box.
- Responsive Forms With Container Queries — control sizing that responds to available space rather than viewport.
Related articles
More pages in the same section.