Phone

+61 439 645 180

Email

info@a11ypals.com.au

Ah brain’s… Wait, what? Where did they go?

If you have been using ARIA in your web development, you have probably at some stage used the aria-hidden attribute. Aria-hidden is to web developers as… well brains are to zombies. It’s alluring, it’s bright, it’s glowing, and “we wants it.” The problem however, is that what we want, is not always what we get (or even half as alluring as we first thought).

Imagine you’re navigating a digital world teeming with vibrant landscapes and bustling with activity. but lurking in the shadows, concealed from sight, are the “aria-hidden zombies”.

Yes, you heard it right. These invisible creatures, unlike brain eating zombies, who die online (especially if they spend much time on Reddit), the aria-hidden zombies are here to eat your code and ruin your accessibility.

Picture a screen reader user waking up and reaching our their fingertips to their device and screen readers for the news of a new day. Everything seems perfect until suddenly, they encounter an element marked aria-hidden="true". This element, while visible to the sighted screen reader users, becomes a zombified trap for those using assistive technologies. It’s there, but it’s not really there—an empty shell, a ghostly figure, an aria-hidden zombie, who wants to take their access away!

The apocalypse and how it starts

Far away, in the distant night sits web developers. Munching on pizza, screaming at any small sliver of moonlight filtering through the torn holes in their window blinds.

Today our web warrior learned of a new thing called ARIA, and how it can make code more accessible for people with disabilities. They also came to the realisation that blind screen reader users are not interested in every decorative image that comes there way. The developers employ their new shiny toy. The aria-hidden="true" property. They apply it to an image and boom! The zombies eat it!

But danger lurks!

Aria-hidden zombies multiply quickly. They can be benign at first – a decorative image which could have been hidden with alt=”” instead, but the noble web developer chose ARIA, they chose war.

Before you know it you are using aria-hidden="true" indiscriminately. The zombie hoards grow. You have lost interactive controls, buttons, divs dressed to look like buttons, informative images, and some users sanity. Lost forever to these hungry insatiable zombies.

Your users, your customers, are banging on your “Add to cart” button, and nothing happens! The aria-hidden zombies devoured it because somehow you set aria-hidden="true" on the form controls.

No one hears your customers scream, because you used aria-hidden="true" on your feedback forms submit button too.

Who are the aria-hidden zombies and how did they get here?

The aria-hidden zombies were born from a realisation that not every piece of content on a website is useful to every user. Sometimes pages are complex and the complexity needs to be reduced to make it more effective. In fact providing some information to various users of assistive technology can make their access more difficult.

For example if every menu item on a webpage is in a bulleted list which has a cute icon of a cat with different facial expressions, screen reader users want to know where a page links to, without listening to detailed descriptions of the facial expressions of a feline.

The problem with aria-hidden Zombies is they generally stem from poor coding and developer misunderstandings. This can be caused by

  • Developers not having adequate knowledge of how the feature works especially when concerning child elements
  • Browsers implementing the spec differently (in some cases as they try to ‘help’ assistive technologies) meaning different results are seen between browsers such as Chrome versus FireFox
  • Inadequate testing for accessibility. Limiting testing to one browser or one assistive technology when the tester does not understand how other technologies operate with this aria-hidden="true"

How do we fight aria-hidden zombies?

Fending off the aria-hidden Zombies isn’t about eliminating aria-hidden="true" altogether. It’s about using it wisely and sparingly. Building a fort around the zombies, and feeding them sometimes weird, asinine, ugly but interesting – visual design which we put on the page to make it “aesthetic.” Let’s give them that stuff we don’t need everyone to see.

What are safe usages of aria-hidden

aria-hidden="true" can be safely applied to non-interactive elements which do not enhance the experience for screen reader users or items which detract from screen reader users experience. This includes

  • Decorative images
  • Icons
  • Visual effects e.g. backgrounds, breaks, borders and animations
  • Redundant text e.g. when the same text is already included in the accessible label provided to screen reader users.
  • Off screen and hidden content – with caution
  • Modals and dialogs

Note on safe use

While it is safe to use aria-hidden="true" on decorative images, by doing so, a developer is not following best practice. The first rule of ARIA is to not use ARIA when the same function can be provided through semantic HTML. To suppress decorative images a user can simply specify and empty alt text tag (alt=””), so using aria-hidden="true" in most scenarios is redundant.

What are unsafe and problematic usages of aria-hidden?

Every now and then aria-hidden zombie shows up somewhere it shouldn’t. A developer may have set the property on

  • Interactive elements
  • Selective content
  • Items in a disabled (still visible but unselectable) state
  • Items where semantic HTML to perform the same task as aria-hidden="true"
  • Interactive child elements where the parent is hidden

Common ways developers create aria-hidden zombies without realising include

  • Failing to update the aria-hidden="true" property on a components when JavaScript is controls it’s visual display
  • Forgetting that aria-hidden="true" impacts child elements
  • Confusing aria-hidden="true" property with the role="presentation" or CSS display:none

JavaScript Zombie

Our aria-hidden zombies love their coffee, they can demolish JavaScript code in seconds, when they encounter forgetful developers.

Example 1 – aria-hidden is not toggled back on.

This example shows visually toggling an input field when a button is pressed by calling the “toggleInput” function. This function however fails to change the aria-hidden="true" property set on the text input “myInput”. This means while the content can be on screen content is hidden from the accessibility tree.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Example 1 - Toggle Input Field Visibility without changing aria-hidden</title>
    <style>
        .hidden {
            display: none;
        }
    </style>
</head>
<body>
    <button id="toggleButton" onclick="toggleInput()">Show Input Field</button>
	<div id="inputContainer" class="hidden">
		<label id="inputLabel" for="myInput" >Input text field</label> 
		<input type="text" id="myInput" aria-hidden="true" placeholder="Enter text here">
	</div>

    <script>
        function toggleInput() {
            const inputContainer = document.getElementById('inputContainer');
            const toggleButton = document.getElementById('toggleButton');
            const isHidden = inputContainer.classList.contains('hidden');

            if (isHidden) {
                inputContainer.classList.remove('hidden');
                toggleButton.textContent = 'Hide Input Field';
				inputContainer.setAttribute("false"); 
            } else {
                inputContainer.classList.add('hidden');
                toggleButton.textContent = 'Show Input Field';
				inputContainer.setAttribute("true");
            }
        }
    </script>
</body>
</html>

If you open the code in FireFox, start your screen reader (e.g. NVDA), press the “Show Input Field” button and finally open the elements list for the page, you will see that the only form field identified in the elements list is the toggle button itself. The input field is not shown and can not be reached by screen reader navigation shortcuts.

Screenshot of the code example in a browser window showing the "Hide Input Field" button and "Input text field" input displayed. The elements list is open on the form fields tab and only the "Hide Input Field" button is shown.

This aria-hidden Zombie can be killed by updating the toggleInput function to include setting the aria-hidden attribute on the “myInput” element depending on the toggle state.


        function toggleInput() {
            const inputContainer = document.getElementById('inputContainer');
            const toggleButton = document.getElementById('toggleButton');
            const isHidden = inputContainer.classList.contains('hidden');

            if (isHidden) {
                inputContainer.classList.remove('hidden');
                toggleButton.textContent = 'Hide Input Field';
		myInput.setAttribute("aria-hidden", "false"); 
            } else {
                inputContainer.classList.add('hidden');
                toggleButton.textContent = 'Show Input Field';
		myInput.setAttribute("aria-hidden", "true");
            }
        }

Following the same process to check the element list as you did previously, you will also see the fields toggle in the element list.

Screenshot of the code example in a browser window showing the "Hide Input Field" button and "Input text field" input displayed. The elements list is open on the form fields tab and  the "Hide Input Field" button is shown as well as the "Input text field" input

Recursion and child Zombies

Child aria-hidden zombies are the cutest of things, well up until they tear all your interactive content away from your customers with little warning. But still, you have to admit they appeared harmless and cute initially.

aria-hidden="true" when set on a component, will also be set on child components great for what appears to be uncluttered code, but also great at creating aria-hidden zombies.

Example 2 – hidden div

If you have a div which contains interactive elements such as a button, link and input field. If you set aria-hidden="true" on the div this property will carry to all children. So the button, link and input field will not be accessible to assistive technologies so you will not see these items in the element list.

<div aria-hidden="true">
    <button onclick="alert('Brains!')">Say Brains</button>
    <a href="#section">Go to Section</a>
    <input type="text" placeholder="Enter text here">
</div>
Screenshot of the code example in a browser window showing the "Say Brains" button, "Go to Section" link and the unlabelled input field displayed. The elements list is open on the form fields tab and no items are shown.

If you want to correct this aria-hidden zombie, you need to set the aria-hidden="true" property on the individual elements you want to hide, not their parent.

Note that you can not overwrite the child by specifying aria-hidden="false". If the parent has aria-hidden="true". The parent will take precedence.

Example 3 – hidden menu

A developer may implement a navigation menu as a list, but want to hide the list semantics from screen reader users. If they use aria-hidden="true" on the lists, this property will apply to all child elements (the links) within the list items.

<nav>
    <ul aria-hidden="true">
        <li><a href="#home">Home</a></li>
        <li><a href="#about">About</a></li>
        <li><a href="#services">Services</a></li>
        <li><a href="#contact">Contact</a></li>
    </ul>
</nav>
Screenshot of the code example in a browser window showing the bulleted list of links: Home, Blogs, About, Contact Us. The elements list is open on the links section and no items are shown.

aria-hidden=”true” VS role=”presentation”

When developers first start learning aria it is common to confuse the property aria-hidden="true" with role="presentation". These have two have different purposes and operate in different means. Getting these incorrect can result in some unintended consequences.

aria-hidden="true" set on a component hides the component and it’s children from assistive technologies, as if it does not exist. This makes them invisible to screen readers and other assistive technologies but the content still remains in the DOM and can be visible on screen.

role="presetation" which we will will talk in much more details about in a separate blog, removes semantic meaning of a component. This makes the semantics invisible to assistive tech while it still appears the same visually.

Unlike aria-hidden="true", role="presentation" maintains the visibility of the component and it’s children but it removes unnecessary semantics. So nesting links within a list and including role="presentation" will suppress the list structure but still allow the links to be accessible. If using a screen reader user were to keyboard shortcuts to navigate by list, they would not encounter the list, but if they navigated by link, they could access each of the links in the menu.

Don’t worry if this does not make sense right now, the blog focusing on role="presentation" will go through in a lot more details.

Time to save yourself

Quick, quick! Grab that keyboard over there… we’ve got some aria-hidden Zombie hoards to kill!

By understanding and respecting the power of aria-hidden="true", we can prevent the rise of aria-hidden zombies. Instead of a post-apocalyptic digital wasteland, we can build a truly inclusive web landscape where everyone can thrive—brains and code intact.

Know where aria-hidden=”true” is acceptable

aria-hidden="true" is acceptable to use on:

  • Decorative images
  • Icons
  • Visual effects which do not drive meaning
  • Redundant text
  • Off screen and hidden content
  • Modals and dialogs

Know where aria-hidden=”true” is not acceptable

aria-hidden="true" should not be used on

  • Interactive elements
  • Selective content
  • Items in a disabled, but still visible, state
  • Where you can use Semantic HTML to perform the same task

Key caution points

  • Don’t forget to correctly change the aria-hidden="true" attribute when changing visual display of components via JavaScript
  • Understand the purpose of aria-hidden="true" versus role="presentation"
  • Remember that aria-hidden="true" applies to all child components. So ensure you are setting only on the fields you wish to hide and not a parent.

Your challenge

Go forward aria-hidden zombie warrior and apply your knowledge! Equipped with your trusty keyboard, coffee and the cover of darkness, you can regain control of this apocalyptic wasteland and churn out some accessible code so that everyone can equally and fairly order toilet paper online! Oh sorry what was that? Oh does everyone still have toilet paper from the great toilet paper hoarding of 2020? Sorry, Ok well ah maybe churn out some accessible code so that everyone can sell their hoards of toilet paper online. Oh ^%&*# really?! We did that too? Hmm ok, well can we just do it (to quote your mum) “because I said so”?

Every time you introduce aria-hidden="true" into your code, ask the following questions:

  • Does this content need to be hidden from assistive tech?
  • Can I use semantics instead?
  • Will this introduce redundant code?
  • Have I implemented correct my JavaScript functions?
  • Have I checked all the children of this element?
  • Am I confident this has been thoroughly tested and will work correctly in all browsers and assistive tech?

Finally with that in mind it’s now time to talk about the aria-hidden zombies cousins; the precocious role=”presentation” hobgoblins, but we will save that for our next blog….