Skip to main content

Improve initial load time by skipping rendering off-screen content.

the
content-visibility

, released in Chromium 85, could be one of the most powerful new CSS properties for improving page loading performance. content-visibility allows the user agent to skip rendering work on an element, including layout and painting, until needed. Because rendering is skipped, if a large part of your content is off-screen, take advantage of the content-visibility makes the initial user load much faster. It also enables faster interactions with onscreen content. Looking good.

demo-9654979

In our demo article, applying content-visibility: auto to fragmented content areas gives a 7 times increased rendering performance on initial load. Read on for more information.

Browser support

content-visibility is based on primitives within the the CSS containment specification. While content-visibility only supports Chromium 85 for now (and is considered "Worth making a prototype" for Firefox), the containment specification is supported in most modern browsers.

CSS containment

The overall and key goal of CSS containment is to enable performance improvements for rendering web content by providing predictable isolation of a DOM hive from the rest of the page.

Basically, a developer can tell a browser which parts of the page are encapsulated as a content set, allowing browsers to reason about content without having to consider the out-of-subtree state. Knowing which pieces of content (sub-trees) contain isolated content means that the browser can make optimization decisions for the rendering of the page.

There are four types of CSS containment, each a potential value for the contain CSS property, which can be combined into a space-separated list of values:

  • size: Size containment on an item ensures that the item's box can be placed without the need to examine its descendants. This means that we can potentially skip the design of the descendants if all we need is the size of the element.
  • layout: Design contention means that descendants do not affect the external design of other boxes on the page. This allows us to potentially skip the design of the descendants if all we want to do is design other boxes.
  • style: Style contention ensures that properties that can have effects on more than their descendants do not escape the element (for example, counters). This allows us to potentially skip the style calculation for descendants if all we want is to calculate styles in other elements.
  • paint: Paint containment ensures that the offspring of the container box are not displayed out of bounds. Nothing can visibly overflow the item, and if an item is off screen or not visible, its descendants will not be visible either. This allows us to potentially skip painting the descendants if the element is off screen.

Skip rendering work with content-visibility

It can be difficult to determine which containment values to use, as browser optimizations can only be triggered when an appropriate set is specified. You can play with the values to see what works best, or you can use another CSS property called content-visibility to apply the necessary containment automatically. content-visibility ensures that you get the highest performance gains the browser can provide with minimal effort on your part as a developer.

The content visibility property accepts multiple values, but car it is the one that provides immediate performance improvements. An item that has
content-visibility: auto Profits layout, style and paint containment. If the element is off screen (and not relevant to the user, the relevant elements would be the ones that have focus or selection in their subtree), it also wins size containment (and stops
painting
and
test of success
its content).

What does this mean? In short, if the element is off screen, its descendants are not rendered. The browser determines the size of the element without considering any of its contents, and stops there. Most of the rendering, such as the element's hive layout and style, is ignored.

As the element approaches the viewport, the browser no longer adds the size
containment and begins to paint and test the content of the item. This allows rendering work to be done just in time for the user to see it.

Example: a travel blog

In this example, we base our travel blog on the right and apply content-visibility: auto to fragmented areas on the left. The results show render times ranging from 232 ms to 30 ms on the initial page load.

A travel blog usually contains a set of stories with some images and descriptive text. This is what happens in a typical browser when you navigate to a travel blog:

  1. A part of the page is downloaded from the web, along with the necessary resources.
  2. The browser designs and distributes all the content on the page, regardless of whether the content is visible to the user.
  3. The browser returns to step 1 until the entire page and resources are downloaded.

In step 2, the browser processes all the content looking for things that may have changed. Update the style and layout of any new items, along with items that may have changed as a result of new updates. This is rendering work. This takes time.

travelblog-9968669

An example of a travel blog. Watch Demo on Codepen

Now consider what happens if you put content-visibility: auto in each of the individual blog stories. The general cycle is the same: the browser downloads and displays fragments of the page. However, the difference is in the amount of work you do in step 2.

With content visibility, it will apply style and layout to all content that is currently visible to the user (is on screen). However, when rendering the story that is completely off-screen, the browser will skip the rendering work and only apply style and layout to the item's box.

The performance of loading this page would be as if it contained full stories on screen and empty boxes for each of the stories off screen. This works much better, with expected reduction of 50% or more of charge rendering cost. In our example, we see an impulse of a 232 ms giving time to a
30 ms render time. That's a 7 times increase performance.

What is the work you need to do to get these benefits? First, we divide the content into sections:

travelblog-chunked-2397894

Example of content fragmentation into sections with the story applied class, receive content-visibility: auto. Watch Demo on Codepen

Then we apply the following style rule to sections:

.story {
content-visibility : auto ;
contain-intrinsic-size : 1000px ;
}

Note that as content moves in and out of visibility, it will start and stop rendering as needed. However, this does not mean that the browser will have to render and re-render the same content over and over again, as rendering work is saved when possible.

Specify the natural size of an element with contain-intrinsic-size

To realize the potential benefits of content-visibility, the browser must apply size containment to ensure that the rendering results of the contents do not affect the size of the element in any way. This means that the element will be rendered as empty. If the element does not have a specified height in a regular block layout, then it will have a height of 0.

This might not be ideal, as the size of the scroll bar will change, depending on each story having a non-zero height.

Fortunately, CSS provides another property, contain-intrinsic-size, which effectively specifies the natural size of the element if the element is affected by size contention. In our example, we are setting it to 1000px as an estimate of the height and width of the sections.

This means that it will be rendered as having a single dimension child of "intrinsic size", ensuring that your unsized divs still take up space.
contain-intrinsic-size acts as a placeholder size instead of rendered content.

Hide content with content-visibility: hidden

What if you want to keep the content raw regardless of whether it is on screen or not, while taking advantage of the cached rendering state? Get in:
content-visibility: hidden.

the content-visibility: hidden The property gives you the same raw content and cached status benefits as content-visibility: auto does off screen. However, unlike car, it does not automatically start rendering on screen.

This gives you more control, allowing you to hide the content of an item and then quickly display it.

Compare it to other common ways to hide element content:

  • display: none- Hides the item and destroys its rendering state. This means that displaying the element is as expensive as rendering a new element with the same content.
  • visibility: hidden- Hides the item and maintains its rendering state. This does not actually remove the item from the document as it (and its subtree) still takes up geometric space on the page and is still clickable. It also updates the rendering status anytime it is needed, even when it's hidden.

content-visibility: hiddenon the other hand, it hides the element while preserving its rendering state, so if there is any change that needs to happen, it only happens when the element is shown again (i.e. the
content-visibility: hidden property is removed).

Some great use cases for content-visibility: hidden they are when advanced virtual shifters are implemented and the layout is measured.

conclusion

content-visibility and the CSS containment spec means that some interesting performance increases will go straight to your CSS file. For more information on these properties, see: