See No Evil: Hidden Content and Accessibility
At 4/19/2024
When I first started learning web development I thought hiding content was simple: slap display: none;
onto your hidden element and call it a day. Since then I’ve learned about screen readers, ARIA attributes, the HTML5 hidden attribute, and more!
It’s important to ensure our websites are accessible to everyone, regardless of whether or not they use a screen reader, but with this myriad of options, how do we know when to use what?
There are four main scenarios where you may wish to hide content:
1. Hiding content for everyone, regardless of whether they use a screen reader
2. Hiding content for screen readers while showing it to other users
3. Showing additional content for screen readers while hiding it from other users
4. Hiding content at specific screen sizes
Let’s dive deeper into each of those scenarios to learn how to handle them.
Hiding Content for Everyone
When hiding content for all users we can take advantage of HTML5’s hidden
attribute. The hidden
attribute signals that content should not be rendered, regardless of medium or screen reader use. In supported browsers it also hides the content from view, similar to display: none;
.
It may feel odd to be handling display in your HTML instead of your CSS, but there’s a good reason for it! All devices should respect the hidden
attribute, including browsers, screen readers, and printers, even if they don’t load your stylesheets.
This technique is most often used when a site is dynamically showing and hiding content, like a popup or accordion. You may need to combine the hidden
attribute with a CSS class to allow for transitions. In that case, just make sure you update the hidden
attribute whenever you change visibility by another means.
There’s one extra wrinkle when using hidden
. It’s not supported in Internet Explorer 10 and below so if you do use hidden
you should also set display: none; !important
Footnote
1
in CSS to ensure the content is hidden in all browsers.
<div class="example" hidden></div>
<style>
.example[hidden]{
display:none !important;
}
</style>
Code language: HTML, XML (xml)
This can also be set as a global style using attribute selectors.Footnote 2
[hidden] {
display: none !important;
}
Code language: CSS (css)
Hiding Content for Screen Readers
Some content is not important for understanding a web page, but is added to make the design more visually appealing. For example, icons and glyphs can provide a nice visual polish, but tend to be unhelpful — and sometimes downright distracting — for screen reader users. In this scenario we’ll want to hide the content from screen readers while showing it to everyone else.
In this case we’ll use the aria-hidden
attribute. aria-hidden
is a boolean attribute so it can be set to true
or false
. Setting the attribute to false
is the same as not including it at all, so you’ll generally want to set it to true
and use it like this:
<div class="my-glyph" aria-hidden="true"></div>
Code language: HTML, XML (xml)
aria-hidden="true"
should not be confused with role="presentation"
which strips the semantic meaning of an element from the accessibility tree. Here’s a helpful article outlining the difference between the two.
Showing Additional Content for Screen Readers
A good web page design often uses visual clues to convey information to the viewer. It’s important to structure your page so that screen reader users get these same clues from your text. For example, pagination may be obvious when laid out visually, but might read as a meaningless list of numbers over a screen reader. In these scenarios it’s helpful to include extra information for screen readers without cluttering up your visual design.
Setting display: none;
hides the content but also removes it from the accessibility tree so screen readers won’t read it. Because of that it’s best to fall back to other CSS tools to hide the content while keeping it in the accessibility tree.
The 18f site has a great solution to hide content visually while keeping it in the accessibility tree for screen readers:
.sr-only {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
Code language: CSS (css)
In the comments of this post Edward Martin pointed out that the CSS clip
property is deprecated and that we should be using clip-path
. clip-path
isn’t fully supported yet, so for now it’s best to include both clip
and clip-path
.
In addition, Kimblim pointed out that this technique can cause screen readers to skip the spaces in between words and suggested adding white-space: nowrap;
to avoid this.
Following this advice leaves us with this more robust class:
.sr-only {
border: 0;
clip: rect(0 0 0 0);
clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
-webkit-clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}
Code language: CSS (css)
The aria-label attribute can also be used to provide additional information to screen readers though generally the CSS solution is preferable. It’s worth learning both techniques and knowing when to use one or the other.
Hiding Content at Specific Screen Sizes
When building responsive web pages designers often choose to display content at certain screen sizes but not others using media queries. In these scenarios the content should generally be included in the accessibility tree for screen readers, so hidden
and aria-hidden
are not necessary.Footnote
3
Bringing it All Together
Now we’ve got a lot of tools in our toolbox. We can hide content for all users, for screen readers, for users not using screen readers, and for specific screen sizes. Learning how to properly hide content in an accessible way is a valuable skill for anyone touching the front-end, and your users will appreciate it!
Footnotes
-
Šime Vidas pointed out on twitter that using
!important
for this declaration better matches the behavior of supported browsers. Return to the text before footnote 1 - A previous version of this post incorrectly claimed that IE 7 and 8 did not support attribute selectors. Aaron Gustafson kindly pointed out that this was incorrect, and that both of those browsers support attribute selectors. Return to the text before footnote 2
- Patrick H. Lauke pointed out that one of the paragraphs in the original post was somewhat misleading. That paragraph has been removed in the current version of this post. You can see more discussion in the post comments. Return to the text before footnote 3