See No Evil: Hidden Content and Accessibility

Written by Paul Hebert on

Illustration of an eye bracketed by less than and greater than signs

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; !important1 in CSS to ensure the content is hidden in all browsers.

<div class="example" hidden></div>

<style>
  .example[hidden]{
    display:none !important;
  }
</style>

This can also be set as a global style using attribute selectors.2

[hidden] {
  display: none !important;
}

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>

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;
}

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;
}

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


  1. Šime Vidas pointed out on twitter that using !important for this declaration better matches the behavior of supported browsers. 
  2. 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. 
  3. 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. 
Paul Hebert

Paul Hebert is a hybrid designer and developer at Cloud Four. When he's not designing and developing websites he enjoys bouldering, drawing, cooking, gardening, and eating too much cheese.

Never miss an article!

Get Weekly Digests


Comments

If for some reason your design requires including the same content once for small screens and again for large screens, then one of the duplicates should get the aria-hidden="true" attribute.

Or, simply make sure that the visually hidden duplicate is set to display:none;, which – as noted in the section just before that – removes the content from assistive technologies / screen readers already anyway.

aria-hidden="true" is really mostly useful if you have to hide things in some other way (e.g. if you’re not using display:none; because you’re animating things fading in and out / moving in from the side and back again, etc. Then you want to make sure you dynamically juggle aria-hidden correctly. Another case where it’s useful, of course, is if you want to explicitly hide something from assistive technologies that is otherwise visible / displayed (generally done because you’ve already provided a more appropriate alternative to AT anyway through other means, and having the visible part be announced by AT is redundant or leads to odd output – e.g. icon font containers set to aria-hidden="true" to avoid AT trying to read out the glyphs, and because there’s already an adjacent visually hidden but accessible alternative / an aria-label or similar on the relevant control that uses the icon / etc.)

Patrick H. Lauke

Replies

Good point. Thanks Patrick! I appreciate the feedback!

You’re right that using display: none; is generally the right approach here.

I thought about re-working that paragraph to go into more detail about when to use display: none; vs. aria-hidden="true" but it ended up being kind of confusing and I didn’t feel like it was adding a lot of value so I ended up removing that paragraph from the post. I think this should be more accurate, and easier to understand.

Thanks for the correction!

According to the MDN web docs, using clip found in the sr-only class is no longer recommended.

Maybe we should find a new way to show content to screen readers only?

And isn’t using aria-label better anyway?

https://developer.mozilla.org/en-US/docs/Web/CSS/clip

Edwin Martin

Replies

Hey Edwin!

Thanks for pointing out that clip is deprecated! It looks like the best option for now is including both clip and clip-path, but I’m open to other ideas as well.

aria-label is certainly useful but fills a slightly different use case and my research points to the CSS solution generally working better. It can be a tricky call when to use one or the other though.

I’ve added some more info to the article about both the clip/clip-path support and aria-labels.

Thanks!

I would add that it is important to decode html entities from aria-labels, as some screen readers, like VoiceOver, will read   as a word, and the fact that a space is breakable or not is of no importance to screen reader, so it’s always a safe net to remove them entirely. This is where aria-labels become useful, as you may need to keep the non-breakable spaces visually, but hide them in the audio version.

It is something that can happen a lot when receiving content from massive CMS, with many content editors.

A good library for this problem is ‘he’.
https://github.com/mathiasbynens/he

Consider removing the , (comma) after the attribute selector in the Hiding Content for Everyone code example .example[hidden], to avoid a parse error.

Otherwise, fantastic article!

For anyone wanting to a “screenreader-only” class, I would suggest to consider AMP’s tried and tested version .i-amphtml-screen-reader. See the detailed notes in https://github.com/ampproject/amphtml/blob/master/css/amp.css


Let’s discuss your project! Email Us