In this article, we look at 20 ways to optimize your CSS so that it’s faster-loading, easier to work with and more efficient.
According to the latest HTTP Archive reports, the web remains a bloated mess with the mythical median website requiring 1,700Kb of data split over 80 HTTP requests and taking 17 seconds to fully load on a mobile device.
In this article, we’ll concentrate on CSS. Admittedly, CSS is rarely the worst culprit and a typical site uses 40KB spread over five stylesheets. That said, there are still optimizations you can make, and ways to change how we use CSS that will boost site performance.
You can’t address performance problems unless you know where the faults lie. Browser DevTools are the best place to start: launch from the menu or hit F12, Ctrl + Shift + I or Cmd + Alt + I for Safari on macOS.
All browsers offer similar facilities, and the tools will open slowly on badly-performing pages! However, the most useful tabs include the following …
The Network tab displays a waterfall graph of assets as they download. For best results, disable the cache and consider throttling to a lower network speed. Look for files that are slow to download or block others. The browser normally blocks browser rendering while CSS and JavaScript files download and parse.
The Performance tab analyses browser processes. Start recording, run an activity such as a page reload, then stop recording to view the results. Look for:
Chrome-based browsers provide an Audits tab which runs Google’s Lighthouse tool. It’s often used by Progressive Web App developers, but also makes CSS performance suggestions.
Alternatively, use online analysis tools that are not influenced by the speed and capabilities of your device and network. Most can test from alternative locations around the world:
CSS is unlikely to be the direct cause of performance issues. However, it may load heavy-hitting assets which can be optimized within minutes. Examples:
Images are normally the biggest cause of page bulk, yet many sites fail to optimize effectively:
That said, be aware that _x_Kb of image data is not equivalent to _x_Kb of CSS code. Binary images download in parallel and require little processing to place on a page. CSS blocks rendering and must be parsed into an object model before the browser can continue.
It’s rarely necessary to use background images for borders, shadows, rounded edges, gradients and some geometric shapes. Defining an “image” using CSS code uses considerably less bandwidth and is easier to modify or animate later.
Services such as Google Fonts make it easy to add custom fonts to any page. Unfortunately, a line or two of code can retrieve hundreds of kilobytes of font data. Recommendations:
&text=
value to the font URL — such as fonts.googleapis.com/css?family=Open+Sans&text=SitePon
for displaying “SitePoint” in Open Sans.@import
The @import
at-rule allows any CSS file to be included within another. For example:
/* main.css */
@import url("base.css");
@import url("layout.css");
@import url("carousel.css");
This appears a reasonable way to load smaller components and fonts. It’s not. @import
rules can be nested so the browser must load and parse each file in series.
Multiple <link>
tags within the HTML will load CSS files in parallel, which is considerably more efficient — especially when using HTTP/2:
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="layout.css">
<link rel="stylesheet" href="carousel.css">
That said, there may be more preferable options …
Most build tools allow you to combine all partials into one large CSS file that has unnecessary whitespace, comments and characters removed.
Concatenation is less necessary with HTTP/2, which pipelines and multiplexes requests. In some cases, separate files may be beneficial if you have smaller, regularly-changing CSS assets. However, most sites are likely to benefit from sending a single file that is immediately cached by the browser.
Minification may not bring considerable benefits when you have GZIP enabled. That said, there’s no real downside.
Finally, you could consider a build process that orders properties consistently within declarations. GZIP can maximize compression when commonly used strings are used throughout a file.
For many years it was necessary to use CSS float
to lay out pages. The technique is a hack. It requires lots of code and margin/padding tweaking to ensure layouts work. Even then, floats will break at smaller screen sizes unless media queries are added.
The modern alternatives:
Both options are simpler to develop, use less code, can adapt to any screen size, and render faster than floats because the browser can natively determine the optimum layout.
The most reliable and fastest code is the code you need never write! The smaller your stylesheet, the quicker it will download and parse.
All developers start with good intentions, but CSS can bloat over time as the feature count increases. It’s easier to retain old, unnecessary code rather than remove it and risk breaking something. A few recommendations to consider:
widgets/_carousel.css
.!important
to override the cascade.Tools such as UnCSS can help remove redundant code by analyzing your HTML, but be wary about CSS states caused by JavaScript interaction.
The rise of CSS-in-JS has allowed developers to avoid the CSS global namespace. Typically, randomly generated class names are created at build time so it becomes impossible for components to conflict.
If your life has been improved by CSS-in-JS, then carry on using it. However, it’s worth understanding the benefits of the CSS cascade rather than working against it on every project. For example, you can set default fonts, colors, sizes, tables and form fields that are universally applied to every element in a single place. There is rarely a need to declare every style in every component.
Even the most complex CSS selectors take milliseconds to parse, but reducing complexity will reduce file sizes and aid browser parsing. Do you really need this sort of selector?!
body > main.main > section.first h2:nth-of-type(odd) + p::first-line > a[href$=".pdf"]
Again, be wary of deep nesting in preprocessors such as Sass, where complex selectors can be inadvertently created.
Some properties are slower to render than others. For added jankiness, try placing box shadows on all your elements!
*, ::before, ::after {
box-shadow: 5px 5px 5px rgba(0,0,0,0.5);
}
Browser performance will vary but, in general, anything which causes a recalculation before painting will be more costly in terms of performance:
border-radius
box-shadow
opacity
transform
filter
position: fixed
Native CSS transitions and animations will always be faster than JavaScript-powered effects that modify the same properties. CSS animations will not work in older browsers such as IE9 and below, but those users will never know what they’re missing.
That said, avoid animation for the sake of it. Subtle effects can enhance the user experience without adversely affecting performance. Excessive animations could slow the browser and cause motion sickness for some users.
Animating the dimensions or position of an element can cause the whole page to re-layout on every frame. Performance can be improved if the animation only affects the compositing stage. The most efficient animations use:
opacity
and/ortransform
to translate (move), scale or rotate an element (the original space the element used is not altered).Browsers often use the hardware-accelerated GPU to render these effects. If neither are ideal, consider taking the element out of the page flow with position: absolute
so it can be animated in its own layer.
The will-change property allows CSS authors to indicate an element will be animated so the browser can make performance optimizations in advance. For example, to declare that an element will have a transform
applied:
.myelement {
will-change: transform;
}
Any number of comma-separated properties can be defined. However:
will-change
as a last resort to fix performance issuesScalable vector graphics (SVGs) are typically used for logos, charts, icons, and simpler diagrams. Rather than define the color of each pixel like JPG and PNG bitmaps, an SVG defines shapes such as lines, rectangles and circles in XML. For example:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600">
<circle cx="400" cy="300" r="50" stroke-width="5" stroke="#f00" fill="#ff0" />
<svg>
Simpler SVGs are smaller than equivalent bitmaps and can infinitely scale without losing definition.
An SVG can be inlined directly in CSS code as a background image. This can be ideal for smaller, reusable icons and avoids additional HTTP requests. For example:
.mysvgbackground {
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600"><circle cx="400" cy="300" r="50" stroke-width="5" stroke="#f00" fill="#ff0" /></svg>') center center no-repeat;
}
More typically, SVGs are embedded directly within an HTML document:
<body>
<svg class="mysvg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600">
<circle cx="400" cy="300" r="50" />
<svg>
</body>
This adds the SVG nodes directly into the DOM. Therefore, all SVG styling attributes can be applied using CSS:
circle {
stroke-width: 1em;
}
.mysvg {
stroke-width: 5px;
stroke: #f00;
fill: #ff0;
}
The volume of embedded SVG code is reduced and the CSS styles can be reused or animated as necessary.
Note that using an SVG within an <img>
tag or as a CSS background image means they’re separated from the DOM, and CSS styling will have no effect.
Standard bitmap JPGs, PNGs and GIFs can be encoded to a base64 string within a data URI. For example:
.myimg {
background-image: url('data:image/png;base64,ABCDEFetc+etc+etc');
}
Unfortunately:
While fewer HTTP requests are made, it rarely provides a noticeable benefit — especially over HTTP/2 connections. In general, avoid inlining bitmaps unless the image is unlikely to change often and the resulting base64 string is unlikely to exceed a few hundred characters.
Those using Google page analysis tools will often see suggestions to “inline critical CSS” or “reduce render-blocking stylesheets”. Loading a CSS file blocks rendering, so performance can be improved with the following steps:
<style>
element in the HTML <head>
.The technique undoubtedly improves performance and could benefit Progressive Web or single-page apps that have consistent interfaces. Gains may be less clear for other sites/apps:
That said, Google will love your site and push it to #1 for every search term. (SEO “experts” can quote me on that. Everyone else will know it’s nonsense.)
Rather than using a single site-wide CSS file, progressive rendering is a technique that defines individual stylesheets for separate components. Each is loaded immediately before a component is referenced in the HTML:
<head>
<!-- core styles used across components -->
<link rel='stylesheet' href='base.css' />
</head>
<body>
<!-- header component -->
<link rel='stylesheet' href='header.css' />
<header>...</header>
<!-- primary content -->
<link rel='stylesheet' href='content.css' />
<main>
<!-- form styling -->
<link rel='stylesheet' href='form.css' />
<form>...</form>
</main>
<!-- header component -->
<link rel='stylesheet' href='footer.css' />
<footer>...</footer>
</body>
Each <link>
still blocks rendering, but for a shorter time, because the file is smaller. The page is usable sooner, because each component renders in sequence; the top of the page can be viewed while remaining content loads.
The technique works in Firefox, Edge and IE. Chrome and Safari “optimize” the experience by loading all CSS files and showing a white screen while that occurs — but that’s no worse than loading each in the <head>
.
Progressive rendering could benefit large sites where individual pages are constructed from a selection of different components.
The most important tip: understand your stylesheets!
Adding vast quantities of CSS from StackOverflow or BootStrap may produce quick results, but it will also bloat your codebase with unused junk. Further customization becomes frustratingly difficult, and the application will never be efficient.
CSS is easy to learn but difficult to master. You can’t avoid the technology if you want to create effective client-side code. A little knowledge of CSS basics can revolutionize your workflow, enhance your apps, and noticeably improve performance.
Have I missed your favorite CSS performance tip?
Originally published by Craig Buckler at https://www.sitepoint.com
#CSS #WebDev