(Posts)

Selectors for Humans, Hashes for Machines

Apr 19, 2024 [ CSS , CSS Modules , Vite ]

One aspect of CSS modules that I truly appreciate is its ability to compress class names into very short hashes. This feature allows me to keep my CSS selectors as long and descriptive as needed, while still compressing them into concise three or four character hashes. It aligns with my rule for CSS: selectors should be written for human readability, but compressed for machine efficiency.

However, setting the hash length too short can lead to hidden hash collision issues which are not easy to detect until you encounter a bug. To address this, I’ve developed a solution in Vite that utilizes the getJSON callback function provided by postcss-modules. By storing all used selector hashes in a map, we can easily check for any duplicates. If a duplicate hash is detected, an error is immediately raised, and the build process is halted. In such cases, the issue can be resolved by simply increasing the hash length and rebuilding the project.

// vite.config.js

let modulesConfig = {
  generateScopedName: "[local]-[hash:base64:4]" // original selectors and four-character hashes in the development environment
};

if (process.env.IS_PROD) {
  const fileSet = {};
  const hashSet = {};
  modulesConfig = {
    getJSON: function (file, json) {
      if (fileSet[file]) return; // skip checking files that have already been processed

      fileSet[file] = true;
      Object.values(json).forEach((i) => {
        if (hashSet[i]) throw Error("CSS MODULES HASH COLLISION ERROR");
        hashSet[i] = true;
      });
    },
    generateScopedName: "[hash:base64:2]" // two-character hashes in the production environment, increase the hash length for needed
  };
}


export default defineConfig({
  css: {
    modules: modulesConfig,
  }
});

I have implemented this code snippet in my react-template, and I am extremely satisfied with the results. It strikes a perfect balance between readability and efficiency, ensuring that my CSS is both human-friendly and optimized for production.

Join the conversation! Share your ideas with me on Twitter. The best stories are yet to come!