docs.rodeo

MDN Web Docs mirror

CSS performance optimization

{{LearnSidebar}} {{PreviousMenuNext("Learn_web_development/Extensions/Performance/html", "Learn_web_development/Extensions/Performance/business_case_for_performance", "Learn_web_development/Extensions/Performance")}} 

When developing a website, you need to consider how the browser is handling the CSS on your site. To mitigate any performance issues that CSS might be causing, you should optimize it. For example, you should optimize the CSS to mitigate render-blocking and minimize the number of required reflows. This article walks you through key CSS performance optimization techniques.

Prerequisites: Basic software installed, and basic knowledge of client-side web technologies.
Objective: To learn about the impact of CSS on website performance and how to optimize your CSS to improve performance.

To optimize or not to optimize

The first question you should answer before starting to optimize your CSS is “what do I need to optimize?”. Some of the tips and techniques discussed below are good practices that will benefit just about any web project, whereas some are only needed in certain situations. Trying to apply all these techniques everywhere is probably unnecessary, and may be a waste of your time. You should figure out what performance optimizations are actually needed in each project.

To do this, you need to measure the performance of your site. As the previous link shows, there are several different ways to measure performance, some involving sophisticated performance APIs. The best way to get started however, is to learn how to use tools such as built-in browser network and performance tools, to see what parts of the page load are taking a long time and need optimizing.

Optimizing rendering

Browsers follow a specific rendering path — paint only occurs after layout, which occurs after the render tree is created, which in turn requires both the DOM and the CSSOM trees.

Showing users an unstyled page and then repainting it after the CSS styles have been parsed would be a bad user experience. For this reason, CSS is render blocking until the browser determines that the CSS is required. The browser can paint the page after it has downloaded the CSS and built the CSS object model (CSSOM).

To optimize the CSSOM construction and improve page performance, you can do one or more of the following based on the current state of your CSS:

Handling animations

Animations can improve perceived performance, making interfaces feel snappier and making users feel like progress is being made when they are waiting for a page to load (loading spinners, for example). However, larger animations and a higher number of animations will naturally require more processing power to handle, which can degrade performance.

The simplest advice is to cut down on all unnecessary animations. You could also provide users with a control/site preference to turn off animations if they are using a low-powered device or a mobile device with limited battery power. You could also use JavaScript to control whether or not animation is applied to the page in the first place. There is also a media query called prefers-reduced-motion that can be used to selectively serve animation styles or not based on a user’s OS-level preferences for animation.

For essential DOM animations, you are advised to use CSS animations where possible, rather than JavaScript animations (the Web Animations API provides a way to directly hook into CSS animations using JavaScript).

Choosing properties to animate

Next, animation performance relies heavily on what properties you are animating. Certain properties, when animated, trigger a reflow (and therefore also a repaint) and should be avoided. These include properties that:

Modern browsers are smart enough to repaint only the changed area of the document, rather than the entire page. As a result, larger animations are more costly.

If at all possible, it is better to animate properties that do not cause reflow/repaint. This includes:

Animating on the GPU

To further improve performance, you should consider moving animation work off the main thread and onto the device’s GPU (also referred to as compositing). This is done by choosing specific types of animations that the browser will automatically send to the GPU to handle; these include:

Animation on the GPU can result in improved performance, especially on mobile. However, moving animations to GPU is not always that simple. Read CSS GPU Animation: Doing It Right (smashingmagazine.com, 2016) for a very useful and detailed analysis.

Optimizing element changes with will-change

Browsers may set up optimizations before an element is actually changed. These kinds of optimizations can increase the responsiveness of a page by doing potentially expensive work before it is required. The CSS will-change property hints to browsers how an element is expected to change.

Note: will-change is intended to be used as a last resort to try to deal with existing performance problems. It should not be used to anticipate performance problems.

.element {
  will-change: opacity, transform;
}

Optimizing for render blocking

CSS can scope styles to particular conditions with media queries. Media queries are important for a responsive web design and help us optimize a critical rendering path. The browser blocks rendering until it parses all of these styles but will not block rendering on styles it knows it will not use, such as the print stylesheets. By splitting the CSS into multiple files based on media queries, you can prevent render blocking during download of unused CSS. To create a non-blocking CSS link, move the not-immediately used styles, such as print styles, into separate file, add a <link> to the HTML mark up, and add a media query, in this case stating it’s a print stylesheet.

<!-- Loading and parsing styles.css is render-blocking -->
<link rel="stylesheet" href="styles.css" />

<!-- Loading and parsing print.css is not render-blocking -->
<link rel="stylesheet" href="print.css" media="print" />

<!-- Loading and parsing mobile.css is not render-blocking on large screens -->
<link
  rel="stylesheet"
  href="mobile.css"
  media="screen and (max-width: 480px)" />

By default, the browser assumes that each specified style sheet is render blocking. Tell the browser when the style sheet should be applied by adding a media attribute with the media query. When the browser sees a style sheet it knows that it only needs to apply it for a specific scenario, it still downloads the stylesheet, but doesn’t render block. By separating out the CSS into multiple files, the main render-blocking file, in this case styles.css, is much smaller, reducing the time that rendering is blocked.

Improving font performance

This section contains some useful tips for improving web font performance.

In general, think carefully about the fonts you use on your site. Some font files can be very large (multiple megabytes). While it can be tempting to use lots of fonts for visual excitement, this can slow down page load significantly, and cause your site to look like a mess. You probably only need about two or three fonts, and you can get away with less if you choose to use web safe fonts.

Font loading

Bear in mind that a font is only loaded when it is actually applied to an element using the font-family property, not when it is first referenced using the @font-face at-rule:

/* Font not loaded here */
@font-face {
  font-family: "Open Sans";
  src: url("OpenSans-Regular-webfont.woff2") format("woff2");
}

h1,
h2,
h3 {
  /* It is actually loaded here */
  font-family: "Open Sans";
}

It can therefore be beneficial to use rel="preload" to load important fonts early, so they will be available more quickly when they are actually needed:

<link
  rel="preload"
  href="OpenSans-Regular-webfont.woff2"
  as="font"
  type="font/woff2"
  crossorigin />

This is more likely to be beneficial if your font-family declaration is hidden inside a large external stylesheet, and won’t be reached until significantly later in the parsing process. It is a tradeoff however — font files are quite large, and if you preload too many of them, you may delay other resources.

You can also consider:

Loading only the glyphs you need

When choosing a font for body copy, it is harder to be sure of the glyphs that will be used in it, especially if you are dealing with user-generated content and/or content across multiple languages.

However, if you know you are going to use a specific set of glyphs (for example, glyphs for headings or specific punctuation characters only), you could limit the number of glyphs the browser has to download. This can be done by creating a font file that only contains the required subset. A process called subsetting. The unicode-range @font-face descriptor can then be used to specify when your subset font is used. If the page doesn’t use any character in this range, the font is not downloaded.

@font-face {
  font-family: "Open Sans";
  src: url("OpenSans-Regular-webfont.woff2") format("woff2");
  unicode-range: U+0025-00FF;
}

Defining font display behavior with the font-display descriptor

Applied to the @font-face at-rule, the font-display descriptor defines how font files are loaded and displayed by the browser, allowing text to appear with a fallback font while a font loads, or fails to load. This improves performance by making the text visible instead of having a blank screen, with a trade-off being a flash of unstyled text.

@font-face {
  font-family: someFont;
  src: url(/path/to/fonts/someFont.woff) format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: fallback;
}

Optimizing styling recalculation with CSS containment

By using the properties defined in the CSS containment module, you can instruct the browser to isolate different parts of a page and optimize their rendering independently from one another. This allows for improved performance in rendering individual sections. As an example, you can specify to the browser to not render certain containers until they are visible in the viewport.

The {{cssxref("contain")}}  property allows an author to specify exactly what containment types they want applied to individual containers on the page. This allows the browser to recalculate layout, style, paint, size, or any combination of them for a limited part of the DOM.

article {
  contain: content;
}

The {{cssxref("content-visibility")}}  property is a useful shortcut, which allows authors to apply a strong set of containments on a set of containers and specify that the browser should not lay out and render those containers until needed.

A second property, {{cssxref("contain-intrinsic-size")}} , is also available, which allows you to provide a placeholder size for containers while they are under the effects of containment. This means that the containers will take up space even if their contents have not yet been rendered, allowing containment to do its performance magic without the risk of scroll bar shift and jank as elements render and come into view. This improves the quality of the user experience as the content is loaded.

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

See also

{{PreviousMenuNext("Learn_web_development/Extensions/Performance/html", "Learn_web_development/Extensions/Performance/business_case_for_performance", "Learn_web_development/Extensions/Performance")}} 

In this article

View on MDN