Sass with CSS Modules & Webpack

17,277

Soon after posting, I figured out the solution. The problem, which I thought was quite confusing, was in my Webpack config. Originally my loader looked like:

loader: 'style!css?sourceMap&localIdentName=[local]___[hash:base64:5]!resolve-url!sass?outputStyle=expanded&sourceMap

which enabled to me to 1) require my Sass and 2) wrap my styles in :local.

However, the css loader was missing the modules option so that it looked like:

loader: 'style!css?modules&sourceMap&localIdentName=[local]___[hash:base64:5]!resolve-url!sass?outputStyle=expanded&sourceMap

Now I can import my styles and I don't have to wrap them in :local (although I presume I still can if I want to).

What I found most interesting about all this is that without the modules option, one can still use CSS Modules-esque features, although somewhat limiting.

EDIT:

Something I noticed, a future warning to whomever looks at this answer, is if you're using the eslint-plugin-import to lint the imports in your javascript code, it will throw an error upon importing styles like:

import styles from './MyStyles.scss';

because of the way CSS Modules exports the resulting styles object. That does mean you'll be required to do require('./MyStyles.scss') to bypass any warnings or errors.

Share:
17,277

Related videos on Youtube

barndog
Author by

barndog

I code sometimes. Formerly @apple.

Updated on June 15, 2020

Comments

  • barndog
    barndog almost 4 years

    I've been building a project for a while using Webpack, Sass, and CSS modules. Normally in my .scss files, I define a class like:

    :local(.button) {
        color: white;
    }
    

    and in my React components, in the render method, I require the styles:

    render = () => {
        const styles = require('./MyStyles.scss');
        <div className={ styles.button } />
    }
    

    and all is good with the world. Everything works as expected.

    Now today I was reading through the CSS Modules page and noticed that none of the selectors were encompassed by :local() like mine and furthermore that they were importing the styles like:

    import styles from './MyStyles.scss';
    

    And I thought "Wow that looks much nicer, it's easier to see where it's imported, ect. And I'd love not to use :local() and just have things local by default." So I tried that and immediately ran into several problems.

    1) `import styles from './MyStyles.scss';

    Because I'm using ESLint on my React files, I immediately get an error thrown that MyStyles.scss doesn't have a default export which would normally make sense but the CSS Modules page stated:

    When importing the CSS Module from a JS Module, it exports an object with all mappings from local names to global names.

    so I naturally expected the default export of the stylesheet to be the object they're referring too.

    2) I tried import { button } from './MyStyles.scss';

    This passes linting but button logs as undefined.

    3) If I revert to the require method of importing my styles, anything not specified with :local is undefined.

    For reference, my webpack loader (I'm also including Node-Neat and Node-Bourbon, two awesome libraries):

    { test: /.(scss|css)$/, loader: 'style!css?sourceMap&localIdentName=[local]___[hash:base64:5]!resolve-url!sass?outputStyle=expanded&sourceMap&includePaths[]=' + encodeURIComponent(require('node-bourbon').includePaths) +
    '&includePaths[]=' + encodeURIComponent(require('node-neat').includePaths[1]) + '&includePaths[]=' + path.resolve(__dirname, '..', 'src/client/') }
    

    My questions, following all of this, are:

    1) When using CSS Modules with Sass, am I confined to using either :local or :global?

    2) Since I'm using webpack, does that also mean I can only require my styles?

    • zedd45
      zedd45 about 8 years
      I would escape the wildcard (.) character in your loader's test property, like so: test: /\.(scss|css)$/
  • Luke Willis
    Luke Willis over 6 years
    When using eslint-plugin-import, you can ignore your scss files specifically. see: npmjs.com/package/eslint-plugin-import#importignore