Hiding Content for Accessibility

For years now, we've used a number of techniques for hiding content offscreen for accessibility purposes. We do this because the content is still accessible to screenreaders while being removed from the interface for sighted users.

An article over at Adaptive Themes reviews a number of techniques for hiding content that were considered for inclusion on a Drupal 7 project (but certainly applicable to any project).

Here is a summary of those techniques and the pitfalls of each technique:

Text Indent

.element-invisible {
  text-indent: -9999em;
  outline: 0;
}

Unfortunately, this technique doesn't work with RTL (Right-to-Left) languages.

Position Absolute and Collapsed

.element-invisible {
  height: 0;
  overflow: hidden;
  position: absolute;
}

In this case, Apple's Voice Over will not read content within an element that has zero height.

Position Absolute and Offscreen

.element-invisible {
  position: absolute;
  top: -999999em;
  left: auto;
  width: 1px;
  height: 1px;
  overflow:hidden;
}

In this case, if you have focusable content within the positioned element, the page will scroll to that element, thus creating an unsettling jump for sighted users.

Clip Method

.element-invisible {
  position: absolute !important;
  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
  clip: rect(1px, 1px, 1px, 1px);
}

The article ends with this final technique and is the solution they ended up using on their project.

With the work I've been doing at Yahoo!, we had been using the Position Absolute and Offscreen method. But sometimes we wanted to set focus to offscreen content. We had switched our technique to the Clip Method but uncovered differing behaviour between browsers.

Everything works great in Internet Explorer and Firefox. However, in Webkit (Chrome and Safari) and Opera, there's an interesting behavior when the element is at the edge of the screen. If the element, when unclipped, is large enough to force a horizontal scrollbar, will force a scrollbar even when clipped.

This seems to go against the CSS 2.1 guidelines that say:

Content that has been clipped does not cause overflow.

However, by forcing a scrollbar in Webkit and Opera, it does, in fact, seem to cause overflow. So how did we get around this?

Positioned, Clipped, and (almost) Collapsed

We combine a few techniques into one:

.element-invisible {
  position: absolute !important;
  height: 1px; width: 1px; 
  overflow: hidden;
  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
  clip: rect(1px, 1px, 1px, 1px);
}

Using absolute positioning, we take the element out of flow so as not to affect the layout of anything around it. With a height of 1px, the element should still be visible for Voice Over to read the content. The clipping removes any visible trace of the element from the page.

Any focusable elements inside are still focusable, so depending on placement within the overall layout, some considered placement may still be of concern. Although, I might question why you are focusing on an element that was so far removed from the overall flow of the document.

We've only begun using and testing this technique, so even this may not be perfect. Any feedback and suggestions are quite welcome.

Published February 25, 2011 · Updated February 25, 2011
Categorized as HTML and CSS
Short URL: http://snook.ca/s/992

Conversation

30 Comments · RSS feed
Tanner said on February 25, 2011

Interesting approach(es) to hiding content. I had no idea that Apple's Voice Over wouldn't read 0 height content.

With all of these possabilities in mind, do we know how search engines react to hidden content? There's been a number of SEO discussions in the past on the topic of search engines not liking it when content is hidden.

Thierry Koblentz said on February 25, 2011

If we use width/height/overflow, then why do we need clip?

Thierry Koblentz said on February 25, 2011

Thanks for the heads up regarding the Webkit/Opera issue btw :-)

Jonathan Snook said on February 25, 2011

@Thierry: without the clipping, you have a 1px element that is still visible. Any background colours or anything else drawing in that 1px would be noticeable.

Thierry Koblentz said on February 25, 2011

Hi Jonathan,

Good point about the possibility of having a colored dot on the page.

For the Webkit/Opera scrollbar bug, I believe we could fix it by using position:fixed rather than absolute, Using something like this may be:


.element-invisible {
  position: fixed !important;
  _position: absolute !important;
  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
  clip: rect(1px, 1px, 1px, 1px);
}

That should kill the unwanted scrollbar, but unfortunately that would also bring back the tabbing navigation issue since any focusable element in there would make the page scroll.

As a side note, what do you think of dropping the "," notation, and go with:


.element-invisible {
  position: absolute !important;
  height: 1px;
  width: 1px;
  overflow: hidden;
  clip: rect(1px 1px 1px 1px);
}

The spec says: "User agents must support separation with commas, but may also support separation without commas".
Should we expect browsers to drop support for space separation any time soon?

Joshua said on February 25, 2011

I have always been told to set the element to display: none;.
Does this not work?

Paul Irish said on February 25, 2011

This clip technique is also what's provided in the .visuallyhidden helper class in HTML5 Boilerplate.

Jonathan Snook said on February 25, 2011

@Thierry: Only IE recognizes the syntax without commas.

@Paul: It's nice to see implementations elsewhere. I hadn't seen documentation on this technique until it was shown to me by a co-worker.

Thierry Koblentz said on February 25, 2011

Sorry for the spam, but your post got me thinking :)

If this variation is a patch for the "clip property" method, then why not dropping that technique altogether since it fails in some browsers?

Why not going with a rule in which each declaration is used for what it's supposed to do rather than used to "fix" another declaration within the same rule (does that make sense?).

It is this:

.element-invisible {
  position: absolute !important;
  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
  clip: rect(1px, 1px, 1px, 1px); /* this kind of fail in Webkit and Opera */
  height: 1px; /* since clip is not reliable we need that */
  width: 1px; /* since clip is not reliable we need that too */
  overflow: hidden; /* since clip is not reliable we also need that */
}

Versus this for example:

.element-invisible {
  position: absolute !important;
  height: 1px;
  width: 1px;
  overflow: hidden; /* we make the box 1x1 pixel and prevent overflow */
  filter:alpha(opacity=0); /* that one we could drop,  we're really taking about one single pixel after all */
  opacity:0; /* we make that single box transparent */
}

@Paul - I like the class name Boilerplate is using

Thierry Koblentz said on February 25, 2011

@Jonathan: I believe it is the other way around. All browsers recognize the syntax without commas, but IE 6 and 7 that do not recognize the "comma" separated syntax.

Kevin Potts said on February 26, 2011

Great article. I've been using the "Position Absolute and Offscreen", but this is far better. Thanks.

Taimar said on February 26, 2011

Add “text-align: left” to the Text Indent example, fixes the RTL pitfall.

askthebigo said on February 26, 2011

I use a variety of methods - for skip navigation items, I style up the :focus and bring the content onto the screen (if you are physically impaired and use TAB to get around the screen - you still want to see these). In the non-focussed state I use:


a.skip{
margin: 0;
padding: 0;
position: absolute;
top: -99999px;
left: -99999px;
}

Haven't had any issues with that.

I also use a similar technique for hiding additional text (for users with screen readers).

For example:



<h3>An article on Bees</h3>

This is some text representing a summary for an article that you could find on many websites.

Read more<span class="hidden"> from the article on Bees</span>

and then use the same CSS as in the non-focussed skip link, but change the class name to .hidden

I haven't had reports of any issues with this.

Arieh said on February 27, 2011

I have been using the text-indent method for quite some time now, and has no real issues with it - and I'm an RTL user.
The only thing I noticed is that for safari I also need to add 'overflow:hidden'.
Can you tell me what you mean when you say it doesn't work?

Ido said on February 27, 2011

Been using the text-indent method for years now, and except for extreme cases with a mixture of relative and absolute positioning, it works great.
Would also like to know the issues you where talking about?
accept for the RTL issue you where referring to, isn't this the best and easiest way to use ?

Tom Bigelajzen said on February 27, 2011

Text Indent issues with RTL are easily solved with setting direction:ltr;text-align:left on the element you wish to hide.

Tom Bigelajzen said on February 27, 2011

btw - a thread exactly about this issue in html5boilerplate issues - https://github.com/paulirish/html5-boilerplate/issues/194

Mike Cherim said on March 01, 2011

When using the Position Absolute and Offscreen method I have found it is better to move the element to the left rather than to the top -- in the interest of keyboard users. The reason is it lessens the occurrence of the user being/feeling bounced around (going to top, then back again). When the element is moved off screen to the left the user doesn't get bounced when tabbing and the whole use feels more natural.

Neil Osman said on March 01, 2011

Hi Jonathan,
Haven't encountered any issues in RTL pages while using:
.skip {
left: -1000px;
position: fixed;
speak: normal;
top: -1000px;
voice-family: female;
}
see http://ww3.co.il

Thanks for your thoughts

Ahmad Alfy said on March 02, 2011

I work most of the time on RTL website... Hiding contents is really an issue and a pain in the ass
I try to set that particular element to { direction:ltr; } to get it to work correctly

Browser vendors should really do something about it

Aaron Gustafson said on March 03, 2011

I'm still a big fan of

.hidden {
  position: absolute;
  left:-999em;
}

It's short, sweet and hasn't caused any issues in my experience.

Brent Lagerman said on March 03, 2011

I've been using the same method as Aaron Gustafson above for years and have never had an issue either... Are there any drawbacks that you know of ?

Jonathan Snook said on March 03, 2011

Aaron/Brent: The problem with left:-999em is in RTL interfaces. It forces a horizontal scrollbar. Which means that you'd actually have to swap it for right:-999em;. If you don't have to worry about RTL interfaces, then you'll be fine.

Aaron Gustafson said on March 04, 2011

Right. When I've had to deal with RTL (like on CharterForCompassion.org), I've usually set an "rtl" or "ltr" class on the html element or gone with the value of dir and then swapped out left for right depending on the situation. One thing worth noting to anyone doing that: if you set a default of left, make sure you set left: auto in the RTL version.

jelumalai said on March 06, 2011

I trying to hide the "Browse" (file) button for file attachment. Its working well in all browsers, but if I switch to other language OS like Chinese/Japanese, browse button area is not clickable.

Anybody face & fix this same problem?

Thomas Sabo Charm Club said on March 09, 2011

This is a good article. Just Keep it up! Thanks!

Thomas Sabo Watches said on March 16, 2011

This is a good article. Just Keep it up! Thanks!

Thomas Sabo Chains said on March 17, 2011

This is a good article. Just Keep it up! Thanks!

Janice Schwarz said on March 17, 2011

Like Joshua up there, I too would just use display:none. I've been using that for accessibility for years. Any particular reason it wasn't considered for this project either?

Mike Gifford said on March 18, 2011

As someone who was heavily involved in this discussion for Drupal 7 I'd like to just quickly pipe in to say that I don't think we've had any reports of problems with this approach that I am aware of since the launch in January. They would appear here.

I'm not suggesting that Drupal has the perfect solution, but a great many issues were considered in this thread. And it seems to be working for most users so far.

Ultimately the problem lies with Assistive Technology & the lack of an agreement on standards. If we could get them all to agree on a common behaviour for any set of code, we'd be so much further ahead. As it is there's a lot of guessing & endless testing involved.

Glad to hear that there are examples with simpler left: -1000px; based solutions working in RTL environments. I don't know how universally this would be.

Joshua & Janice - Most screen readers respect display:none and won't read out the content. This is certainly the case in VoiceOver. However, you can always test it with free tools like VoiceOver, NVDA or Orca & let me know if I'm wrong.

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.