Aria Hidden
You have probably met aria-hidden already, usually on a little icon: <span aria-hidden="true">β
</span>. It looks harmless, and used well, it is. But it is also one of the most misused attributes in ARIA, and a single wrong use can make a button invisible to a screen reader while still sitting in the keyboard's path.
aria-hidden="true" has one job: it tells assistive technology to ignore an element and everything inside it. It does not hide anything on screen. It does not stop the keyboard. It only edits what the screen reader is told. Once you understand that, you will know exactly when to use it, and when to reach for something else.
What You Will Learnβ
- What
aria-hidden="true"does, and what it does not do - The safest rule: never put it on something focusable
- When it is right: decorative icons, duplicated content, and hidden slides
- How it differs from
hidden,display: none,visibility: hidden,inert, and visually hidden text - How to treat the page behind a modal dialog
- How hidden content still affects names and descriptions
- How to check your pages
The Big Ideaβ
- HTML Code
- Hiding Methods Lab
<!-- Decorative icon: hide it, the text already says it all -->
<button type="button">
<svg aria-hidden="true" focusable="false" width="16" height="16"><!-- star --></svg>
Add to favourites
</button>
<!-- Decorative emoji next to real text -->
<h2><span aria-hidden="true">π</span> Rye bread</h2>
<!-- WRONG: a focusable control hidden from screen readers -->
<button type="button" aria-hidden="true">Close</button>
<!-- Better: hide it from EVERYONE if it should not be used -->
<button type="button" hidden>Close</button>
<!-- Page behind a modal: inert switches the whole area off -->
<main inert> ... </main>
<div role="dialog" aria-labelledby="dlg-title"> ... </div>
- Seen on screen: Yes
- In the accessibility tree (screen readers): Yes
- Can receive keyboard focus: Yes
The button is shown, it is in the accessibility tree, and the keyboard can reach it. This is the starting point.
The focus test is real: it tries to focus the actual button on this page. The three lines above describe what the standards and current browsers normally do, and they cannot be measured from inside the page. Details can vary a little between browsers and screen readers, so test the pages you build.
Pick each method in turn, then press the focus test. Watch what happens with aria-hidden: the button is still on screen, and focus can still land on it.
aria-hidden="true" removes an element, and everything inside it, from the accessibility tree. Nothing else changes: it is still drawn, still clickable, and still reachable with Tab.
The Rule That Matters Mostβ
The ARIA rules say it plainly: do not use aria-hidden="true" on a focusable element, or on an element that contains one.
Why? Think of what happens to a keyboard and screen reader user:
- They press Tab and land on the button.
- The screen reader has been told that button does not exist.
- They hear nothing, or something confusing like "blank." They are now focused on a control they cannot identify, and cannot tell why.
That is a ghost control. It is worse than a missing one, because the user feels lost.
Some browsers now warn in the developer tools, or even refuse to apply aria-hidden, when the focused element is inside a hidden area. Do not rely on that. Avoid the situation.
Fixing itβ
| If you want... | Use... |
|---|---|
| It gone for everyone | The hidden attribute (or display: none) |
| It visible but switched off (a dimmed page behind a dialog) | inert |
| It removed from the screen reader only, and it is not focusable | aria-hidden="true" |
| It invisible on screen but read to listeners | Visually hidden ("sr-only") text |
How the Hiding Methods Compareβ
| Method | Seen on screen? | Read by screen readers? | Focusable? |
|---|---|---|---|
hidden or display: none | No | No | No |
visibility: hidden | No (space kept) | No | No |
aria-hidden="true" | Yes | No | Yes (danger) |
inert | Yes (can be dimmed) | No | No |
| Visually hidden ("sr-only") | No | Yes | Yes, if it is a control |
opacity: 0 or moved off-screen with no other care | No | Yes | Yes, if it is a control |
Two things to notice:
aria-hiddenand visually hidden text are opposites. One hides from listeners but not viewers. The other hides from viewers but not listeners.- Only the first three rows remove focus.
aria-hiddendoes not.
(These are the normal results. Browsers and assistive technology can differ in small ways, so always test.)
Safe Usesβ
aria-hidden="true" is the right choice when an element is not focusable and would only add noise or repeat something a listener already has.
Decorative icons and emojiβ
If an icon sits next to text that already says the same thing, hide the icon. Otherwise, a screen reader may read out something like "star" or the emoji's name, before "Add to favourites."
<button type="button">
<svg aria-hidden="true" focusable="false" width="16" height="16"></svg>
Download
</button>
For inline SVG, add focusable="false" too. Old versions of Internet Explorer and Edge made SVG focusable by default, and the habit is harmless in modern browsers.
For an <img>, you do not need aria-hidden. Use an empty alt (alt="") for decoration, as covered in Alt Text.
If a button contains only an icon, hiding the icon leaves the button with no name, which is a serious failure. Give the button a name with aria-label or visually hidden text, and then hide the icon. See Accessible Names.
<button type="button" aria-label="Close">
<svg aria-hidden="true" focusable="false"></svg>
</button>
Decoration that would be read as noiseβ
Dividers made of characters (* * *), fancy bullets, "breadcrumb" separators (> or /), and repeated arrows are good candidates. For lists and breadcrumbs, prefer CSS (::before with a separator) so there is no extra text in the page at all.
Duplicate contentβ
Sometimes the same text appears twice: a visible, styled version, and a plain one for screen readers. Hide one of them so it is not read twice.
Carousel slides and tab panels that are off-screenβ
A slide that is not showing should be hidden from everyone, not only from listeners. Use hidden or display: none. If you must keep it on the page for an animation, use inert so the controls inside cannot be reached, and (optionally) aria-hidden="true" for older assistive technology. More in ARIA Roles for tabs and carousels.
The page behind a modalβ
A dialog that takes over the screen should make the page behind it unavailable. The modern, simple way is inert on the page behind. Try it in the lab below.
- HTML Code
- Modal Background Lab
<!-- The page behind -->
<div id="page">
<button type="button" id="open">Open dialog</button>
...
</div>
<!-- The dialog (a sibling, not inside the page) -->
<div role="dialog" aria-labelledby="dlg-title" id="dlg" hidden>
<h2 id="dlg-title">Delete recipe?</h2>
<button type="button" id="close">Close</button>
</div>
<script>
const page = document.getElementById("page");
const dlg = document.getElementById("dlg");
document.getElementById("open").addEventListener("click", () => {
page.inert = true;
dlg.hidden = false;
document.getElementById("close").focus();
});
document.getElementById("close").addEventListener("click", () => {
dlg.hidden = true;
page.inert = false;
document.getElementById("open").focus();
});
</script>
The page behind
Open the dialog, then press Tab several times. With "Do nothing" or "aria-hidden only," focus can leave the dialog and wander into the page behind. With inert, it cannot. This small dialog is only a demonstration. A real modal also needs a focus trap or inert background, a close button, Esc handling, and focus returned to the opener. See Focus Management.
In the lab, choose "aria-hidden only," open the dialog, and press Test: try to focus "Recipes" behind. Focus escapes. That is the trap. Now try inert: blocked.
The native <dialog> element, opened with showModal(), already makes the rest of the page inert and traps focus for you. If you can use it, start there. If you cannot, set inert on the content behind the dialog. Use aria-hidden there only as a backup for very old browsers, and never as the only protection.
inert is supported in the current versions of the major browsers, but older browsers and some embedded web views may lack it. Check a current support table such as MDN or Can I Use, and test in the browsers your readers use.
Visually Hidden Text: The Opposite Trickβ
Sometimes a listener needs more than a viewer sees. A common example is an icon-only button, or a link that says "Read more" with a hidden suffix: "Read more about rye bread."
Hide the text visually, keep it in the accessibility tree. This is often called a "sr-only" or "visually-hidden" class:
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
}
<a href="/rye">Read more<span class="visually-hidden"> about rye bread</span></a>
Do not use display: none or hidden for this. Those remove the text from screen readers too. And do not use opacity: 0 or font-size: 0, which behave unpredictably.
Use it for short, real help for listeners. Do not hide long text, keyword lists, or content that sighted users would object to if they could see it. It is poor practice and search engines can penalise it.
aria-hidden and Namesβ
Hidden does not always mean ignored. Two rules to remember:
- A hidden element can still name something. If
aria-labelledbyoraria-describedbypoints to an element that is hidden (even witharia-hidden="true"orhidden), the text is still used. That is useful, because you can keep a label off-screen. It is also a trap if you forgot the label was there. See aria-labelledby. - Children are hidden too. You cannot hide a parent and show a child. Once
aria-hidden="true"is on a container, everything inside it is gone, whatever the children say.
aria-hidden="false" Is Not a Fixβ
You might expect aria-hidden="false" to say "show this." It does not work that way:
- If an element is hidden another way (
display: none,hidden),aria-hidden="false"does not bring it back. - Its effect inside a parent that is
aria-hidden="true"is not reliable.
If you want something visible to everyone, remove the attribute and fix the real cause.
Keep It in Syncβ
If you toggle aria-hidden with JavaScript, tie it to the same state as the visual change. The common bug is one of these:
- A panel is closed on screen, but
aria-hiddenis stillfalse. Listeners hear content that does not exist. - A panel is open on screen, but
aria-hiddenis stilltrue. Listeners miss the main content.
Better still, use hidden or inert. They change what the screen shows, what the keyboard can reach, and what the screen reader hears, together. There is one state to keep correct, not three.
Check Your Pagesβ
- HTML Code
- aria-hidden Checker
<!-- Things the checker looks for -->
<button aria-hidden="true">Close</button> <!-- focusable and hidden -->
<div aria-hidden="true"><a href="/x">Menu</a></div> <!-- link inside -->
<img src="a.png" alt="Sales up" aria-hidden="true"> <!-- alt hidden -->
<div aria-hidden="false">Maybe</div> <!-- not reliable -->
<svg viewBox="0 0 10 10"></svg> <!-- icon with no name -->
Run the messy sample, fix every problem in the box, then try the fixed version.
Check it by handβ
- Search your code for
aria-hidden. For each one, ask: is it focusable, or does it contain something focusable? - Tab through the page. Does focus ever land on something with no visible or spoken name?
- Open each dialog and menu. Can Tab reach the page behind?
- Turn on a screen reader. Walk over your icons. Do you hear noise like "graphic, star"?
- Browser developer tools: the Accessibility panel shows what is, and is not, in the tree for the selected element.
- Run an automated tool such as axe DevTools, Lighthouse, or WAVE. They flag many focusable
aria-hiddencases.
Good Habitsβ
- Use
hiddenorinertfirst. Only reach foraria-hiddenfor decoration and duplicates. - Never on focusable elements, or on a parent of one.
- Never on the
<body>or on the main content. - Hide icons that sit beside text, but give icon-only buttons a name.
- Add
focusable="false"to decorative inline SVG. - Use visually hidden text when only listeners need more.
- Keep
aria-hiddenand the visible state in sync, or avoid toggling it at all. - Test with a keyboard and a screen reader.
Accessibility Checklistβ
- No
aria-hidden="true"on, or around, anything focusable - Hidden slides, panels, and menus use
hidden,display: none, orinert - The page behind a modal is inert (or the native
<dialog>is used) - Decorative icons and emoji are hidden, and their text is still there
- Icon-only buttons and links still have a name
- No
aria-hidden="false"used as a way to show things - No important content (headings, landmarks, alt text) is hidden by mistake
- Visually hidden text is short, real, and helpful
-
aria-hiddenalways matches the visible state - Tested with a keyboard and a screen reader
Security and Privacy Notesβ
- Hiding is not protecting.
hidden,aria-hidden,inert, and CSS hiding only change how a page is shown. The content is still in the HTML. Anyone can read it with "view source," developer tools, or a scraper. Never put passwords, keys, or private data in a page just because you hid it. - Invisible text is still read. Text that is hidden from sight, but kept for screen readers, can also be read by search engines and by automated tools such as AI crawlers and summarisers. Do not hide instructions or claims there that you would not show to a person.
- Do not hide important notices from listeners (cookie consent choices, charges, terms). Hiding information from some users while showing it to others is unfair, and may be a legal problem in some places. This is not legal advice. Check the rules that apply to you.
- Hiding something does not disable it for a determined user. Disabled or hidden buttons can be re-enabled in developer tools. Always check permissions on the server.
Global Audience Notesβ
- Emoji are read in the listener's language, and different screen readers read them differently. A decorative emoji can be noisy ("bread," "loaf of bread") in every language. If it only decorates, hide it. If it carries meaning (a status, a mood), give it text.
- Translation tools translate hidden text too. Visually hidden text and
aria-labelvalues are usually translated with the page, so write them as short, plain words that translate well. - Right-to-left languages: arrows and chevrons may need to be mirrored. Decorative ones are hidden from listeners either way, but check them visually.
- Icon fonts can fail on slow networks, showing empty boxes or odd letters. Where possible, use inline SVG, and keep a real text label beside the icon.
- Not every device has inert support. If your readers use older phones or embedded browsers, test your modals there.
Common Mistakes to Avoidβ
aria-hidden="true"on a button or link. This is the big one: a ghost control.- Hiding a whole container that contains links or buttons.
- Using
aria-hiddento hide something "for everyone." It does not hide it on screen. Usehidden. - Using
aria-hiddenalone for the page behind a dialog. Focus can still escape. Useinert. - Hiding the only label of a control, such as the icon in an icon-only button.
aria-hidden="false"expecting it to reveal something.display: nonefor visually hidden text. It removes it from screen readers too.- Hiding headings or landmarks, which breaks navigation.
- Hiding
alttext by puttingaria-hiddenon an image that carries meaning. - Forgetting that hidden labels are still used, and then leaving stale text behind.
- Out-of-sync toggles: the screen shows one thing and
aria-hiddensays another. - Never testing with a keyboard.
Practice Timeβ
Try these in your browser and your editor:
- Use the hiding methods lab. For each method, press the focus test, and write down what happens.
- In the modal lab, choose each mode in turn. Which mode lets focus escape, and why?
- Run the checker on the messy sample, fix every problem, and run it again.
- Search a page you built for
aria-hidden. Find one use that is wrong. - Give an icon-only button a name, and then hide its icon properly.
- Build a "Read more" link with visually hidden text, and listen to it with a screen reader.
- Replace a closed panel's
aria-hiddentoggle with thehiddenattribute. - Open your browser's Accessibility panel, select a hidden element, and look at the tree.
- Turn on a screen reader and walk through a page full of emoji. Which ones should be hidden?
- Bonus: build a modal with the native
<dialog>element, then withinert, and compare how they behave.
Quick Recapβ
Click a question to check your answer.
1. What does aria-hidden="true" do?
It removes an element, and everything inside it, from the accessibility tree, so screen readers ignore it. It does not hide it on screen, and it does not stop the keyboard.
2. Why must you never put it on a focusable element?
A keyboard user can still Tab onto the control, but the screen reader has been told it does not exist. They land on a "ghost" with no name, and cannot tell what it is.
3. Which should you use to hide something from everyone?
The hidden attribute, or display: none. Both remove it from the screen, the accessibility tree, and the focus order.
4. What is the best way to switch off the page behind a modal?
Set inert on the content behind the dialog, or use the native <dialog> element with showModal(). aria-hidden alone does not stop focus escaping.
5. How is visually hidden text different from aria-hidden?
Visually hidden text is invisible on screen but still read by screen readers. aria-hidden is the opposite: visible on screen, but ignored by screen readers.
6. Does a hidden element still work as a label?
Yes. If aria-labelledby or aria-describedby points to a hidden element, its text is still used for the name or description.
Key Takeawaysβ
aria-hidden="true"only edits what screen readers hear. It does not hide anything on screen, and does not block the keyboard.- Never use it on, or around, a focusable element. That creates ghost controls.
- Use
hiddenorinertto really switch something off. Usearia-hiddenfor decoration and duplicates. - Hide decorative icons, but name icon-only buttons.
- The page behind a modal should be
inert, or use the native<dialog>. - Visually hidden text is the opposite trick: seen by listeners, not by viewers.
- Hidden is not secure. The content is still in the page.
- Test with a keyboard and a screen reader.
Next, learn how to announce changes to people who cannot see them happen, in aria-live.