Skip to main content

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​

index.html
<!-- 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>

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.

What it does, in one sentence

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:

  1. They press Tab and land on the button.
  2. The screen reader has been told that button does not exist.
  3. 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 everyoneThe 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 focusablearia-hidden="true"
It invisible on screen but read to listenersVisually hidden ("sr-only") text

How the Hiding Methods Compare​

MethodSeen on screen?Read by screen readers?Focusable?
hidden or display: noneNoNoNo
visibility: hiddenNo (space kept)NoNo
aria-hidden="true"YesNoYes (danger)
inertYes (can be dimmed)NoNo
Visually hidden ("sr-only")NoYesYes, if it is a control
opacity: 0 or moved off-screen with no other careNoYesYes, if it is a control

Two things to notice:

  • aria-hidden and 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-hidden does 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.

Do not hide an icon that is the only label

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.

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.

modal.html
<!-- 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>

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.

Prefer inert, or the native dialog

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.

Browser support for inert

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:

style.css
.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;
}
index.html
<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.

Visually hidden is not "invisible ink"

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-labelledby or aria-describedby points to an element that is hidden (even with aria-hidden="true" or hidden), 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-hidden is still false. Listeners hear content that does not exist.
  • A panel is open on screen, but aria-hidden is still true. 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​

check.html
<!-- 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​

  1. Search your code for aria-hidden. For each one, ask: is it focusable, or does it contain something focusable?
  2. Tab through the page. Does focus ever land on something with no visible or spoken name?
  3. Open each dialog and menu. Can Tab reach the page behind?
  4. Turn on a screen reader. Walk over your icons. Do you hear noise like "graphic, star"?
  5. Browser developer tools: the Accessibility panel shows what is, and is not, in the tree for the selected element.
  6. Run an automated tool such as axe DevTools, Lighthouse, or WAVE. They flag many focusable aria-hidden cases.

Good Habits​

  • Use hidden or inert first. Only reach for aria-hidden for 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-hidden and 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, or inert
  • 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-hidden always 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-label values 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​

  1. aria-hidden="true" on a button or link. This is the big one: a ghost control.
  2. Hiding a whole container that contains links or buttons.
  3. Using aria-hidden to hide something "for everyone." It does not hide it on screen. Use hidden.
  4. Using aria-hidden alone for the page behind a dialog. Focus can still escape. Use inert.
  5. Hiding the only label of a control, such as the icon in an icon-only button.
  6. aria-hidden="false" expecting it to reveal something.
  7. display: none for visually hidden text. It removes it from screen readers too.
  8. Hiding headings or landmarks, which breaks navigation.
  9. Hiding alt text by putting aria-hidden on an image that carries meaning.
  10. Forgetting that hidden labels are still used, and then leaving stale text behind.
  11. Out-of-sync toggles: the screen shows one thing and aria-hidden says another.
  12. Never testing with a keyboard.

Practice Time​

Try these in your browser and your editor:

  1. Use the hiding methods lab. For each method, press the focus test, and write down what happens.
  2. In the modal lab, choose each mode in turn. Which mode lets focus escape, and why?
  3. Run the checker on the messy sample, fix every problem, and run it again.
  4. Search a page you built for aria-hidden. Find one use that is wrong.
  5. Give an icon-only button a name, and then hide its icon properly.
  6. Build a "Read more" link with visually hidden text, and listen to it with a screen reader.
  7. Replace a closed panel's aria-hidden toggle with the hidden attribute.
  8. Open your browser's Accessibility panel, select a hidden element, and look at the tree.
  9. Turn on a screen reader and walk through a page full of emoji. Which ones should be hidden?
  10. Bonus: build a modal with the native <dialog> element, then with inert, 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 &lt;dialog&gt; 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 hidden or inert to really switch something off. Use aria-hidden for 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.