Testing Target Size 2.5.8 in Dense Grids
Permalink to "Testing Target Size 2.5.8 in Dense Grids"Dense data grids are full of small targets: 16-pixel row checkboxes, icon-only row action buttons, tiny sort arrows in headers, column resize handles a few pixels wide, expand chevrons. WCAG 2.2 added SC 2.5.8 Target Size (Minimum) at level AA, requiring targets of at least 24×24 CSS pixels — or enough spacing around smaller ones. Users with tremor, limited dexterity or touch screens are most affected. Target size is measurable, so it belongs in automated tests.
This guide belongs to visual regression & theme testing.
Spec reference
Permalink to "Spec reference"SC 2.5.8 Target Size (Minimum) (AA) — targets are at least 24 by 24 CSS pixels, except where:
- Spacing: undersized targets are positioned so that a 24-pixel diameter circle centred on each target’s bounding box does not intersect another target or another target’s circle.
- Equivalent: the function is available through another control on the same page that meets the criterion.
- Inline: the target is in a sentence or its size is constrained by line height of non-target text.
- User agent control: the size is determined by the browser and not modified by the author.
- Essential: a particular presentation is essential or legally required.
SC 2.5.5 Target Size (Enhanced) (AAA) raises the bar to 44×44.
When to use — and when not to
Permalink to "When to use — and when not to"Test target size on every grid and toolbar in the product, in every density. Dense layouts are where failures cluster.
Do not lean on the “equivalent” exception casually; it requires another control on the same page that performs the same function and meets the size. A row action available in a bulk toolbar is an equivalent only if the toolbar acts on that row in a comparable way. Do not count the 24-pixel minimum against the visible icon; it is the target (hit area) that matters.
The misapplication: row checkboxes rendered at 16 pixels with 4 pixels between rows, justified as “inline”. Grid checkboxes are not in a sentence; the inline exception does not apply.
Annotated code example
Permalink to "Annotated code example"// target-size.spec.js — measure targets and the spacing exception (SC 2.5.8)
test('grid targets meet 2.5.8', async ({ page }) => {
await page.goto('/invoices?density=compact');
const failures = await page.evaluate(() => {
const sel = 'button, a[href], input, select, [role="checkbox"], [role="button"], [role="columnheader"][aria-sort] button, [role="separator"][tabindex]';
const targets = [...document.querySelectorAll(`[role="grid"] ${sel}, table ${sel}`)]
.filter(el => el.offsetParent !== null)
.map(el => ({ el, r: el.getBoundingClientRect() }));
const small = targets.filter(t => t.r.width < 24 || t.r.height < 24);
const centre = (r) => ({ x: r.left + r.width / 2, y: r.top + r.height / 2 });
return small.filter(t => { // spacing exception: 24 px circles must not intersect
const c = centre(t.r);
return targets.some(o => o !== t && (() => {
const oc = centre(o.r), dx = c.x - oc.x, dy = c.y - oc.y, d = Math.hypot(dx, dy);
const otherSmall = o.r.width < 24 || o.r.height < 24;
return otherSmall ? d < 24 : d < 12 + Math.min(o.r.width, o.r.height) / 2; // approximation
})());
}).map(t => `${t.el.getAttribute('aria-label') || t.el.textContent.trim() || t.el.tagName} ${Math.round(t.r.width)}×${Math.round(t.r.height)}`);
});
expect(failures).toEqual([]);
});
/* Enlarge hit areas without changing visuals */
/* Wrong: margin: 4px around a 16 px checkbox does NOT enlarge its target */
.grid input[type="checkbox"] { inline-size: 16px; block-size: 16px; }
/* Right: a label wrapping the checkbox makes the whole 24 px box clickable */
.grid label.cb-wrap { display: inline-grid; place-items: center; min-inline-size: 24px; min-block-size: 24px; cursor: pointer; }
.icon-btn { min-inline-size: 24px; min-block-size: 24px; padding: 4px; }
.resize-handle::before { content: ""; position: absolute; inset: 0 -10px; } /* wider hit area */
The comment in the first CSS rule is deliberate: margins do not increase a target’s hit area. Wrap the checkbox in a label (or the cell) so the clickable area grows.
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"Target size is about pointer input; keyboard and screen reader users are unaffected directly. But fixes can affect them:
| Fix | Keyboard / AT consideration |
|---|---|
| Label wrapping the checkbox | Accessible name may change — keep it “Select INV-1042” |
| Larger icon buttons | No change; ensure focus ring fits inside the cell |
| Wider resize handles | Handles must remain keyboard operable (window splitter or column resize pattern) |
| Bigger touch areas via pseudo-elements | No change |
Integration context
Permalink to "Integration context"Dense grids interact with density settings — see segmented controls for view density and density tokens and target size in data grids. Mobile tables have stricter practical needs, covered in touch targets in mobile data tables. Resize handles also need keyboard support per keyboard accessible column resizing.
Gotchas
Permalink to "Gotchas"Bounding boxes versus hit areas. getBoundingClientRect measures the element, not pseudo-element hit areas. If you enlarge targets with ::before, measure the larger area in tests (or use elementFromPoint sampling).
Zoom. Target size is in CSS pixels, so browser zoom does not help or hurt the measurement — test at 100%.
Spacing calculations. The exact spacing rule uses circles centred on bounding boxes; approximations in tests can over- or under-report. Treat flagged items as candidates for review.
Common mistakes
Permalink to "Common mistakes"- 16-pixel row checkboxes with no wrapper.
- Using margins to “enlarge” targets.
- Claiming the inline exception for grid controls.
- Testing only the comfortable density.
Framework notes
Permalink to "Framework notes"MUI Checkbox has padding that already makes a 38–42 px target by default; compact grids often remove it — check the size="small" variant. AG Grid checkbox selection renders small checkboxes; wrap them via cell renderers or increase rowHeight. Angular Material density scales below -2 reduce touch targets; test each density you allow.
// MUI X: keep a 24 px minimum target in compact density
<DataGrid density="compact" slotProps={{ baseCheckbox: { sx: { p: '4px' } } }} />
Worked example
Permalink to "Worked example"A trading blotter grid at compact density had 14-pixel checkboxes, 18-pixel row action icons and 4-pixel resize handles. The automated test flagged 312 targets per page. Wrapping checkboxes in 24-pixel label cells, padding icon buttons, widening resize hit areas with pseudo-elements, and adding a column menu with “Resize column…” as an equivalent brought the count to zero, without changing the compact look. Mis-click complaints from traders on touch laptops fell sharply.
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Define minimum target tokens (24 px at AA; 44 px recommended for touch) and build them into checkbox, icon-button and handle components, so density changes padding around content but never shrinks hit areas below the minimum.
FAQ
Permalink to "FAQ"Is 24×24 the visible size or the clickable area?
The target — the area that responds to pointer input. Visuals can be smaller if the hit area is large enough.
Do row checkboxes need to be 24 px if rows are 32 px tall?
The checkbox target needs 24×24 or adequate spacing. A label or cell wrapper that is clickable is the easiest way.
Does 2.5.8 apply to mobile only?
No. It applies to all pointer input, including mouse.
What about 44 px?
That is AAA (2.5.5) and a good target for touch-first products.
Can I rely on the equivalent exception for resize handles?
If columns can also be resized by keyboard and via a menu that meets size requirements, yes.
How do I test hit areas enlarged with pseudo-elements?
Sample points around the element with document.elementFromPoint and check which element responds.
Related
Permalink to "Related"- Text spacing override testing — the other dense-grid stress test
- Touch targets in mobile data tables — small screens
- Density tokens and target size — designing it in