Hit Area

Why hover states flicker between elements, and how to fix it, WCAG 2.5.8

By Kacper Szarkiewicz

There's a gap between interactive elements - a few dead pixels where nothing reacts to your pointer. Usually invisible. You don't notice it until something animates: hover states flicker off, transitions reset, things feel broken.

The fix is to extend the hit area - the clickable box - beyond the visible bounds of the element.

Hit areas also have a formal floor. WCAG 2.5.8 requires interactive targets to be at least 24×24 CSS pixels (Level AA). The stronger Level AAA criterion raises that to 44×44 - the threshold most often cited in practice. Apple recommends 44×44; Google 48×48.

Padding

The video and live example below show scroll indicators - 3px-tall dots stacked with 10px gaps between them. Move the cursor from one dot to the next and it briefly crosses dead space - the active dot's expand animation snaps back before the next one takes over. Five pixels of vertical padding on each button stretches its clickable box into the gap, so the hit areas tile edge-to-edge and the active state hands off cleanly.

Without padding, the cursor falls into the gap between dots and the highlight resets. Padding closes the gap.

Scroll indicators - live example

Without hit area
With hit area
py-[5px]

::before pseudo-element

Padding doesn't work when the visible box has its own background or border - like a nav menu item that highlights on hover. Extending the box would extend the highlight too. There, an invisible ::before pseudo-element extends the hit area without disturbing the design. It's visually empty by default but still receives hover events.

Let's take a look at the navigation menu from our Direction Aware Navigation article.

Moving between pills, the menu collapses mid-transition. The pseudo-element extends each item's hit area into the gap.

Navigation bar - live example

Without hit area
With hit area
before:absolute before:inset-x-[-8px] before:inset-y-0 before:content-['']

One rule: the element needs position: relative so the absolutely-positioned pseudo-element is contained. In Tailwind that's just relative on the parent.

Negative margin + padding

Section headings on this site use padding to extend their hit area left, into where the copy icon floats -ml-6 shifts the heading's box, pl-6 keeps the visible text in its original spot, and the 24px of left padding becomes the bridge to the icon. Hover slowly from the icon into the heading, or try disabling it.

The cursor crosses the gap between icon and heading and the copy button disappears. Negative margin plus padding bridges them.

Section heading - live example

Without hit area

Animated arrow

Below the nav bar sits a small white arrow that slides left and right to point at whichever trigger is open.

With hit area

Animated arrow

Below the nav bar sits a small white arrow that slides left and right to point at whichever trigger is open.

-ml-6 pl-6

Newsletter

Stay updated with my latest articles and projects. No spam, no nonsense.