WCAG 2.2 Focus Not Obscured: Fixing Sticky Headers and Cookie Banners
## The short answer
WCAG 2.2 success criterion **2.4.11 Focus Not Obscured (Minimum)** requires that when an element receives keyboard focus, it is not **entirely hidden** by author-created content such as a sticky header, sticky footer, cookie banner, or floating chat widget. At least part of the focused element must remain visible. This is an AA criterion. The related AAA criterion, 2.4.12, goes further and requires that **no part** of the focused element is obscured. The most common cause of failure is a sticky header that overlaps the element a keyboard user has just tabbed to.
## Why this is a real problem
Keyboard users navigate by pressing Tab and watching the focus indicator move. If a sticky header covers the top of the page, an element scrolled to just beneath it can receive focus while sitting underneath the header - invisible to the user. They cannot see where they are, so they cannot tell what pressing Enter will do. The same happens with sticky footers covering controls at the bottom, and with cookie or consent banners layered over content.
## What "not entirely hidden" means
The AA criterion (2.4.11) is satisfied as long as some portion of the focused element peeks out from behind the overlapping content. The AAA criterion (2.4.12) is stricter: the entire focused element must be visible, with nothing covering it. For most compliance programmes you target the AA bar, but designing for the stricter bar produces a noticeably better experience, because a partly-hidden control is still hard to read.
## The usual culprits
- **Sticky / fixed headers** that remain pinned as the page scrolls.
- **Sticky footers** or fixed action bars at the bottom of the viewport.
- **Cookie and consent banners** overlaid on the page.
- **Floating chat or help widgets** in a corner.
- **Skip-link or in-page anchor jumps** that scroll a target under a fixed header.
## How to fix it
The cleanest technical fix is **scroll-padding** on the scroll container. CSS `scroll-padding-top` (and `scroll-padding-bottom`) tells the browser to keep that much space clear when scrolling an element into view, so focusing or anchoring to an element parks it below the sticky header rather than under it. Set the padding to at least the header's height.
Other effective measures:
- Add `scroll-margin-top` to focusable targets that anchor links jump to.
- Ensure your sticky header has a defined, known height you can reference in the scroll-padding value.
- For overlays like cookie banners, trap nothing important behind them - or better, require the banner to be dismissed or interacted with before the rest of the page is reachable, so focus never lands beneath it.
- For floating widgets, keep them out of the natural tab path's visible region, or ensure their footprint never overlaps focusable content.
Beware dynamic header heights: headers that shrink on scroll, or expand on certain breakpoints, can defeat a hard-coded scroll-padding value. Tie the value to the actual rendered height, for example via a CSS custom property updated as the header resizes.
## Don't forget the focus indicator itself
2.4.11 is about the element being visible, but it works hand in hand with having a visible focus indicator in the first place (2.4.7 from earlier WCAG versions, plus the new 2.4.13 Focus Appearance at AAA). If you have removed default outlines without replacing them, fix that first - there is no point ensuring an invisible-styled control is unobscured.
## Testing it
- Tab through the whole page slowly from top to bottom and watch where focus lands. Is the focused control ever fully hidden by the header, footer, or a banner?
- Repeat with the cookie banner present, before dismissing it.
- Use in-page anchor links and confirm the target lands below the sticky header, not under it.
- Test at multiple zoom levels and viewport sizes - a header that is fine at desktop width may grow at narrow widths.
- Check with any chat widget expanded.
## Building it in
Because the fix is largely a layout concern, it belongs in shared layout components rather than individual pages. When we build enterprise-grade products at neart.ai, the sticky-header height and the matching scroll-padding live in the same layout primitive, so every page that uses the standard header inherits correct focus scrolling automatically. Centralising it avoids the situation where one new page reintroduces the bug.
## Practical takeaway
If you use any sticky or fixed UI, set `scroll-padding-top` (and bottom, if you have a sticky footer) to at least the height of that element, and tie the value to the real rendered height. Tab through every page with banners present to confirm the focused element is always at least partly visible - aim for fully visible to clear the stricter AAA bar.