Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add loader for .module.css to load CSS modules #6055

Closed
manuelbieh opened this issue Mar 13, 2019 · 19 comments
Closed

Add loader for .module.css to load CSS modules #6055

manuelbieh opened this issue Mar 13, 2019 · 19 comments

Comments

@manuelbieh
Copy link

manuelbieh commented Mar 13, 2019

Is your feature request related to a problem? Please describe.

I'd love to use CSS Modules together with Storybook but the default config won't let me do that out of the box.

Describe the solution you'd like

I'd like to have another loader added to the storybookBase webpack config which checks for /\.module.css/ and uses css-loader with the modules option set to true. At the same time /\.module.css/ must be added as exclude pattern to the default css loader config.

Describe alternatives you've considered

I have added a really super ugly (and error prone) hack to filter out the css from Storybook's default webpack config and replace it with my own css loading mechanism:

https://github.com/wiremore/grid/blob/master/config/webpack.config.js/storybook.js#L7-L9

Are you able to assist bring the feature to reality?

I did, kind of. See my hack above. Feel free to adopt. If you point me to the webpack config that is used to generate the stories I could also implement it myself. It's probably not a breaking change.

Additional context

Both Gatsby and Create React App use .module.css to support CSS Modules without any further configuration, hacks or workarounds:

@mbifulco
Copy link

Hey @manuelbieh - one particularly simple way to get css modules working in your storybook is to add react scripts to the project. Storybook should then pick up on your create-react-app config:

image

Doing so enables .module.css files without any further configuration.

With that being said, adding react-scripts effectively adds dependency weight under the hood, even if you're not using it for anything else. It's not perfect, but it does just work to enable CSS modules.

Caveat: I've found this to be almost perfect for my needs building a gatsby/storybook project for thegymnasium.com. The unfortunate problem I'm having at the moment is that Gatsby and Storybook process modules differently - you can see my dilemma here.

@stale
Copy link

stale bot commented Apr 9, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Apr 9, 2019
@stale stale bot removed the inactive label Apr 9, 2019
@shilman shilman modified the milestones: 5.0.x, 5.1.0 Apr 9, 2019
@shilman
Copy link
Member

shilman commented Apr 9, 2019

@manuelbieh This is a great feature. However, I don't want to add any more dependencies to Storybook (in fact, I'd like to start removing them!). Last year @igor-dv developed a feature called "presets" which is perfect for this. It's a solution that can modify your webpack/babel configs and more by adding a single line to presets.js. It's used internally by all of our different "apps" (React, Vue, Angular, etc.) but we're still documenting it for public use. I'd like to circle back with you as soon as its documented and see if you might be able to implement "preset-css-modules" as one of the first Storybook presets.

@stale
Copy link

stale bot commented Apr 30, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Apr 30, 2019
@stale
Copy link

stale bot commented May 30, 2019

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

@stale stale bot closed this as completed May 30, 2019
@shilman
Copy link
Member

shilman commented May 30, 2019

@manuelbieh FYI we merged the presets doc and released our first preset.

#5333
https://www.npmjs.com/package/@storybook/preset-typescript

Any interest in doing this for CSS modules? If so, I'd def feature it as part of the push towards presets.

@shilman shilman reopened this May 30, 2019
@stale stale bot removed the inactive label May 30, 2019
@shilman shilman modified the milestones: 5.1.0, 5.1.x Jun 5, 2019
@stale
Copy link

stale bot commented Jun 26, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Jun 26, 2019
@stale
Copy link

stale bot commented Jul 26, 2019

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

@stale stale bot closed this as completed Jul 26, 2019
@shilman
Copy link
Member

shilman commented Jul 27, 2019

@manuelbieh Any interest in pairing on a CSS modules preset? I'm familiar with the presets side and it sounds like you're familiar with the CSS modules side. I bet we could get something going in 30m if you're interested.

@manuelbieh
Copy link
Author

@shilman Hey, sure. Just let me know how I can help. I just googled the concept of presets in Storybook, looks awesome. I guess the preset will probably look similar to what I've already done here, right?

https://github.com/manuel-bieh/ui/blob/master/config/webpack.config.js/storybook.js

@shilman
Copy link
Member

shilman commented Aug 7, 2019

@manuelbieh Yeah, we can probably just copy and paste from that. Want to hop on our discord and pair on it? Or if you can explain to me how to use it / test it, I can probably just whip something out.

@astorije
Copy link
Contributor

I'm currently setting CSS Modules in a shared component library written in TypeScript (apps consuming this library already use CSS Modules, but the shared library doesn't) and I ended up with this:

  config.module.rules.find(
    rule => rule.test.toString() === '/\\.css$/',
  ).exclude = /\.module\.css$/;

  config.module.rules.push({
    test: /\.module\.css$/,
    use: [
      'style-loader',
      'css-modules-typescript-loader',
      {
        loader: 'css-loader',
        options: {
          modules: true,
        },
      },
    ],
  });

It's a work in progress for a few reasons which I assume are on our end (e.g. the build fails the first time because *.d.ts.css files do not initially exist as we are not committing them in, but they exist upon re-builds), but a preset and/or improvement to the Storybook Wepback config would be very welcome. What would help:

  • Preset or not, the base config could exclude *.module.css files, that way the first block is not necessary (which would be necessary in the preset as well)
  • The preset would remove the dependency on style-loader, css-loader, and css-modules-typescript-loader on our end, which is always welcome.

Unfortunately, our Webpack config does something additional that the preset would not do (see below) so depending on the capabilities to extend / hook into / compose presets, we would or would not be able to use it. If not, then excluding *.module.css files from the base config would already help!

Thanks for your hard work! 👏


If anyone is interested about what we do in the Webpack config:

config.plugins.push(
  new DefinePlugin({
    'process.env.GIT_REVISION': JSON.stringify(
      execSync('git describe')
        .toString()
        .trim(),
    ),
  }),
);

This exposes a human-readable git revision to the build, which we then use for a couple things, such as setting a link to the component library repo using the theme brandUrl:

brandUrl: `https://github.com/my-org/my-repo/tree/${process.env.GIT_REVISION}`,

That offers a quick way to know the current version one is looking at, among other things.

@morena
Copy link

morena commented Sep 26, 2019

@astorije thanks for the solution, we don't have Typescript, but SASS and css modules and I'm using the classes from css module to add them to the components, how would I adapt your code to our setup? so far I have

config.module.rules.push({
    test: /\.scss$/,
    use: ['style-loader', 'css-loader', 'sass-loader'],
    include: path.resolve(__dirname, '../'),
    exclude: /\.module\.scss$/
  });
config.module.rules.push({
    test: /\.module\.scss$/,
    use: [
      'style-loader',
      {
        loader: 'css-loader',
        options: {
          modules: true,
          importLoaders: 1,
          localIdentName: '[path]__[name]___[local]'
        }
      },
      'sass-loader'
    ]
  });

I have SASS working, SOME css modules working (I use them as SCSS files) but if there's a SCSS module overriding a default material-ui rule (I'm using gatsby and material-ui) then the module.scss file gets overridden by the material-ui rule :(

@marcobiedermann
Copy link
Contributor

I pretty much used the same technique, just in an immutable way:

module.exports = async ({ config }) => {
  const rules = config.module.rules.map(rule => {
    if (rule.test.toString() !== '/\\.css$/') {
      return rule;
    }

    const use = rule.use.map(u => {
      const { loader } = u;

      if (!loader || !loader.includes('/css-loader/')) {
        return u;
      }

      const options = {
        ...u.options,
        modules: true
      };

      return {
        ...u,
        options
      };
    })

    return {
      ...rule,
      use
    };
  })

  return {
    ...config,
    module: {
      ...config.module,
      rules,
    }
  };
};

Loop over rules, find rule applied to .css files and update the option to add modules: true to css-loader

@pchodak
Copy link

pchodak commented Dec 12, 2019

@astorije
damn, you saved my 2 hours, thx

@freesh
Copy link

freesh commented Mar 31, 2020

I solved it like that in my ./storybook/main.js

module.exports = {
  stories: ['../stories/**/*.stories.js'],
  addons: ['@storybook/addon-actions', '@storybook/addon-links'],
  webpackFinal: async (config, { configType }) => {

    // get index of css rule
    const ruleCssIndex = config.module.rules.findIndex(rule => rule.test.toString() === '/\\.css$/');

    // map over the 'use' array of the css rule and set the 'module' option to true
    config.module.rules[ruleCssIndex].use.map(item => {
      if (item.loader && item.loader.includes('/css-loader/')) {
        item.options.modules = {
          mode: 'local',
          localIdentName: configType === 'PRODUCTION' ? '[local]--[hash:base64:5]' : '[name]__[local]--[hash:base64:5]',
        };
      }
      return item;
    })

    // Return the altered config
    return config;
  }
};

@ekilah
Copy link

ekilah commented May 2, 2020

@freesh thanks, though i did something different it helped. in my case I have been struggling for ages and I just removed all the default storybook webpack CSS stuff and used (copy/paste mostly) what Create React App gave me when I ejected, manually:

module.exports = {
  stories: ['../storybook/**/*.stories.tsx'],
  addons: ['@storybook/addon-actions', '@storybook/addon-links'],
  webpackFinal: async config => {
     
     config.module.rules = config.module.rules.filter(rule => rule.test.toString() !== '/\\.css$/')

      config.module.rules.push({oneOf: [
        { ... }
      ]})

also note that I know very little about webpack but running yarn storybook --debug-webpack was much better than nothing!

@roblevintennis
Copy link

roblevintennis commented Aug 30, 2020

@freesh solution was still required for me to use Storybook outside a create react app. Thanks. I had style-loader and css-loader with typical css:modules true but it seems that the configuration is ignored in this sort of environment. I was literally only creating components in this project so the humungous create react app seemed like overkill. I don't think this should take this kind of webpack hijacking personally.

It's interesting too that it appears like https://github.com/storybookjs/presets/tree/master/packages/preset-scss might provide css module too. Seems too bad that it's scss specific. I'm using css-loader modules: true without scss to keep my CSS lightweight until/unless my project absolutely needs a preprocessor (in which case postcss is most likely next thing I'd add).

Lastly, I think it's also a Storybook noob thing (at least for me) since I just also encountered:
https://storybook.js.org/docs/react/configure/webpack#extending-storybooks-webpack-config

which seems to perfectly tell you how to override this manually which brings me around full circle that this is not really a bug or missing feature ¯_(ツ)_/¯ but:

https://storybook.js.org/docs/react/get-started/install if you see the:

sb init is not made for empty projects

It would maybe be nice to point out that Webpack can be overriden by looking at #extending-storybooks-webpack-config and a similar link to rollup if it's overridable. I think I/we are finding ourselves stubbornly trying anyway and then finding this issue :)

@iDenisM
Copy link

iDenisM commented May 17, 2021

Hi there
I managed to make it work so that I can load .css and .module.css files with storybook 6 and use the postcss-loader on top of it. To do it you have to remove the css and/or the .module.css rules from the base config of the storybook webpack and add yours. Here is my example.

webpackFinal: async (config, { configType }) => {
    // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
    // You can change the configuration based on that.
    // 'PRODUCTION' is used when building the static version of storybook.

    const indexOfCssRules = config.module.rules.findIndex(
      rule => rule.test.toString() === '/\\.css$/'
    );

    const indexOfModuleCssRules = config.module.rules.findIndex(
      rule => rule.test.toString() === '/\\.module\\.css$/'
    );

    if (indexOfCssRules != -1) {
      config.module.rules.splice(indexOfCssRules, 1);
    }

    if (indexOfModuleCssRules != -1) {
      config.module.rules.splice(indexOfModuleCssRules, 1);
    }

    config.module.rules.push({
      test: /\.css$/,
      exclude: /\.module\.css$/,
      use: getStyleLoaders({
        importLoaders: 1,
        sourceMap: true,
      }),
      sideEffects: true,
      include: path.resolve(__dirname, '../'),
    });
    config.module.rules.push({
      test: /\.module\.css$/,
      use: getStyleLoaders({
        importLoaders: 1,
        sourceMap: true,
        modules: {
          localIdentName,
          exportGlobals: true,
        },
      }),
      include: path.resolve(__dirname, '../'),
    });

    // Return the altered config
    console.log(JSON.stringify(config.module.rules));
    return config;
  },

getStyleLoaders is a function I got from create-react-app webpack.config.js

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

No branches or pull requests