A new (and easy) way to hide content accessibly
Tags: css, a11y
Published: 2019-05-02 20:59:10

---
title: A new (and easy) way to hide content accessibly
description: Possibly the best way to hide content accessibly. The CSS is easy to write and understand!
canonical_url: https://zellwk.com/blog/hide-content-accessibly
tags: css, accessibility
published: true
---

When I want to hide content accessibly, I always turn to [Jonathan Snook's snippet][1].

```css
.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);
}
```

But yesterday, I happened to chance upon Scott O'Hara's [article on hiding content][2]. Scott says we only want to hide content in three different contexts:

1. Hide it completely
2. Hide it visually
3. Hide it from screen readers


When we say hide content accessibly, we effectively mean option #2 (hiding content visually, but not from screen readers and keyboard users).

## Then I had an idea

If we only want to hide elements visually, why don't we use `opacity: 0`? Opacity is used to hide elements visually anyway. Content hidden with `opacity: 0` is still accessible to screen readers.

```css
.hide-accessibly {
  opacity: 0;
}
```

I took it up a notch by adding `position: absolute`. This takes the element away from the document flow; and allows us to style other elements as if the hidden content isn't there.

```css
.hide-accessibly {
  position: absolute !important;
  opacity: 0;
}
```

I thought this felt good enough, and I asked Jonathan about it.

Here's what he responded with:

{% twitter 1120809900878307328 %}

He also wondered if `pointer-events: none` would stop keyboard-trigged click events (which are absolutely essential for screen readers and keyboard users).

{% twitter 1120843242252070912 %}
{% twitter 1120844382754754562 %}

I was curious, so I tested `pointer-events: none` and discovered it works with keyboard-generated clicks, screen-reader-generated clicks, and JavaScript generated clicks.

Here's the Codepen I used for my test:

{% codepen https://codepen.io/zellwk/pen/mgzPXa %}


I reported my findings back to Jonathan and he said we might have a winner!

{% twitter 1120862948866633728 %}


## The snippet

Here's the snippet if you want to use this method.

```css
.hide-accessibly {
  position: absolute !important;
  opacity: 0;
  pointer-events: none;
}
```

**DISCLAIMER:** This method is still incredibly new. I've only tested it on the latest versions of Firefox, Safari, and Chrome. I wasn't able to run a test on Edge and other browsers yet.

If you're an accessibility consultant, I'd greatly appreciate it if help me take this snippet out for a spin.

For the rest: I don't recommend using this snippet in production yet. (Not until I get confirmation from accessibility experts).

**UPDATE**: Many developers voiced their opinions, concerns, and experiments over at Twitter. I wanted to share with you what I consolidated and learned.

At the start, all three properties were debated upon.

First, let's talk about `opacity`.

## The problem with opacity?

Patrick and Vadim were concerned about `opacity` because it seemed to break in some browser/screen reader combination.

{% twitter 1120949919391604737 %}
{% twitter 1120955137491705856 %}

But Jonathan found some research that suggests that `opacity` is okay. Patrick further did some tests and agreed that `opacity` is okay.

{% twitter 1120995225063895040 %}
{% twitter 1121000192080273409 %}


Scott O'Hara also chimed in on the original problem with `opacity`

{% twitter 1121045899982012416 %}



The verdict at this point:

1. Opacity seems to be screen-reader friendly!
2. But it might not work on ChromeVox now. More tests are required to validate this.

Next, let's talk about `pointer-events` because it's the second most-troublesome thing.

## Pointer-events

Scott O'Hara pointed out that iOS Voiceover users wouldn't be able to trigger a click if an element had `pointer-events: none`. I tested what Scott said and found it to be true.

{% twitter 1120909503585767424 %}


This means we can't use the `pointer-events` universally on all elements.

My next question was: If we can't use `pointer-events`, what if we set `z-index` to `-999`? This would prevent the hidden element from obscuring clickable elements.

```css
.hide-accessibly {
  position: absolute !important;
  opacity: 0;
  z-index: -999;
}
```

Well, Scott said we shouldn't use `z-index: -999` on buttons as well, because visually hidden buttons wouldn't work correctly on iOS Voiceover.

{% twitter 1121016947251064832 %}


I'll be honest. I don't understand why `z-index: -999` wouldn't work correctly with iOS Voiceover, so I don't have a proper conclusion here. I didn't test it.

## MacOS Voiceover reading content out of source order

Scott and João Beleza Freire (@letrastudio mentioned above) pointed out a noteworthy bug where macOS Voiceover read content out of source-order.

{% twitter 1120909503585767424 %}
{% twitter 1120970115628130304 %}


I did my own test on this, but the bug Joao reported doesn't seem to happen on my computer, even though we used the same device!

{% twitter 1120980475307274240 %}

Scott O'Hara shared a little more info on when this bug occurs:

{% twitter 1121005390412562432 %}


It turns out, a bunch of experts (including Scott) were already going back-and-forth about this macOS Voiceover bug since 2017. It's worth reading through the entire [issue thread][3] about the problem.

From what I've read, it seems like the problem happens when `position: absolute` is used. When you use `position: absolute` and you mess around with the CSS positing, it messes with the position of the Voiceover focus-ring, which changes the reading order.

<figure><img src="https://zellwk.com/images/2019/hide-content-accessibly/focus-ring.png" alt="An image detailing the experiments done by Joe Watkin on how CSS affects focus rings"></figure>

This means ANY solution that there's a chance that macOS Voiceover screws ANY solution that contains `position: absolute`.

😱

And this whole issue is only Voiceover related. We haven't considered how `position: absolute` can make it weird for other screen readers.

{% twitter 1121053472105934849 %}


## The solution in HTML Boilerplate

Some folks have suggested they use the `sr-only` snippet from HTML5 Boilerplate. They felt it's the best method out there because many experts came together to create this.

```css
.sr-only {
  border: 0;
  clip: rect(0, 0, 0, 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
  /* 1 */
}
```

However, this is the same solution that triggered the [issue thread][4] I mentioned above! Experts, like Scott O'Hara, have been working on this since 2017 and there doesn't seem like THE solution to date.

The best solution so far was suggested by Joe Watkin:

```css
.visuallyhidden {
    border: 0;
    clip: rect(0 0 0 0);
    height: auto; /* new - was 1px */
    margin: 0; /* new - was -1px */
    overflow: hidden;
    padding: 0;
    position: absolute;
    width: 1px;
    white-space: nowrap; /* 1 */
}
```

At the time of writing, this solution has not been integrated into HTML5 Boilerplate officially. Do take note.

Again, it's worth going through the [conversations in the issue thread][5] if you nerd out in this field. It's priceless. (As an aside, I learned that `aria-label` is [ignored by Google's and Microsoft's translators][6]! 😱).

## Concluding words

**While Joe Watkin's solution seems to be the best so far, the real answer is it depends.** Each method we discussed above, in [Jonathan's article][7], and elsewhere on the internet has their pros and cons.

Like Scott mentioned, it's almost like a situation where you can choose between grid vs flex vs other layout methods. You have to pick the best method depending on the situation (and your knowledge of the weird quirks).

{% twitter 1121056096356007937 %} 

**There's one thing we can do to further clarify things.** And that's to compile the pros and cons of each solution we know so far.

{% twitter 1121058550539747329 %}
{% twitter 1121067322477817856 %} 

Unfortunately, this is something that's way out of my league right now. If you'd like to step up and participate in the conversation, I'm sure Jonathan, Scott, and many others would love to chat!


[1]:	https://snook.ca/archives/html_and_css/hiding-content-for-accessibility
[2]:	https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html
[3]:	https://github.com/h5bp/main.css/issues/12
[4]:	https://github.com/h5bp/main.css/issues/12
[5]:	https://github.com/h5bp/main.css/issues/12
[6]:	https://github.com/h5bp/main.css/issues/12#issuecomment-392156112
[7]:	https://snook.ca/archives/html_and_css/hiding-content-for-accessibility

<hr>

Thanks for reading. This article was originally posted on [my blog](https://zellwk.com/blog/hide-content-accessibly/). Sign up for [my newsletter](https://zellwk.com) if you want more articles to help you become a better frontend developer.