Using CSS clip as an Accessible Method of Hiding Content

Its relatively easy to hide content in Drupal using CSS, however its a whole different ball game to hide content and keep it accessible to all site visitors. Disabled web users may be using a screen reader or other Assistive Technology. For Drupal 7 we wanted a way to hide content that worked in all browsers and avoided many of the issues associated with current techniques.

The idea was to develop a new CSS class that could be applied to any element we needed to hide. We called this class .element-invisible and set about testing properties and values to make this work, in all browsers and all AT devices. First lets look at some of the current techniques we looked into and tested for Drupal 7, then I'll introduce a new technique I came up using the little known clip property and see how it stacks up - I think you'll be intrigued.

Update: A clip method was committed for Drupal 7.

Current Accessibility Techniques for Hiding Content

At the moment theres really only two methods that are in common usage - the text-indent method (not unlike the Phark text-image-replacement method) and using absolute positioning. Lets take a look at both of these and analyze their flaws.

Text Indent

This is probably the easiest to understand and implement - the text is given a large negative value to move it off-screen (outside the boundaries of the browser window):

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

The outline is zeroed out otherwise if the element is focused we will see the outline extend around the element and all the way off the screen.

The big problem with this technique is with RTL (Right to Left languages) - any way we deal with text indent there will be issues in at least one browser - in short, its just a bloody pain in the arse dealing with negative text indent and RTL, so much so it simply rules it out as a candidate.

Position Absolute - Collapse the Element

There are principally two main methods that use position absolute - one simply collaspses the element and the other moves the element off screen.

Lets look at the first one. This is pretty cool technique that sets the height to zero, hides the overflow and takes the entire element out of the flow of the document.

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

This is in fact the original method we choose to put into Drupal 7, it was clean, easy to understand and it worked in all browsers - or so we thought. This method has one major flaw and its the height property value (zero). The problem is with Apples Voice Over software - it interprets elements with height:0; to be invisible and does not read them out. This issue was first reported by Everett Zufelt back in Feb, 2010. Subsequent releases of Voice Over have not changed the situation (as of writing in any case). Both NVDA and Jaws screen-readers had no problems with this implementation so this was a real blow and meant we needed to find a new, better technique.

Position Absolute - Move it Off-Screen

One of the oldest techniques for hiding content accessibly is to move the element out of the view-port. Easy to grasp this technique merely sets a big negative top value to shift the element out of site.

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

This technique is pretty solid and works in all browsers and won't give any hassles in RTL websites, however there are two main issues with this technique - its relatively easy to break, and it can cause a page "jump" if applied to focusable elements, which can be very confusing to sighted keyboard users.

Let say you apply this to an H2 heading, and then start adding other generic styles to all H2 headings - you can and probably will pretty quickly start having issues with this. The only way around this is to start adding many other properties and setting them to !important, so pretty quickly your nice simple technique is bloated and is going to end up looking something like this:

.element-invisible {
  background: transparent none !important;
  border: none !important;
  display: block !important;
  height: 1px !important;
  overflow: hidden !important;
  padding: 0 !important;
  position: absolute !important;
  top: -999999em !important;
  width: 1px !important;
}

This is not so bad, but certainly rather crufty, but at a pinch it would get the job done - however we still have the big problem of the page "jump" issue if this is applied to a focusable element, such as a link, like skip navigation links. WebAim and a few others endorse using the LEFT property instead of TOP, but this no go for Drupal because of major pain-in-the-butt issues with RTL.

The Clip Method for Accessibly Hiding Content

In early May 2010 I was getting pretty frustrated with this issue so I pulled out a big HTML reference and started scanning through it for any, and I mean ANY property I might have overlooked that could possible be used to solve this thorny issue. It was then I recalled using clip on a recent project so I looked up its values and yes, it can have 0 as a value. My first iteration was this:

.element-invisible {
  position: absolute;
  clip: rect(0px 0px 0px 0px);
}

At first I only tested this Firefox, IE6/7 and NVDA and it seemed to be working well. Problem was I used the depreciated syntax (no comma delimiters) and IE8 uses the recommended syntax while IE6 and IE7 will only work with space delimiters.

I left it for a while since there didn't seem to be much support for the concept and it is a pretty radical idea, however more recently support has grown and a better, cross browser implementation was developed by casey:

.element-invisible {
  position: absolute !important;
  clip: rect(0, 0, 0, 0);
}
/* IE7 \*/
\*:first-child + html .element-invisible {
  clip: rect(0 0 0 0);
}
/* IE6 \*/
\* html .element-invisible {
  clip: rect(0 0 0 0);
}

The strike against this are the browser hacks - since we cant load conditional stylesheets for Drupal core we have to do something like this in the system-behaviors.css file directly. Additionally this still does not solve the page jump problem, which I think can only be solved by using a position absolute LEFT/RIGHT method, however this could be very difficult to get working reliably and we just have to live with the fact that hiding focusable elements (pretty much the only one we would hide is the skip navigation link) comes down to a particular sites requirement.

The main reason I like this method is that is its 1) hard to break and 2) relatively lightweight CSS. I can live with the hacks - we have been for many years in Garland already.

I've now tested this in Firefox, Opera 10, Chrome, IE6/7/8, Safari 4 and 5 (but not with Voice Over) and NVDA - and it works. Additionally its very hard to break, in fact I haven't been able to break it yet other than re-declaring clip, which would be very rare indeed.

Unfortunately I do not have a Mac and cannot test Voice Over, nor do I have Jaws, so I'm out there also.

If you do have a Mac, or Jaws, please join the effort - you can read the entire issue here: http://drupal.org/node/718922

I have big hopes for the clip technique, if we can test it thoroughly and it works, we would have pioneered a brand new accessibility technique and one that could be most useful to many other websites and developers, not just for Drupal, but for disabled web users everywhere.

Update 23rd June 2010

Theres an off-left method in the thread linked to that uses left:auto; for RTL that seems to work well in early testing, this good news for those wanting to use this technique in Drupal.

Also we have tested the clip method in all major browsers and screen-readers (the newer method - see below) and found its working - both hiding content from normal visual display and being announced by screen-readers (Jaws, NVDA and Apple Voice Over).

The new method uses 1px value instead of 0:

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

So what have we got to hide here...?

One of the commenters below (#5) asked about what it is we're trying to hide. In Drupal 7 we added proper H2 headings to most lists - such as menu link lists (main menu/secondary menu - this is done in theme_links) search block gets a heading if one is not set in the block settings, breadcrumbs have a heading also. Many "active" links get a hidden message and we envision adding other elements to help screen-reader users so having a reliable way to hide these elements from visual display is imperative (since sighted users don't need these)

I made a couple of screenshots that show some of these hidden elements - this is a basic forum post in Garland for Drupal 7. Pay attention to the main menu, breadcrumbs and the search block.

Drupal 7 Garland theme - with CSS
Drupal 7 Garland theme - no CSS

Last updated 9th July, 2010 - 6:11pm

Authored by Jeff Burnz on