optimize downloading

revision:


Content

definition and usage optimize CSS loading performance optimize CSS performance optimize image dowwnloading


definition and usage

top

Web performance is the objective measurements and the perceived user experience of load time and runtime.

Web performance is how long a site takes to load, become interactive and responsive, and how smooth the content is during user interactions - is the scrolling smooth? are buttons clickable? Are pop-ups quick to load and display, and do they animate smoothly as they do so?
Web performance includes both objective measurements like time to load, frames per second, and time to become interactive, and subjective experiences of how long it felt like it took the content to load.

Web performance remains a problem.

According to HTTP Archive, the average webpage requires a 2 MB download, makes more than 60 HTTP requests, and can take 18 seconds to load on a mobile device fully.
Stylesheets account for 60 kB split over seven requests, so it's rarely a top priority when attempting to address performance issues.
However, CSS does have an effect, however slight it may seem.

Once you've addressed your JavaScript, learning to optimize CSS properly should be the next priority.


optimize CSS loading performance

top

Optimize web font usage:

only load the fonts you need,
only load the weights and styles you need,
limit the characters required,
consider variable fonts,
load fonts from your local server,
consider OS fonts or web-safe fonts.

Use an appropriate font-loading option:

the CSS font-display property can select an alternative option:

auto: the browser's default behavior (usually flash-of-invisible-text (FOIT));
block: text is invisible for up to three seconds; there's no font swap, but the text can take time to appear;
swap: the first fallback is used until the web font is available; text is immediately readable, but the font swap effect can be jarring; font style matcher can be used to define a similarly sized fallback;
fallback: a compromise between FOIT (flash-of-invisible-text) and FOUT (flash-of-unstyled-text); text is invisible for a short period (typically 100 ms), then the first fallback is used until the web font is available;
optional: similar to fallback, except no font swapping occurs; the web font will only be used if it's available within the initial period; the first-page view will likely show a fallback font, with subsequent views using the downloaded and cached web font.

Using swap, fallback, or optional can offer a perceived performance boost.

Avoid CSS @import:

the @import at-rule allows CSS files to be included within others. This seems like an effective way to load smaller components and fonts. Unfortunately, each @import is render-blocking, and every file must be loaded and parsed in series. Multiple <link> tags within HTML is more efficient and loads CSS files in parallel.

Concatenate and minify CSS:

Modern build tools, CSS pre-processors - such as Sass - and WordPress plugins can combine all partials into one large CSS file. Unnecessary whitespace, comments, and characters are then removed to reduce the file size to a minimum. Multiple files are less of a performance problem with HTTP/2 and higher, but a single file requires just one header and can be gzipped and cached more efficiently.

Preload CSS files:

The <link> tag provides an optional "preload attribute" that can start a download immediately rather than waiting for the real reference in the HTML.

code:
            <!DOCTYPE html>
            <html lang="en">
            <head>
            <meta charset="UTF-8">
            <title>My page</title>
            <!-- preload styles -->
            <link rel="preload" href="/css/main.css" as="style" />
            <!-- lots more code -->
            <!-- load preloaded styles -->
            <link rel="stylesheet" href="/css/main.css" />
        

Use critical inline CSS:

This improves performance by:

identifying essential styles used by elements above the fold (those visible when the page loads);
inlining that critical CSS into a <style> tag in the <head>;
loading the remaining CSS asynchronously to avoid render blocking; this can be accomplished by loading the stylesheet in a “print” style that the browser gives a lower priority. JavaScript then switches it to an “all” media style once the page has loaded (a <noscript> ensures the CSS works is JavaScript is not available):

code:
                <style>
                    /* critical styles */
                    body { font-family: sans-serif; color: #111; }
                    </style>
                    <!-- load remaining styles -->
                    <link rel="stylesheet" 
                        href="/css/main.css"
                        media="print" 
                    onload="this.media='all'">
                    <noscript>
                    <link rel="stylesheet" href="/css/main.css">
                    </noscript>
                </style>
        

Use media query rendering:

A single concatenated and minified file will benefit most sites, but sites that require a significant quantity of larger screen styles could split CSS files and load using a media query:

code:
            <!-- core styles loaded on all devices -->
            <link rel="stylesheet" href="core.css">
            <!-- served to screens at least 40em wide -->
            <link rel="stylesheet" media="(min-width: 40em)" href="40em.css">
            <!-- served to screens at least 80em wide -->
            <link rel="stylesheet" media="(min-width: 80em)" href="80em.css">
        

Use progressive rendering

Progressive rendering is a technique that defines individual stylesheets for separate pages or components. It can benefit very large sites where individual pages are constructed from an extensive range of components. Each CSS file is loaded immediately before a component is referenced in the HTML.

code:
        <head>
            <!-- core styles -->
            <link rel="stylesheet" href="core.css" />
          </head>
          <body>
            <!-- header -->
            <link rel="stylesheet" href="header.css" />
            <header>...</header>
            <!-- primary content -->
            <link rel="stylesheet" href="main.css" />
            <main>
              <!-- widget styling -->
              <link rel="stylesheet" href="widget.css" />
              <div class="mywidget>...</div>
            </main>
            <!-- footer -->
            <link rel="stylesheet" href="footer.css" />
            <footer>...</footer>
          </body>
    

optimize CSS performance

top

Adopt modern layout techniques (grid and flexbox):

float-based layouts are difficult to create, use numerous properties, require continual margin and padding tweaks, must be managed using media queries, and incur considerable browser processing. They were the only viable layout method for many years, but are no longer necessary.

Use either "CSS Flexbox" for one-dimensional layouts, which could wrap to the next row - ideal for menus, image galleries, cards, etc.- or "CSS Grid" for two-dimensional layouts with explicit rows and columns - ideal for page layouts. Both are simpler to develop, use less code, render faster, and adapt to any screen size without media queries.

Very old browsers will not recognize modern flexbox and grid properties, so each element becomes a block. Show them in a simple mobile-like linear layout: there's no need to emulate the design with float-based fallbacks.

Replace images with CSS gradients and effects:

Where possible, opt for CSS code rather than images. Experiment with gradients, borders, radius, shadows, filters, blend modes, masks, clipping, and pseudo-element effects to reuse or replace existing images.
CSS effects use considerably less bandwidth, are easier to modify, and can usually be animated.

Avoid overusing expensive properties:

Some CSS requires more processing than others. The following properties trigger painting calculations which can be expensive when used in excess: position: fixed, border-radius, box-shadow, text-shadow, opacity, transform, filter, backdrop-filter, background-blend-mode

Use CSS transitions and animations when possible:

CSS transitions and animations will always be smoother than JavaScript-powered effects, which modify similar properties. They won't be processed in very old browsers.
However, avoid excessive animation. Effects should enhance the user experience without adversely affecting performance or causing motion sickness. Check the "prefers-reduced-motion media query" and disable animations when necessary.

Avoid animating properties which trigger a re-Layout;

Altering an element's dimensions (width, height, padding, border) or the position (top, bottom, left, right, margin) can cause the whole page to re-layout on every animation frame.
The most efficient properties to animate are: opacity, filter (blur, contrast, shadow, and other effects), transform (to translate (move), scale, or rotate an element).
Browsers can use the hardware-accelerated GPU to render these effects in their own layer, so only the compositing stage is affected.
If you have to animate other properties, you may improve performance by taking the element out of the page flow with "position: absolute".

Watch for complex selectors:

Browsers will quickly parse the most complex CSS selectors, but simplifying them reduces file sizes and improves performance. Complex selectors are often generated when you create deeply nested structures in CSS preprocessors like Sass.

Indicate which elements will change:

The CSS will-change property allows you to warn how an element will be changed or animated so the browser can make optimizations in advance.

code:
        .myelement {
            will-change: transform, opacity;
          }
    

Any number of comma-separated values can be defined, but the property should only be used as a last resort to fix known performance issues. You should not apply it to too many elements, and be sure to give it sufficient time to initialize.

Consider CSS containment:

Containment is a new CSS feature that can improve performance by allowing you to identify isolated subtrees of a page. The browser can optimize processing by rendering — or not rendering — a specific DOM content block.
The contain property accepts one or more of the following values in a space-separated list:

none: containment is not applied;
layout: the layout of the element is isolated from the rest of the page — its content will not affect other elements;
paint: children of the element are not displayed outside its boundary;
size: the size of the element can be determined without checking child elements — the dimensions are independent of the content.

Two special values are also available:

strict: all containment rules (except none) are applied;
content: applies layout and paint.

A page has a <ul> list with "contain: strict;" applied. If you change the content of any child <li>, the browser will not recalculate the size or position of that item, other items in the list, or any other elements on the page.

CSS containment is supported in most modern browsers. There's no support in Safari or older applications, but containment can be safely used in these because the browser will simply ignore the property.

React to the save-data header

Save-Data is an HTTP request header indicating the user has requested reduced data. It may be labeled “Lite” or “Turbo” mode in some browsers. When enabled, a Save-Data header is sent with every browser request.

code:
        GET /main.css HTTP/1.0
        Host: site.com
        Save-Data: on
    

optimize image dowwnloading

top

Use the height and width attributes in HTML to get proper aspect-ratio on first render.

Layout shifts during downloading of web pages are very disrupting to the user, especially if he already started reading the article.
This also puts extra work on the browser to recalculate the page layout as each image arrives across the internet.
On a complex page with a lot of images this can place a considerable load on the device at a time when it's probably got a lot of better things to deal with!

The traditional way to avoid this is to provide width and height attributes in the <img> markup so even when the browser has just the HTML, it is still able to allocate the appropriate amount of space.

code:
        <h1>Your title</h1>
        <p>Introductory paragraph.</p>
        <img src="hero_image.jpg" alt="" width="400" height="400">
        <p>Lorem ipsum dolor sit amet, consectetur…</p>
    

Always specify both the height and width attributes for images. If height and width are set, the space required for the image is reserved when the page is loaded. However, without these attributes, the browser does not know the size of the image, and cannot reserve the appropriate space to it. The effect will be that the page layout will change during loading (while the images load).

Downsizing a large image with the height and width attributes forces a user to download the large image (even if it looks small on the page). To avoid this, rescale the image with a program before using it on a page.

Use content-visibility: auto;

The content-visibility property in CSS indicates to the browser whether or not an element's contents should be rendered at initial load time. So, as the browser starts loading content and is playing it on the screen, this property allows us to step in and tell the browser not to load the contents of an element until it's needed.

Think of it sort of like "lazy" loading in the sense that an off-screen element's children are not rendered until they enter the viewport.

The main point of using content-visibility is performance. It can help to speed up page load because the browser is able to defer rendering elements that are not in the user's viewport until the user scrolls to them. The results can be dramatic.

Syntax: content-visibility: [visible | auto | hidden];, with three values:

hidden: the element bypasses its contents (kind of similar to applying display: none; to the contents).
visible: there is no effect and the element is rendered as normal.
auto: the element has layout, style, and paint containment; the browser gets to determine if this content is relevant to the user and, if it isn't, then the browser will skip it. At the same time, the element is still focusable, selectable and accessible to things like tabbing and in-page search.

code:
        <style>
            /* This probably only makes sense for images within the main scrollable area of your page. */
            main img {
              /* Only render when in viewport */
              content-visibility: auto;
            }
          </style>
    

Send AVIF when you can; transcode images to AVIF and webp and generate "picture" element

AVIF is the most recent image format that has gained adoption in web browsers. It is currently supported in Chromium browsers, and available behind a flag in Firefox. Safari support isn't available yet, but given that Apple is a member of the group that is behind the format, we can expect future support.

AVIF is notable because it very consistently outperforms JPEG in a very significant way. This is different from WebP which doesn't always produce smaller images than JPEG and may actually be a net-loss due to lack of support for progressive loading.

Use responsive images syntax

Web browsers will reserve the correct vertical space for the image before it loads, if the width and height attributes are provided for the img element. This avoids "cumulative layout shift (CLS)".

code:
            <style>
                img { max-width: 100%; height: auto; }
            </style>
            <!-- Providing width and height is more important than ever. -->
            <img height="853" width="1280" … />
        

To add responsive images in HTML, the starting point is always the standard image syntax, consisting of the <img> element and the src and alt attributes:<img src="images/myimage.jpg alt="my image">. For the "src attribute", either an absolute (starting with the http:// or https:// protocol) or a relative path can be used.

The responsive image syntax needs to be built on top of this standard <img> definition; this also ensures backward compatibility.

Set far-out expires headers on images and have a cache-busting strategy (like changing the file name)

Expires headers are Hypertext Transfer Protocol (HTTP) page headers that tell web browsers whether they should download a resource remotely – from the website's server – or load the resource from a local cache. They are known as “expires headers” because they include an expiration time for resources. After a listed resource has expired, web browsers will have to download it again.

To add Expires Headers to your site, you need to edit the .htaccess file and add the following code:

code:
        ## EXPIRES CACHING ##
        
        ExpiresActive On
        # Images
        ExpiresByType image/jpeg “access plus 1 year”
        ExpiresByType image/gif “access plus 1 year”
        ExpiresByType image/png “access plus 1 year”
        ExpiresByType image/webp “access plus 1 year”
        ExpiresByType image/svg+xml “access plus 1 year”
        ExpiresByType image/x-icon “access plus 1 year”
        # Video
        ExpiresByType video/mp4 “access plus 1 year”
        ExpiresByType video/mpeg “access plus 1 year”
        # CSS, JavaScript
        ExpiresByType text/css “access plus 1 month”
        ExpiresByType text/javascript “access plus 1 month”
        ExpiresByType application/javascript “access plus 1 month”
        # Others
        ExpiresByType application/pdf “access plus 1 month”
        ExpiresByType application/x-shockwave-flash “access plus 1 month”
        
        ## EXPIRES CACHING ##
    

Use loading="lazy"

Lazy loading is a strategy to identify resources as non-blocking (non-critical) and load these only when needed. It's a way to shorten the length of the critical rendering path, which translates into reduced page load times. Lazy loading can occur on different moments in the application, but it typically happens on some user interactions such as scrolling and navigation.

Adding loading="lazy" to the img instructs the browser to only start fetching the image as it gets closer to the screen and is likely to actually be rendered.

code:
        <img src=" . . ." loading="lazy" … />
    

Use decoding="async"

The async attribute is a boolean attribute. If the async attribute is set, the script is downloaded in parallel to parsing the page, and executed as soon as it is available. The parsing of the page is interrupted once the script is downloaded completely, and then the script is executed, before the parsing of the rest of the page continues.

The async attribute is only for external scripts (and should only be used if the src attribute is present).

There are several ways an external script can be executed:

If async is present: the script is downloaded in parallel to parsing the page, and executed as soon as it is available (before parsing completes);
If defer is present (and not async): the script is downloaded in parallel to parsing the page, and executed after the page has finished parsing.
If neither async or defer is present: the script is downloaded and executed immediately, blocking parsing until the script is completed.

Adding decoding="async" to the img gives the browser permission to decode the image off the main thread avoiding user impact of the CPU-time used to decode the image. This should have no discernible downside except that it cannot always be the default for legacy reasons.

code:
        <img src=" . . ." decoding="async" … />
    

Use inline CSS/SVG for a blurry placeholder.

A blurry placeholder is an inline image that provides the user some notion of the image that will load eventually without requiring fetching bytes from the network.

The blurry placeholder is inlined as a background-image of the image. This avoids using a second HTML element and it naturally hides the placeholder when the image loads, so that no JavaScript is needed to implement this.

The blurry placeholder wraps the data URI of the actual image in a data URI of a SVG image. That is done because the blurring of the image is done at the SVG level instead of through a CSS filter. The result is that the blurring is only performed once per image when the SVG is rasterized, instead of on every layout saving CPU.