More on content-visibility

Avatar of Chris Coyier
Chris Coyier on

Back in August 2020, when the content-visiblity property in CSS trickled its way into Chrome browsers, Una Kravets and Vladimir Levin wrote about it and we covered it. The weirdest part is that to get the performance value out of it, you pair it with contain-intrinsic-size on these big chunks of the page where you insert some arbitrary guess at a height. I wrote:

That part seems super weird to me. Just guess at a height? What if I’m wrong? Can I hurt performance? Can (or should) I change that value at different viewports if the height difference between small and large screens is drastic?

Jake Archibald and Das Surma just did a video on all this and it helped clarify that a bit. You can see at about 7:30 in just how confusing it is. Jake used this massive HTML spec page as a demo, and made <section> wrappers around big chunks of HTML, and applied:

section {
  content-visibility: auto; /* this is the thing that delays painting */
  contain-intrinsic-size: 1px 5000px; /* this is the guess at the height of the content, and also saying width doesn't matter */
}

Apparently that 5000px isn’t the height of the element, it’s the size of the content of that element. I guess that matters because it will push that parent element taller by that number, unless the parent element overrides that with a height of its own. The magic comes from the fact that the browser will only paint¹ the first section (where it’s very likely the viewport isn’t over 5000px tall) and defer the painting on the rest. Sorta like lazy loading, but everything rather than media alone. It assumes the next section is 5000px tall, but once the top of it becomes visible, it will actually get painted and the correct height will be known. So assuming your page is just big ass blocks on top of each other, using an extremely large number should work fine there. Godspeed if your site is more complicated than that, I guess.

It’s a good video and you should watch it:

This is yet another thing where you have to inform the browser about your site so that it can Do Performance Good™. It is information that it can figure out by itself, but not until it has done things that have a performance cost. So you have to tell it up front, allowing it to avoid doing certain types of work. With responsive images, if we give images a srcset attribute with images and tell the browser in advance how big they are, including a sizes attribute with information about how our CSS behaves, it can do calculations ahead of time that only download the best possible image. Likewise, with the will-change property in CSS, we can tell the browser when we’re going to be doing movement ahead of time so it can pre-optimize for that in a way it couldn’t otherwise. It’s understandable, but a little tiresome. It’s like we need a stuff-you-need-to-know.manifest file to give browsers before it does anything else — only that would be an additional request!

The accessibility implications are important too. Steve Faulkner did a test applying content-visibility: auto to images and paragraphs:

The content is visually hidden, but in both JAWS and NVDA the hidden <img> is announced but the content of the <p> element is not. This has to do with how the img and the p element content are represented in the browser accessibility tree: The img is exposed in the accessibility tree with the alt text as the accessible name. The content of the p element is not present in the accessibility tree.

He notes that content hidden this way should not be available to screen readers, per the spec. I could see it going either way, like hide it all as if it was display: none, meaning none of it is in the accessibility tree. Or, leave it all in the accessibility tree. Right now it’s a tweener where you might see a bunch of stray images in the accessibility tree without any other context than their alt text. This is an interesting example of new tech going out with more rough edges than you might like to see.

Speaking of alt text, we all know those shouldn’t be empty when they represent important content that needs to be described to someone who can’t see them. They should be like paragraphs, says Dave:

I finally made the simplest of all connections: alt text is like a paragraph. Word pictures. Basic I know, but it helps me contextualize how to write good alt text as well as source order of my code.

I don’t want to be overly negative here! The performance gains for setting up a long-scrolling page with content-visibility is huge and that’s awesome. Being able to inform the browser about what is OK not to paint in two lines of code is pretty nice.

  1. I keep saying “paint” but I’m not sure if that’s really the right term or if it means something more specific. The spec says stuff like “allowing user agents to potentially omit large swathes of layout and rendering work until it becomes needed” (emphasis mine).