Understanding WCAG 1.4.13

What is it?

WCAG 1.4.13 Content on Hover or Focus is a Level AA criterion, added in WCAG 2.1.

When hovering or focusing something makes extra content appear, that content has to be dismissible, hoverable, and persistent. Tooltips, sub-menus, and popovers all count.

Why it matters

Content that appears on hover is easy to build and easy to get wrong.

It can vanish before you’ve finished reading. It can sit on top of the thing you were trying to read. It can refuse to let you move your pointer onto it. The information is there, and you still can’t get to it.

None of these are exotic bugs. They come from ordinary CSS and ordinary JavaScript doing exactly what they were told, on patterns that look fine when you build them and fail the moment someone interacts with them differently than you did.

Who it affects

Anyone can lose a tooltip on the way to it. For some people it happens every time.

People who are…

The three rules

Dismissible. You can get rid of it without moving your pointer or your focus.

Hoverable. You can move the pointer onto the content itself without it disappearing.

Persistent. It stays until hover or focus is removed, you dismiss it, or the information stops being valid.

Common failures

  1. A gap between the trigger and the tooltip. The tooltip sits a few pixels below the icon. Move toward it, the pointer crosses dead space, and it’s gone. The pointer is briefly over neither the trigger nor the tooltip, hover is lost, and the tooltip hides before the pointer arrives.
  2. The sub-menu that closes on the way there. You move diagonally toward an item and pass over a sibling on the way. The sub-menu swaps or shuts before you arrive. The more items in the menu, the harder the diagonal is to complete.
  3. The tooltip on a timer. It shows for a few seconds, then hides itself. You’re still hovering, and you never dismissed it. Reading speed varies enormously between people, and a timer tuned to how fast the developer reads will always be too short for somebody.
  4. No way to get rid of it. A large popover covers the content underneath. Escape does nothing, and the only way out is to move the pointer somewhere else entirely. If the thing you need to read is behind the popover, you’re stuck.

Dismissible has two exceptions

You don’t need a dismiss mechanism if the content communicates an input error, or if it doesn’t obscure or replace other content.

So a small tooltip sitting in empty margin space is fine without a dismiss mechanism. The rule applies when the tooltip covers something.

Worth being precise about this one: the exception only lets you off Dismissible. Hoverable and Persistent still apply to that tooltip in the margin.

Solution

Close the gap with a wrapper

Put the hover state on a wrapper that holds both the trigger and the tooltip. Hovering the tooltip keeps the wrapper hovered, so the tooltip stays open.

.tooltip-wrapper:hover .tooltip,

.tooltip-wrapper:focus-within .tooltip {

display: block;

}

:focus-within only fires if the trigger can actually take focus. If your trigger is a styled <span> or a bare icon, give it a real <button> first, or the keyboard half of this never runs.

Close the gap with a transparent border

The wrapper keeps the tooltip open once you’re on it, but it doesn’t help you get there. If a margin separates the two, the pointer still crosses dead space on the way.

A margin between the trigger and the tooltip is dead space. Build the gap from a transparent border instead, and the pointer never loses hover.

.tooltip {

margin-top: 0;

border-top: 1rem solid transparent;

background-clip: padding-box;

}

The border area is transparent but still part of the element, so the pointer stays on the tooltip the whole way across. background-clip: padding-box stops the tooltip’s background painting into that border, so the gap still looks like a gap.

Let the user decide when it goes

Delete the auto-hide timer. The criterion allows exactly three reasons for the content to disappear on its own terms: hover or focus is removed, the user dismisses it, or the information is no longer valid.

A timer is none of those.

Give it an Escape key

One keydown handler covers Dismissible for most tooltips and popovers.

If the content is a popover, you get this for free. A popover with popover=”auto” dismisses on Escape and on a click outside.

Give sub-menus a moment

Don’t close a sub-menu the instant the pointer leaves its parent. A short close delay lets a diagonal path reach its destination.

Larger menus go further and watch the triangle between the pointer and the sub-menu to work out whether someone is still heading there.

What’s excluded

How to test

Closing

Automated checks can’t do much with this one. It depends on pointer paths and timing, so you find it with your own hands.

Every fix here is small, though: a wrapper, a transparent border, a deleted timer, one key handler.

The content appeared because someone asked for it. Let them decide when it goes.

Learn more

Read the full breakdown: a20y.com/wcag-plain-english/1-4-13-content-on-hover-or-focus

Want more clear and actionable WCAG breakdowns? Check out: wcagInPlainEnglish.com