How to fix a hidden element that can still take keyboard focus?
aria-hidden-focus page
The issue
A keyboard user tabs onto a control and hears nothing, or hears only “blank”. The link or button is still in the tab order, but aria-hidden="true" has removed it from what a screen reader announces. The user can activate something without knowing what it is.
The cause is a mismatch between two systems. aria-hidden hides an element from assistive technology but does nothing to keyboard focus, so anything focusable inside it stays reachable. It usually comes from a closed menu, a carousel slide or a modal that is hidden with aria-hidden but still sits in the page. WCAG 4.1.2 requires every control’s name and role to be available to assistive technology.
Scope
Every element with aria-hidden="true" is checked, whether or not it is otherwise hidden. It fails if the element itself is focusable or if anything inside it is. Probeo counts as focusable a link with an href, a <button>, <input>, <select>, <textarea> or <summary>, and any element with a tabindex, unless it is disabled or has tabindex="-1".
How to check you have it
- In probeo: the page reports
aria-hidden-focus, “ARIA hidden element must not be focusable or contain focusable elements”, with serious impact, on eacharia-hiddenelement that is focusable or contains a focusable element. - By hand: search the page source for
aria-hidden="true"and look inside each match for links, buttons, form fields andtabindex. Then tab through the page with a screen reader on and listen for stops that announce nothing. - With a tool: axe DevTools reports the same rule id. The accessibility pane in Chrome or Firefox DevTools shows whether a focused element is in the accessibility tree.
The fix
Take everything inside a hidden region out of the tab order, or stop hiding the region from assistive technology.
If the content should be hidden from everyone, as a closed menu is, hide it fully with the hidden attribute or display: none, which removes it from both the accessibility tree and the tab order. If it must stay rendered but unreachable, give each focusable element inside it tabindex="-1", or disabled for form controls, and restore them when the region is shown. If the content is meant to be used, remove aria-hidden.
<!-- Closed menu: hidden from everyone -->
<ul id="menu" hidden>
<li><a href="/pricing/">Pricing</a></li>
</ul>Done when: no element with aria-hidden="true" is focusable or contains a focusable element, and the check above comes back clean.
Do not
- Do not put
aria-hidden="true"on content a keyboard user can reach. - Do not hide a decorative icon inside a button by putting
aria-hiddenon the button itself; put it on the icon. - Do not rely on
opacity: 0or off-screen positioning to remove something from the tab order.
FAQ
Does the inert attribute fix this?
In browsers, inert removes a subtree from both the tab order and the accessibility tree, which is the right outcome. Probeo’s check reads tabindex and disabled but not inert, so an inert region that also carries aria-hidden="true" is still reported. Drop aria-hidden from an inert region, since inert already hides it.
Why is a decorative icon inside a link fine?
aria-hidden on the icon hides only the icon. The link itself is not hidden, so it keeps its name and stays announced. The rule fails only when the hidden element is, or contains, the focusable thing.
What about a modal that hides the rest of the page?
A common pattern sets aria-hidden="true" on the page behind an open modal. That page still contains links and buttons, so the rule fails unless they are also taken out of the tab order. Using inert on the background, or the native <dialog> element with showModal(), handles both at once.
Which product detects this
Probeo checks this on every page of your site as part of Site Clarity. This page explains one instance; the product tells you every page that has it.