Gatsby: Css modules classnames might use shorter, but unique names

Created on 6 Oct 2017  路  22Comments  路  Source: gatsbyjs/gatsby

Currently css modules are generated by using pattern: [path]---[name]---[local]---[hash:base64:5]

So example css module classname looks like:

.src-components-contact----contactBlock-module---block---JFm8l
or
.src-components-generic-image----Image-module---imageWrap---2Xy0r

All these class names are directly in html & css files and might dramatically increase bundle size.

On previous projects I've used pattern [name]_[local]--[hash:base64:5] which excludes path to file, which in most cases is not needed. All classes are still unique because of hash.

So long css names like this (before): .src-components-generic-image----Image-module---imageWrap---2Xy0r
Will look simply like this (after): Image-module_imageWrap--2Xy0r

This might be simple change by modifying pattern in https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-1-config-css-modules/src/index.js . I might try to update code (PR) if you are fine with that.

// Might be also nice to enable configuration for developers, so that it's possible to configure to match custom needs.

Most helpful comment

Maybe a simpler solution would be to make the LOCAL_IDENT_NAME a configurable option in gatsby-config.js using the same webpack replacement syntax

All 22 comments

馃憤 for shorter class names. However, I typically name my components like this:

/ComponentName
  - index.js
  - index.module.css

So your proposed pattern [name]_[local]--[hash:base64:5] would make all my classes start with index_, which would give shorter classnames, but not be too useful for development. Something like [folder]-[local]-[hash:base64:5] would be better in my case.

So I guess I'd like to add a 馃憤 for this to be configurable.

Hey folks, so the reason we use the path and no short hash here is that it's actually better for gzipping, giving an overall smaller css file when minified and served. There was an investigation on it in webpack and why we went with it, as unintuitive as that might seem!

That makes sense, thanks for the quick response. I trust Gatsby to do whatever's best in production mode :)

During development I think it's useful to be able to change the default pattern, so I've made a quick plugin to allow that: https://www.npmjs.com/package/gatsby-plugin-module-local-ident-name

I trust Gatsby to do whatever's best in production mode :)

馃槃 that is maybe giving us to much credit, its good to get questions about this stuff. There are always plenty of places we miss clear optimizations

馃ぃ way too much credit

Hopefully though we're 80% of the way there but there's plenty more optimizations to be made!

Can we really get better gzip when using longer names.. hmm.. what kind of magic it is 馃槂but I believe you that probably we can get similar size with and without paths, but still there are more things to consider..

Downsides of current approach:

  • deeper folder structure might generate incredible long class names
  • it's easier to debug & work with shorter classNames (eg. in inspector)
  • for integration tests, it's better to not have path in names (code can be refactored - move files without breaking integration tests)

Why to keep current approach:

  • better gzip (I don't really believe it's such difference - also might depend on use case, because if we create 4 folders, each with single css files; gzip will not help with this and we would still need to transfer all 4 folder names in css classes)
  • we have full paths in names (cannot find useful use case)

Maybe a simpler solution would be to make the LOCAL_IDENT_NAME a configurable option in gatsby-config.js using the same webpack replacement syntax

I've created PR with node environment variable (CSS_MODULES_LOCAL_IDENT_NAME) https://github.com/gatsbyjs/gatsby/pull/3411

It's working, tested by copying file to project (not sure why it doesn't work with gatsby-dev - probably because config is dependency of plugin).

But please check if this might be solution you are comfortable with. If yes, then would be good to add it somewhere to documentation - maybe to custom webpack config section.

I've made a plugin to do this, see:

https://github.com/m-allanson/gatsby-plugin-module-local-ident-name
https://www.npmjs.com/package/gatsby-plugin-module-local-ident-name

It only works for gatsby develop at the moment as that suits my needs. I'm happy to add anyone as a collaborator if they'd like to expand on it.

@m-allanson looks interesting 馃憤 but to be complete I think would be nice to have option to configure also production build... shouldn't it be issue? What do you think..

@jurosh yeah I agree, a good idea and should be pretty easy to add.

I know about this article that explains how to aggressively minify CSS Modules class names, but I don't know how to implement the solution described in Gatsby 馃槩 https://medium.freecodecamp.org/reducing-css-bundle-size-70-by-cutting-the-class-names-and-using-scope-isolation-625440de600b

I'm having a hard time believing that longer class names can produce a smaller compressed bundle than if these class names were minified to begin with... The guy in the article says that:

The first argument against such minification is that the compression algorithms will do it for you. GO2CINEMA [the website being optimized] CSS bundle compressed using the Brotli algorithm saves a mere 1 KB compared to the original bundle with the long class names. [and minifying his class names reduced the bundle size from 140Kb to 47Kb, which is a more significant than the 1Kb reduction achieved by compressing long class names with Brotli]

@jquense, are you sure longer class names really produce a smaller bundle? 馃槙
And does anyone know how to implement the solution described in the article with Gatsby? Thanks! 馃檪

Then no need to bother?

Yeah, I wouldn't mind a PR adding support for what he did as long as it doesn't increasing processing time but I think there's a long list of other more important improvements we could make to Gatsby.

That being said, open source is all about scratching your own itch so if this is what floats your boat, by all means do it :-)

It wouldn't hurt anyone to save 500 bytes!

But didn't you say minifying the class names will make the bundle bigger?! I'm a little confused... 馃槙

No it'll make the bundle smaller but it's a very small difference e.g. ~1%

Happy to take a PR adding support for minified class names if the it also demonstrates that it won't increase build times any but closing this issue now.

I just looked into it and also think this could be an area of improvement. Shorter names for development like provided with https://github.com/m-allanson/gatsby-plugin-module-local-ident-name and thoughtfully minified class names as described here: https://medium.freecodecamp.org/reducing-css-bundle-size-70-by-cutting-the-class-names-and-using-scope-isolation-625440de600b.

Are there any updates on this? If not, maybe I can find the time to make an PR to Gatsby.

@KyleAMathews It shouldn't increase build time for development but if it lowers the size of production bundles, a slower gatsby build would be acceptable, I think. 馃槍

gatsby-node.js

const replacePath = (str) => str.replace('[path]---[name]---[local]---[hash:base64:5]', '[local]-[hash:base64:5]');
const modules = ['sassModules', 'cssModules'];

exports.modifyWebpackConfig = ({ config, stage }) => {
  switch (stage) {
    case 'develop':
      modules.map((item) => {
        config.loader(item, (current) => {
          current.loaders = current.loaders.map((item) => {
            return replacePath(item);
          });
          return current;
        });
      });
      break;
    case 'build-css':
    case 'build-html':
    case 'build-javascript':
      modules.map((item) => {
        config.loader(item, (current) => {
          current.loader = replacePath(current.loader);
          return current;
        });
      });
      break;
  }

  return config;
};
// in gatsby-config.js
plugins: [
  {
    resolve: `gatsby-plugin-sass`,
    options: {
      cssLoaderOptions: {
        localIdentName: '[local]--[hash:base64:5]'
      },
    },
  },
]
// in gatsby-config.js
plugins: [
  {
    resolve: `gatsby-plugin-sass`,
    options: {
      cssLoaderOptions: {
        localIdentName: '[local]--[hash:base64:5]'
      },
    },
  },
]

I tried this out, but nothing changed. Tried debugging it until I eventually realized I needed to upgrade my gatsby-plugin-sass. So, this is a heads up to everyone else: You might need to upgrade your gatsby-plugin-sass!
Applying cssLoaderOptions is only possible since https://github.com/gatsbyjs/gatsby/pull/9462

In case somebody still needing a solution just install this plugin: https://github.com/stldo/gatsby-plugin-minify-classnames

It Is working with Gatsby v2

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Oppenheimer1 picture Oppenheimer1  路  3Comments

andykais picture andykais  路  3Comments

benstr picture benstr  路  3Comments

mikestopcontinues picture mikestopcontinues  路  3Comments

totsteps picture totsteps  路  3Comments