The ‘visually-hidden’ technique

Sometimes you want to hide an HTML element (e.g. <h1> when the logo image is present) from sighted users but not from screen readers. Here’s how.

MasaKudamatsu
Web Dev Survey from Kyoto

--

TL;DR

Apply the following visually-hidden class to the element you want to hide from sighted users but not from screen readers.

.visually-hidden {   /* Contain text within 1px box */
height: 1px;
overflow: hidden;
width: 1px;
/* Keep the layout */
position: absolute;
/* Remove any visible trace (e.g. background color) */
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
/* browsers in the future */
/* Prevent the screen reader to skip spaces between words */
white-space: nowrap;
}

The Problem

Sometimes you want to hide an HTML element from sighted users but not from screen readers.

The best example is the h1 element. The logo or some other image may clearly show what the webpage is about, as exemplified by Apple’s website on Mac machines:

The top of the mobile layout of Apple’s webpage on Mac, screenshot by me with Full Page Screen Capture (a Chrome extension) on 28 May 2019.

In such cases, the addition of the page title as the h1 element is unnecessary, cluttering the user interface. But for the accessibility purpose and also for the search engine optimization (SEO), we do not want to omit the h1 element. Otherwise, the visually impaired and the search engines do not know what the webpage is about.

The usual solutions to hide elements such as display:none and visibility:hidden do not work, because screen readers won’t recognize these elements. See O’Hara (2017/2019) and WebAIM (2019) for more detail.

Note: I use the Author-Date referencing system in this article, to refer to various articles on web development.

The Solution

The “visually-hidden” technique offers a solution. Snook (2011) compares several variants of this technique and proposes the best one, which has become the standard among web developers. Below let me describe it step by step.

Step 1: Apply the visually-hidden class

<h1 class="visually-hidden>Mac</h1>

Step 2: Contain the text content within a 1px square box

.visually-hidden {
height: 1px;
overflow: hidden;
width: 1px;

}

What’s essential here is overflow: hidden. Without this CSS declaration, the text will overflow the 1px square box.

Step 3: Keep the layout intact

.visually-hidden {
height: 1px;
overflow: hidden;
position: absolute;
width: 1px;
}

Without the absolute positioning, the 1px square box messes up the layout by moving other elements by 1px. Remember that web designers always strive for the “pixel-perfect” user interface. :-)

Step 4: Avoid screen readers from skipping word-spacing

.visually-hidden {
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}

The width:1px may force a multiword expression to wrap, which results in the removal of spaces between words. Consequently, ScreenReadersReadItOutLikeThisSentence. See Beach (2016) for more detail.

Dropping width:1px will cause another issue: it may create a horizontal scroll bar if the content is long enough (see Snook 2011).

To solve this issue, Beach (2016) proposes white-space: nowrap, based on her experience as a member of Facebook’s accessibility team. Now it becomes part of the standard solution (see O’Hara 2017/2019 and Rupert and Fairchild 2013/2019).

Step 5: Remove any visible trace such as background color

.visually-hidden {
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}

The height-overflow-width combo hides the content, but it leaves the background of the 1px box visible as a tiny dot. To overcome this issue, Snook (2011) proposed the use of the clip property in addition to the the height-overflow-width combo.

Snook (2011) noted that using the clip property alone can create a horizontal scroll bar when the content is longer than the screen width. That is why we still need the height-overflow-width combo.

We also need clip: rect(1px 1px 1px 1px) as a fallback for Internet Explorer versions 6 and 7, which account for 0.03% of page views globally in March 2020 (Can I Use? 2020a). These ancient browsers fail to recognize the comma as a separator of the pixel values.

Step 6: Add the future-proof of the clip property

The clip property, however, is going to be obsolete in the future. The MDN Web Doc notes:

“Where possible, authors are encouraged to use the newer clip-path property instead.” — MDN contributors (2020)

To achieve the same effect as clip: rect(1px 1px 1px 1px) does, both O’Hara (2017/2019) and WebAIM (2019) suggest clip-path: inset(50%). It means clipping a box by 50% of the length from all the four sides. So it effectively clips the entire box. See MDN Contributors (2019) for more detail on the inset() function.

The browser support for the clip-path property is yet to be universal (Can I Use? 2020b). So we should use the clip property as a fallback.

So the final solution, with the order of CSS declarations alphabetized (to follow Google HTML/CSS Style Guide 4.2.1), will be:

.visually-hidden {
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%); /* browsers in the future */
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}

A field example: Apple.com

Apple’s web developers use the “visually-hidden” technique to hide

<h1>Mac</h1>

from the Apple.com site on Mac machines. Using the Chrome DevTools, I have discovered that the following CSS code is applied to the h1 element:

border: 0;
clip: rect(1px, 1px, 1px, 1px);
-webkit-clip-path: inset(0px 0px 99.9% 99.9%);
clip-path: inset(0px 0px 99.9% 99.9%);
height: 1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;

The lack of white-space: nowrap shouldn’t be a problem as the text content is short and without a space. The border and padding properties are likely to be unnecessary. The -webkit-clip-path deals with the current browser compatibility situation: the global coverage of the unprefixed clip-path is about 70% as of April 2020 (Can I Use? 2020b). The argument for the inset() function differs but achieves the same result (I’m not sure why it needs to be 99.9%, not 100%, though).

Other (less desirable) solutions

See WebAIM (2019) for why height:0px; width:0px, text-indent:-10000px, or position:aboslute; top:-10000px should not be used. Snook (2011) discusses the issues for a few other solutions.

WebAIM (2019) suggests one viable alternative solution: move the element off the screen to the farthest left:

left:-10000px;
position:absolute;
top:auto;

with the following caveat:

“there are a few instances where positioning may be disabled” — WebAIM (2019)

So it recommends using this set of CSS code together with the height-overflow-width combo. If so, however, it’s not clear to me why we need to use the left property in the first place.

Also, one concern for this solution is whether it works for webpages in the right-to-left (RTL) languages (Arabic, Hebrew, etc.). Snook (2011), in one of his replies to the comments received, writes:

“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.” — Snook (2011)

It seems to me that switching between left:-999em and right:-999em requires an extra cognitive load for those web developers working in both left-to-right and right-to-left languages. Then it’s safer to use the height-overflow-width combo solution (with the clip and clip-path etc. as described above).

Dealing with sighted keyboard users

The “visually-hidden” technique has one drawback if it’s used for focusable elements such as a , button and input. Some people use the tab key to navigate across focusable elements on a webpage. Such sighted keyboard users get puzzled when a “visually-hidden” focusable element is focused: they see nothing focused on the screen.

To deal with this problem, O’Hara (2017/2019) and Rupert and Fairchild (2013/2019) both suggest the use of :not(:focus):not(:active) pseudo class selectors. That is,

.visually-hidden:not(:focus):not(:active) {
clip: rect(1px 1px 1px 1px);
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}

Then the above CSS code won’t apply when the element is focused or active. That is, the element shows up on the screen when focused.

This may be okay in some cases and may not be okay in other cases. An example of the latter is when you want to customize the style of a file upload button (<input type="file"/>). As described in detail in my other post (Kudamatsu 2020), one strategy is to make the input element “visually-hidden” and to style the associated label element as a good-looking button. If we need to reveal the input element when focused, however, the whole purpose of doing this breaks down. In such a case, we need to find another solution. I elaborate on this issue in Kudamatsu (2020).

Disclaimer

This article is based on my academic skills (I was trained as an academic data scientist) to survey what people argued in the past, to compare the pros and cons of each argument, and to pick the best for logical consistency and available evidence. It’s not based on the experiences as a professional web developer (which I’m on the road to be).

Let me know if I miss something. I’m always eager to learn more about web development.

References

Beach, J. Renée (2016) “Beware smushed off-screen accessible text”, Medium.com, Oct. 12, 2016.

Can I Use? (2020a) “Browser usage table”, Caniuse.com (accessed on Apr. 26, 2020).

Can I Use? (2020b) “CSS clip-path property(for HTML)”, Caniuse.com (accessed on Apr. 1, 2020).

Kudamatsu, Masa (2020) “How to customize the file upload button in React”, Medium.com, Mar. 31, 2020.

MDN contributors (2019) “<basic-shape>”, MDN web docs, Apr. 25, 2019.

MDN contributors (2020) “Clip”, MDN web docs, Feb. 1, 2020.

Rupert, Dave, and Michael Fairchild (2013/2019) “How-to: Hide content”, The A11ly Project, Feb. 15, 2013 (updated on Jul. 28, 2019).

O’Hara, Scott (2017/2019). “Inclusively Hidden”, Scottohara.me, Apr. 14, 2017 (updated on Aug. 20, 2019).

Snook, Jonathan (2011) “Hiding Content for Accessibility”, Snook.ca, Feb. 25, 2011.

WebAIM (2019) “CSS in Action: Invisible Content Just for Screen Reader Users”, WebAIM, Jun. 11, 2019.

--

--

MasaKudamatsu
Web Dev Survey from Kyoto

Self-taught web developer (currently in search of a job) whose portfolio is available at masakudamatsu.dev