The visibility property isn’t just about visibility
I recently ran into an issue where the text content of a <button>
I created wasn’t in the accessibility tree and therefore wasn’t visible to screen readers. After some debugging, I realised that the reason was because I was animating the text visually by changing the opacity and, more importantly the visbility
property.
It was definitely an oversight, as I know and have previously written about the different effects of different ways to hide elements with CSS. But this made me think about the behaviour of the visibility
property, particularly the hidden
value, as it relates to the different kinds of “visibility” - visual, spatial, assistive technology, and interaction. When we hear the world “visibility”, we tend to only think of vision. As we will see, the visibility property isn’t just about (visual) visibility.
visibility: hidden
and visual visibility #
When visual visibility is concerned, the visibility: hidden
rule has the effect we would expect. The element becomes, more or less, “invisible”.
It's gone! ---> <span style="visibility: hidden;">I'm gone!</span>
It's gone! --->
To understand exactly what is happening, let’s revisit the browser’s rendering pipeline, a.k.a. the critical rendering path. Typically, once the page’s styles have been determined, three things happen:
- Layout - what space does the element take up on the page?
- Paint - what goes in each pixel?
- Composite - in what order is each pixel drawn?
For an element with the visibility: hidden
rule, it would make sense for browsers to optimise by not taking the paint and composite steps, since there are no pixels drawn in the place where the element would be. Although this isn’t set in the specification that browsers must make this optimisation, it helps me understand how this rule works by thinking of it in this way.
visibility: hidden
and space #
Although it is technically true to say that an element is invisible when the visibility: hidden
rule is applied to it, it can be argued that it isn’t truly invisible because it still occupies space on the page.
As I mentioned, an element with visibility: hidden
may not have to go through the paint and composite steps in the rendering pipeline. However, it will go through the layout stage. This means that, although the pixels aren’t painted, they still occupy space on the page. It’s sort of like Harry Potter and his invisibility cloak - you couldn’t see him, but he was still there.
Let’s take these three <div>
elements, each with a width of 100px, with the middle <div>
having the visibility: hidden
rule.
<style>
div {
width: 100px;
display: inline-block;
background-color: #ffdb3a;
}
</style>
<div>One</div>
<div style="visibility: hidden;">Two</div>
<div>Three</div>
Although the middle <div>
is technically “visually hidden”, it can be argued that it isn’t truly invisible due to the fact that it still occupies space.
visibility: hidden
and interactivity #
Because an element with the visibility: hidden
rule will still occupy physical space on the page, it may seem as if all this rule does is visually hide the element in a similar way to setting opacity
to 0
. On the contrary, the visibility: hidden
rule is actually much more similar to display: none
.
Any interactive elements, such as forms or links will also lose their ability to be interacted with. For example, take the <button>
below that should pop up an alert when clicked.
It's gone! --->
<button style="visibility: hidden;"
onclick="alert('Hello!')">I'm gone!</button>
<--- You can't touch it!
It's gone! ---> <--- You can't touch it!
Although the button has physical space, it can’t be interacted with in any way.
visibility: hidden
and assistive technology #
The main reason the visibility: hidden
rule isn’t just about visual visibility is because it affects the elements visibility to assistive technology as well. When we apply visibility: hidden
to an element, it also removes it from the accessibility tree, which makes it invisible to technologies like screen readers.
Take, for example, the issue I came across. I have a <button>
element, with a nested <span>
.
<button>
<span style="visibility: hidden">Button label here</span>
</button>
If we look at the accessibility properties of the element, we will see that the <button>
appears to have no text content within it and therefore no accessible name.
Although the visibility property may seem to only affect visual visibility, we can see that it does much more than that.