Skip to content

Accessibility

Focus not obscured: WCAG 2.4.11 explained

Sticky headers, cookie banners and chat widgets can hide the control a keyboard user just focused. How to test for WCAG 2.4.11 and fix it in CSS.

Your UX Agent teamProduct
9 min read

Last updated

WCAG 2.2 success criterion 2.4.11, Focus Not Obscured (Minimum), requires that, when a control receives keyboard focus, it is not entirely hidden by content the author added. Sticky headers and footers, cookie banners and chat widgets are common causes. It is a Level AA criterion, new in WCAG 2.2.

Our checklist has a failing and a passing example for 2.4.11.

This guide takes the overlays that most often hide focus one by one, with the fix for each, and ends with a short keyboard test.

What 2.4.11 requires

The criterion reads: “When a user interface component receives keyboard focus, the component is not entirely hidden due to author-created content.” Two notes narrow what it covers:

  • Content the user can move: if the user can reposition content, only its starting position counts.
  • Content the user opened: it may cover the focused control, as long as the user can reveal the control without moving focus, for example by pressing Escape to close it.

The criterion is about the focused control itself. Whether its focus indicator can be seen at all is a different criterion, 2.4.7 Focus Visible. A focus indicator drawn inside the control, or a change to the control itself, counts as part of the control. 2.4.11 is one of the criteria a site at WCAG 2.1 AA has to add to meet WCAG 2.2.

Partly hidden or entirely hidden

  1. The link is fully below the header: it passes 2.4.11 and the AAA criterion 2.4.12.
  2. The header covers the top half of the link: it passes 2.4.11, because the link is not entirely hidden, and fails 2.4.12, which allows no part of it to be hidden.
  3. The header covers all of the link: it fails both.
The same focused link under a sticky header, at three scroll positions.

A partly covered control passes at Level AA, but the W3C’s guidance is that, for the best visibility, none of it should be covered. A semi-transparent or blurred overlay that sits over a focused control may pass 2.4.11, because the control is not entirely hidden, and still fail 1.4.11 Non-text Contrast if the focus indicator loses its contrast underneath. We cover contrast for text, controls and focus indicators in a separate guide.

Five overlays that hide focus

When keyboard focus moves to a control outside the visible area, the browser scrolls it into view. A sticky or fixed overlay does not move with the page, so the browser can scroll a control right underneath it. And a control that is already in the window, but under the overlay, is not scrolled at all. Most fixes either tell the browser how much space the overlay takes or keep the overlay out of the way.

Tabbing back up the page, the previous link can end up at the top of the window, under the header.

Can fail: an 80px sticky header with nothing reserved
.site-header {  position: sticky;  top: 0;  z-index: 1;  height: 80px;  background: #fff;}
Passes: scroll padding keeps focused content below the header
:root {  --header-height: 80px;}​.site-header {  position: sticky;  top: 0;  z-index: 1;  height: var(--header-height);  background: #fff;}​html {  scroll-padding-top: calc(    var(--header-height) + 8px  );}

scroll-padding on the page’s scroll container tells the browser which part of the window is covered, so it scrolls a focused control clear of that area. W3C lists this as technique C43. Check the result with the keyboard test below. If the header changes height at other screen widths, change the custom property in the same media query.

Sticky footer and bottom bars

A fixed bar at the bottom of the window, such as an “Add to basket” bar on a product page, covers links as you tab down the page. The fix mirrors the header’s, with one addition: padding at the bottom of the page, so the last links can scroll above the bar.

Passes: room for a 64px bottom bar
:root {  --bar-height: 64px;}​.bottom-bar {  position: fixed;  inset: auto 0 0 0;  height: var(--bar-height);  background: #fff;}​html {  padding-bottom: var(--bar-height);  scroll-padding-bottom: calc(    var(--bar-height) + 8px  );}

On many European sites, the cookie consent banner is the first thing a visitor sees. A banner fixed to the bottom of the window that does not take focus, and lets Tab move on into the page behind it, fails 2.4.11 as soon as it entirely covers a focused link.

Three ways to pass: the first is a modal banner. Focus moves into it when it opens, and the page behind it cannot be reached until it closes. The W3C notes that a properly built modal dialog always passes 2.4.11.

Passes: a modal consent dialog
<dialog id="consent" aria-labelledby="consent-title">  <h2 id="consent-title">Cookies on this site</h2>  <p>We use cookies for analytics only if you allow them.</p>  <form method="dialog">    <button value="reject">Reject all</button>    <button value="accept">Accept all</button>  </form></dialog>​<script>  document.getElementById("consent").showModal();</script>

showModal() moves focus into the dialog and makes the rest of the page inert until it closes. Each button in the method="dialog" form closes it and sets the dialog’s returnValue to the button’s value. Escape also closes a modal dialog by default and leaves returnValue as it was, the empty string here; treat that as Reject all. The second way is a banner that stays non-modal but reserves its own space: while it is shown, set padding-bottom and scroll-padding-bottom on the page to its height, as for the bottom bar. The third is a banner in the normal flow of the page, at the top, which scrolls away like any other content.

Chat launcher and chat window

A chat launcher button fixed to a corner can cover a control near the edge of the window, such as the last link in a footer column. Reserve the space from the bottom of the window to the launcher’s top edge with padding-bottom and scroll-padding-bottom on the page, as for the bottom bar, or keep it clear of content.

An open chat window that the visitor opened is user-opened content. It may cover the focused control if the visitor can reveal the control without moving focus, for example by pressing Escape to close it or by scrolling with the keyboard. A chat window that opens on its own after a delay is not user-opened, so it must not entirely hide the control that has focus.

Dialogs and toasts that appear on their own

A non-modal dialog, a “Subscribe to our newsletter” box or a toast that appears without the visitor doing anything can land on top of the focused control. Either make it modal, keep it away from content, or move focus into it and close it when focus leaves it. The W3C lists closing on loss of focus as a way to pass.

How to test it

  1. Load the page and tab from the top to the bottom, then press Shift+Tab back up to the top. At each stop, check that you can see at least part of the focused control.
  2. Repeat at a phone width and a desktop width; headers and bars often change size between them.
  3. Repeat with the browser zoomed to 200 percent, where a sticky header takes up more of the window.
  4. Repeat before you answer the consent banner, with the chat window open and closed, and after waiting for anything that appears late, such as a newsletter box.
  5. For Level AAA, 2.4.12, check that no part of the focused control is covered.

axe-core 4.14.0 has no rule for 2.4.11 or 2.4.12. Testing it automatically means pressing Tab and checking what covers each control as it takes focus, which a scan of the page’s markup cannot do. User-opened content, and the states a page can be in, need a person.

Frequently asked questions

Does a partly hidden focused control fail WCAG?

Not at Level AA. 2.4.11 fails only when the control is entirely hidden. At Level AAA, 2.4.12 fails when any part of it is hidden.

Does 2.4.11 apply to the focus indicator?

It applies to the focused control, including a focus indicator drawn inside it. Whether the focus indicator is visible is 2.4.7 Focus Visible, and an overlay that dims the indicator can fail 1.4.11 Non-text Contrast.

Does a cookie banner fail 2.4.11?

Only if it entirely hides a control that has focus. A modal banner, which takes focus and keeps it until it is closed, passes.

How do I fix focus hidden behind a sticky header?

Set scroll-padding-top on the page’s scroll container, usually the html element, to the header’s height plus a little space. Then check with Tab and Shift+Tab that focused controls stay clear of the header.

Can automated tools test 2.4.11?

Partly. A tool has to press Tab and Shift+Tab and check what covers each control as it takes focus. axe-core 4.14.0 has no rule for it. Content the user opens, and banners or dialogs that appear later, need a person.