Eben Gilkenson

Setting up CSS Modules with PostCSS

I’m using PostCSS with the postcss-preset-env plugin to manage which features I want to polyfill. Currently, that’s just nesting, custom properties and custom media queries.

I ended up hitting a snag, though. One of the benefits of using PostCSS with custom properties is that it will prepend an additional declaration with a static value as a fallback for older browsers.

So a rule like this…

margin-bottom: var(--spacing_block-default);

…gets processed into something like this:

margin-bottom: 1.5em;
margin-bottom: var(--spacing_block-default);

Older browsers will ignore the line with the custom property and fall back to the static value, while modern browsers will have access to the live values, which can be used for all kinds of cool stuff. For this site, I will be using them for a dark mode theme.

When used in CSS Modules, however, PostCSS was not providing a fallback value. Even when importing the variables stylesheet into each module, they were not being processed by PostCSS.

What I was missing was the importFrom property in the postcss-preset-env plugin options.

/* variables.css */

:root {
  --spacing_inline-default: 1.2rem;
  --spacing_block-default: 1.5em;

  --color_text-main: hsla(0, 0%, 0%, 0.8);
  --color_body-background: white;
}
/* gatsby-config.js */

{
  resolve: `gatsby-plugin-postcss`,
  options: {
    postCssPlugins: [
      require(`postcss-preset-env`)({
        stage: 3,
        features: {
          'nesting-rules': true,
          'custom-media-queries': true,
          'custom-properties': {preserve: true},
        },
        importFrom: [`src/css/variables.css`]
      })
    ],
  },
},

This added line will import my CSS variables and write the additional fallback line in CSS Modules.

← Learning Gatsby