clubmate.fi

A good[ish] website

Web development blog, loads of UI and JavaScript topics

Speed up page rendering by setting content-visibility to auto

Filed under: HTML/CSS— Tagged with: css, layout, pagespeed

There’s a new CSS property in town called content-visibility. It will defer the rendering of elements until they’re about to hit the viewport. Calling it "lazy rendering" might be a helpful analogy.

Note: at the moment of writing this, content-visibility ony works on new Chromium based browsers. Version 85 to be precise. Which seems to be about 65% of global usage.

Content visibility demo

Here’s a demo of a simple landing page with cat pictures:

A demo page which uses content-visibility.

There’s not much to see, the difference is more under the hood.

How does "content-visibility: auto" actually work?

Although browsers are generally fast at rendering HTML (it’s their 9-to-5 job), but they still break a sweat for it, specifically when working on modern sites packed full of visually esoteric bells and whistles.

But why do you need to render a module at the bottom of the page, when you’re at the top? Because the browser needs to know all the elements on the page before it can start the paint, there can be an element in the footer that is absolutely positioned to be at the top of the page, for example. The browser needs to see the full page to be able to render anything.

But not anymore, kind of.

We’re all, more-or-less, familiar with image lazy loading, right? content-visibility is similar, but for CSS rendering. Now we can tell the browser to defer the rendering work of certain segments of the page—like layout-heavy landing page modules—and only render them when they are about to be visible. Without breaking in-page search or SEO.

Sounds too good to be true? You’re right, it is too good to be true, content-visibility has some accessibility issues (see the accessibility issues segment below).

The code

Only two CSS rules are needed:

.module {
  content-visibility: auto;
  content-intrinsic-size: 0 500px;
}

content-intrinsic-size is needed because the component looses it’s height in the layout, because the browser needs to render the component before it knows that. The 500px is just an estimation.

Note that content-intrinsic-size doesn’t define the elements size, but size of the content size.

Here are all the values you can feed it:

visible
Normal rendering behavior, nothing will happen.
hidden
The content is not visible at all, including in-page search or search engine crawlers. Similar to display: none, but not completely. This post "Short note on content-visibility: hidden" by stevef, goes deeper into the topic.
auto
This will defer the rendering of the element, by turning on layout containment, style containment, and paint containment. The element is rendered and painted before it enters the viewport, for the user, it should completely unnoticeable.

Some gotchas

Like many new programming related things, content-visibility comes with its limitations and bugs.

Jerky looking scrollbar

If you scroll the above demo you’ll see that the scrollbar appears rather jagged, that’s the page content adjusting itself to the new content being rendered. The more accurately you match the intrinsic-size to the element’s actual height, the less jerky it is. But that’s impossible to do accurately because often on fluid sites the content height is dependent on the screen width.

I really don’t mind that, though. I’ll take faster load time over such a small aesthetic imperfection. Though, it’s good to keep in mind, that a clean looking website conveys trust and might translate to a higher conversion rate. But I think content-visibility makes the cut in that sense.

Accessibility issues

Specifically with headings:

Neat. But my spidey accessibility senses went off as soon as I read those words: off-screen and rendering. [...] My initial thought was that content-visibility: auto could pose an access problem on initial load by suppressing part of the page outline from screen readers, so I set out to test it. Marcy Sutton

According to that article 👆 (great read), headings inside the components of which render work has been deferred, are not visible to a some screen readers, NVDA to be specific. This means that user needs to scroll through the page for the headings to be read.

Which is not good, and which makes this kind of useless, at least for now. Based on this Tweet it seems like a bug and will be fixed at some point 🤞

So, use content-visibility only on elements that have no headings or landmarks in them. Like a footer, or a long list of cards-like UI elements, or long image-heavy lists.

It also seems that content-visibility can break in-page navigation.

Conclusions

This will be an amazing feature when the accessibility issues have been solved.

Also a cool side effect is a smoother screen resizing experience, because there’s less layout for the browser to calculate. I’m not 100% sure, but content-visibility might solve the ages old issues of infinitely scrolling list; list gets big, scrolling becomes janky. That’s why we have packages like react-window.

Thanks for reading. Hope this was helpful.

Comments would go here, but the commenting system isn’t ready yet, sorry. Tweet me @hiljaa if you want to make a correction etc.

  • © 2021 Antti Hiljá
  • About
  • Follow me in Twatter → @hiljaa
  • All rights reserved yadda yadda.
  • I can put just about anything here, no one reads the footer anyways.
  • I love u!