Why don’t we talk about minifying CSS anymore?
Tags: webdev, performance, css
Published: 2024-04-24 17:17:21

Minifying your CSS helps improve your website performance. **But as developers, we don’t really talk about minifying CSS anymore. *Why*?**

The TL;DR is that the delivery and optimization of CSS have both been improved with modern tech stacks, making it practically a non-issue. The efficient and performant delivery of CSS is largely solved by HTTP/2 and modern compression algorithms, whilst modern front end frameworks take care of the boring optimization jobs such as code-splitting and minification. Gone are the days when setting up a new front end project involved hours of frustrating manual configuration of Grunt files or Gulp files to compile, minify and post-process CSS files.

## Why you should minify your CSS files

**Minification in CSS is the process of removing all unnecessary whitespace so that files are smaller in size, but can still be understood by the browser.** In short, minifying your CSS helps to optimize your First Contentful Paint (FCP).

FCP is a Core Web Vital that measures the perceived load speed of a web page, and it marks the first point during page load where a user can see *anything* on the screen. A good FCP score is measured as 1.8 seconds or less and reassures the user that *something is loading* so that they don’t bounce away. To ensure a good FCP score, your stylesheets or inline CSS should be *as small as possible* (i.e. minified and compressed) so that they take less time to download and can be processed faster, allowing the browser to paint something on the page sooner. All of the above also contributes to optimizing the [critical render path](https://developer.mozilla.org/en-US/docs/Web/Performance/Critical_rendering_path) (the steps the browser goes through to convert the HTML, CSS, and JavaScript into pixels on the screen). This is especially important for users who have slower internet speeds.

Additionally, CSS is a [render blocking resource,](https://www.notion.so/https-blog-sentry-io-sentrys-open-source-values-94795d61d0dd4695bb8fcbe5ff8dfdc7?pvs=21) meaning the browser won't show any content until it has downloaded the CSS and constructed the CSSOM ([CSS Object Model](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model)) in conjunction with the DOM ([Document Object Model](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)). This makes sense; most modern web pages won’t function correctly without CSS. Plus, if CSS is loaded and applied *after* HTML renders on the page, users will experience a Flash of Unstyled Content (FOUC) before the CSS rules are applied. Styling and arranging content after it has loaded will also likely negatively impact your CLS score (we’ve already got enough CLS to worry about with [fonts](https://blog.sentry.io/web-fonts-and-the-dreaded-cumulative-layout-shift/), [images](https://blog.sentry.io/from-lcp-to-cls-improve-your-core-web-vitals-with-image-loading-best/) and [other unexpected issues](https://blog.sentry.io/your-background-images-might-be-causing-cls/)!). CSS delivered in stylesheets and as inline CSS in the `<head>` tag of your pages are __both__ render blocking. 

## CSS minification tools

There are a number of tools available to minify CSS in development or as part of your build pipeline including, but not limited to, [cssnano](https://cssnano.github.io/cssnano/) and [Vite](https://vitejs.dev/config/build-options#build-cssminify). If you’re using [sass](https://www.npmjs.com/package/sass), it’s as straightforward as adding `--style=compressed` to your `sass` command. The results you achieve may vary depending on the complexity of your CSS, but you may see numbers similar to this (processed with sass):

| Raw (kB) | Minified (kB) | Saving (kB) |
|----------|---------------|-------------|
| 78       | 65            | 13          |

What’s interesting, though, is that these numbers pertain to files store on disk. Files delivered from a server will actually be much smaller. And this is where compression comes in.

## Modern HTTP compression algorithms

HTTP compression is used by web servers and clients (browsers) to improve the transfer speed of resources, and it also helps to save on bandwidth costs and overheads. There are a number of compression algorithms available, but not all are supported by modern browsers. 

Servers may support multiple compression types, and the most common compression types currently used on the web are [gzip](https://en.wikipedia.org/wiki/Gzip) and [Brotli](https://en.wikipedia.org/wiki/Brotli). When making an HTTP request, browsers tell the server which compression methods they are able to decode, at which point the server sends the appropriately compressed file version. Browsers that do *not* support the available compression methods will receive uncompressed data. While sending and receiving uncompressed data probably isn’t common in 2024, it’s worth bearing in mind for people using older or more niche devices that don’t receive automatic software updates. 

You can inspect HTTP request and response headers for network requests in browser dev tools to understand more about what’s described above. Here's an example:

![Dev tools showing the network tab in Chrome, with the content-encoding and content-length response headers highlighted using a yellow rectangle, and the accept-encoding request header also highlighted.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfhhpk48uh3qtvak8shh.png)

Request headers (sent to the server):

- `Accept-Encoding` lists the compression methods the client/browser supports

Response headers (received from the server):

- `Content-Encoding` describes the compression method used to decode the transferred data
- `Content-Length` provides the size of the compressed file in bytes

If you’re using Sentry performance to monitor your applications, you can also inspect the response headers for each network request in the associated [span](https://docs.sentry.io/product/sentry-basics/concepts/tracing/event-detail/#span-view).

![A Sentry trace view showing the content length and decoded content length response headers for the main css file highlighted with a yellow rectangle.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w3gcr35687harl6h8yqx.png)

What’s interesting is that the difference in size between a non-minified compressed file and a minified compressed file isn’t that significant. In the case of the CSS file for my personal website, there was a saving of only 1kB.

|            | Raw (kB) | Minified (kB) | Saving (kB) |
|------------|----------|---------------|-------------|
| On disk    | 78       | 65            | 13          |
| Compressed | 11       | 10            | 1           |

Whilst 1kB may not seem much, the savings will most definitely benefit both you and your users on a larger scale. As of March 2024 the global median internet speed on mobile is 52.98Mbps. While that equates to being able to download 6625kB per second ([calculated here](https://www.gbmb.org/mbps-to-kbs) and rounded up to 53Mbps), if you can shave off just 1kB from a total of 100 resources on each page load, you could be making a 15ms improvement on your page speed for those users:

```text
How long does it take to transfer 100kB at 6.625 kB/ms?

6625kB/s transferred
6625 ÷ 1000 = 6.625 kB/ms transferred

100 kB ÷ 6.625 kB/ms = 15ms*
```

`*`*This is an extremely basic calculation not taking into consideration TCP slow starts or any other network effects that affect data transfer, but it serves as an example nonetheless.*

Simultaneously you’ll be saving bandwidth costs as a developer or organisation. While 1kB isn’t much of a saving for a single user, __for 1000 users this equates to transferring 1MB less data__.

## HTTP/2 multiplexing

Another advancement that may mean CSS minification is not-that-important-anymore is HTTP/2. Released in 2015 and adopted over time, HTTP/2 allows the server to use a **single connection to deliver multiple requests and responses in parallel** (multiplexing). To take advantage of this, front end bundling tools and frameworks provide the ability to “code split” or “chunk” larger CSS files and JavaScript packages in order to ship smaller files simultaneously, whilst the underlying technology pieces those files together in the client for the web page to function correctly.

If a non-minified compressed CSS file is only 10kB, and then split into smaller chunks of, say, 2kB, then minification becomes kind of a non-issue. Additionally, many modern front end frameworks take care of this for us anyway.

## CSS minification and code-splitting in front end frameworks

If you’re using a modern front end framework, you’ll usually get CSS minification and/or code splitting out of the box. Frameworks provide their own opinionated, yet sensible default configurations for CSS post-processing, and you can choose to optimize those options further if you wish. Additionally, by serving your websites and applications from modern hosting platforms and CDNs, you’ll benefit from HTTP/2, caching and compression without even having to think about it. 

Here’s how just some of the modern JavaScript-based frameworks handle CSS today.

### Astro

[Astro automatically minifies and combines your CSS into chunks](https://docs.astro.build/en/guides/styling/#bundle-control), and further optimizes CSS shared between pages for reuse. Another optimization from Astro is that it “inlines” any CSS chunks that are smaller than 4kB in size (i.e. puts the CSS in a `<style>` tag in the `<head>` of the page) to prevent too many network requests for small stylesheets. This provides a “balance between the number of additional requests and the volume of CSS that can be cached between pages.”

### Next.js

Next.js comes with a number of [pre-configured optimizations](https://nextjs.org/docs/app/building-your-application/styling) for using CSS Modules, Tailwind, Sass and and CSS-in-JS. For example, when using CSS Modules, many smaller files will be automatically bundled into fewer minified code-split CSS files to reduce the number of network requests.   

### Nuxt

Nuxt comes with `postcss` and a number of [pre-configured CSS minification and optimization plugins](https://nuxt.com/docs/getting-started/styling#using-postcss) including `postcss-import`, `postcss-url`, `autoprefixer` and `cssnano`. 

### SvelteKit

SvelteKit uses [Vite](https://vitejs.dev/config/build-options.html#build-csscodesplit) under the hood to build and bundle projects. This comes with configurable CSS code-splitting and minification.

Where Astro inlines all CSS files smaller than 4kB in size, SvelteKit comes with an [inlineStyleThreshold configuration](https://kit.svelte.dev/docs/configuration#inlinestylethreshold) which allows you to explicitly specify the maximum length of a CSS file in UTF-16 code units to be inlined. All CSS needed for the page and smaller than this value are merged and inlined in a `<style>` block.

### Angular

Angular’s build process comes with an `--optimization` flag which enables CSS minification and inlining of critical CSS. To save even more bytes, you can also choose to use the `removeSpecialComments` flag during a build to remove comments in CSS and other extraneous characters.

## Conclusion

With the luxury of modern web hosting platforms and CDNs caching and delivering files fast to users around the world, powerful compression algorithms and the wide adoption of HTTP/2, I guess we don’t really need to *think* about CSS minification anymore. It’s generally taken care of for us in modern front end tooling — albeit in opinionated ways. That being said, it is incredibly important for us, as front end developers, to understand how CSS is being delivered to the browser in our applications in order to monitor the impact it has on the performance of our apps in production. 

Yes, these modern frameworks are doing a lot for us. But can we do *more*?