Skip to content

Instantly share code, notes, and snippets.

@MQuy
Last active March 7, 2021 02:43
Show Gist options
  • Save MQuy/01ad020c488d6972dfde26f142251fd2 to your computer and use it in GitHub Desktop.
Save MQuy/01ad020c488d6972dfde26f142251fd2 to your computer and use it in GitHub Desktop.

How to reduce stylesheet's size by 55% and even more?

Before diving into Long answer πŸ‘©β€πŸ«, let see why stylesheet's size is one of the most important factors in term of web experience.

In general, a stylesheet is just bunch of rules/selectors which can be defined in:

  • External files.
  • Style tags.
  • Inline style attributes.

✍️ There are a lot of discussions for pros and cons of each approach and it's beyond the scope of this article, but if you are interested in you can read here.

In order for a browser to display a webpage, a browser has to construct DOM (our HTML) and CSSOM (our stylesheets) trees like the image below. It means that constructing CSSOM is a render blocking, therefore, the bigger file's size and the more CSS rules we have, the slower our website is πŸ€·β€β™€οΈ .

browser rendering

So, let's start our thinning-the-stylesheet journey. In my opinion, we can classify into two categories:

  • "Minifying" a stylesheet.
  • Removing unused CSS selectors.

✍️ I quote minifying because I cannot find a better word to describe what it does in the following section.

Minifying

What is minifying? In this section, it does two different things:

  • Perform transformations (aka CSS Minifier).
  • Cutting css's class name.

CSS Minifier

CSS Minifiers basically does three things:

  • Cleaning (removing duplicated/empty rules and comments).
  • Compression (shorter form for color, font, background).
  • Restructuring (merge of declarations, rulesets and so on).

The image below is the benchmark of CSS minifiers

css minifier

πŸ“• Worst - πŸ““ Average - πŸ“— Best

As image above shows, there are a bunch of css minifiers, I encourage you use cssnano or csso, top two popular css minifiers in term of features and ecosystem. You can use them in your module bundler via:

Cutting css's classname

If you sneak into Gmail HTML page (the same for other Google platforms: G+, Driver ...), you will see something like this

Gmail

It looks ugly πŸ’©, right? because it doesn't have any meaningful class names, we were taught that we should use meaningful names when programming to improve readability and maintainability, it goes against that. Why Google does that? Because shorter class name means:

  • smaller size -> fewer bytes to delivery -> our website is faster πŸŽ‰.
  • faster when querying rule πŸŽ‰.

The problem we need to solve in order to achieve what we want:

  • Keeping meaningful class name on development for ease of debugging -> πŸ”ͺ we just follow best practices (BEM, SMACSS, OOCSS ...) or your own rule as long as it is consistency in your team.
  • Outputing ugly/shortest class name on production for lightweight delivery and fast loading <- here this tough one πŸ‹οΈβ€β™‚οΈ.

Outputing ugly/shortest class name: We need a tool which transforms class names, also keeps them synchronous other places when compiling our source code. I am currently using Webpack, luckily, one of webpack loaders named css-loader has built-in function getLocalIdent to support us to overcome this obstacle.

If you are familiar with webpack config, this is the simple version of increment name 0, 1, 2 ... πŸ’£

webpack

That is the idea, you can write a better version than me, right? 😜

Removing unused css selectors

In order to remove unused selectors, we need to know which parts are used and which are not, therefore the first step is detecting unused selectors. How? There are two traditional ways to detect unused things on frontend code:

  • Runtime: run your webpage, scan through HTML, CSS and js files to check which selectors are used in HTML/javascript. From used selectors, you/tools can exclude them and get unused selectors.
  • Build time: via module bundler we can import CSS files and specify which selectors. Based on that information from module bundler, we can detect unused selectors (it sounds easy right? πŸ€ͺ)

Runtime

There are three popular ways to check unused CSS selectors:

  • Command line: You run command line with the location of CSS/HTML/js files as input and output is used/unused CSS. There are a lot of tools but purgecss, uncss and purifycss are three top popular ones.
  • Website: Quite similar to Command line but on a website. You enter your webpack's URL and it does its job πŸ€Ήβ€β™‚οΈ.
    For example: https://www.jitbit.com/unusedcss/, https://unused-css.com/, https://uncss-online.com/ ...
  • Chrome Code Coverage: The image below shows code coverage not only for CSS files but also for js files

coverage

How to open Code Coverage:

  • Step 1: Open Chrome Dev Tools
  • Step 2: Press Cmd+Shift+P (Mac) or Ctrl+Shift+P (Windows, Linux), type Coverage

coverage

  • Step 3: Click record button

coverage

Build time

As I mention above. It sounds easy, right? Yes, it will be super easy if your module bundler supports named exports, otherwise, it can be challenging. I am currently using Webpack and unfortunately, webpack and its eco system (css-loader) doesn't support named exports because moving from commonjs to es6 named exports is a breaking change, you can see why via this thread.

In order to climb a steep hill, there are two steps we need to deal with:

πŸ“Œ I made the demo to demonstrate how to use two plugins above, here is source code.

When running the demo, an output looks like the image below

unused

Conclusion

After applying those solutions above, our CSS bundle size at Bokio is reduced from 286 KB to 130 KB even we haven't applied removing unused selectors via build time yet. I will update our final result when we finish optimizing 🀞.

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment