css-loader
error on build.
After migrating to 9.2
and testing the CSS modules features, the below configuration started erroring.
colors.css (containing only values)
@value customGreen: #3ba150;
Component.module.css
@value customGreen from "./colors.css";
.custom {
color: customGreen;
}
I am having:
./Component.module.css (./node_modules/css-loader/dist/cjs.js??ref--5-oneOf-2-1!./node_modules/postcss-loader/src??__nextjs_postcss!./Component.module.css)
CssSyntaxError
Selector ":import("./colors.css")" is not pure (pure selectors must contain at least one local class or id)
Well it did seem to work when I had
next.config.js
module.exports = withPlugins(
[
[
withCSS,
{
cssLoaderOptions: {
localIdentName: '[name]_[local]_[hash:6]',
},
cssModules: true,
},
],
],
nextConfig
);
Not sure if there's some tweaking to do some where, not a champ on CSS modules stuff.
I've tried to understand this test but no luck.
I am trying out the newly released v9.2 and is facing the exact same issue. I am neither able to make the global CSS work nor the local using the module system.
Global CSS doesn't apply but everything compiles correctly. The module-based system throws the "Selector "nav" is not pure (pure selectors must contain at least one local class or id)" error.
I'm following the steps as mentioned in the blog article
I reproduced the problem you're facing by using css modules @value
.
=> It doesn't seem to be supported yet by nextjs :(
For it to be supported, it would need to use the postcss-modules-values
package in the css build step.
Looking at next package.json, I can't see the dependency.
Warning: Please remove the postcss-modules-values plugin from your PostCSS configuration. This plugin is automatically configured by Next.js.
@cedric-marcone It seems to be included
@Pegase745 : You're right, I added a postcss config file with the module and I'm having the same warning.
=> Currently css modules in next 9.2 stop working when using @value
.
=> It is not caused by the absence of postcss-modules-values
I've also found the same issue after migrating my project to the latest Next Canary (9.2.1-canary.11) using SCSS modules.
1:0) Selector "nav" is not pure (pure selectors must contain at least one local class or id)
> 1 | nav {
| ^
2 | width: min-content;
3 | height: min-content;
I'm not sure what the error means but would be happy to modify my SCSS to solve the issue
Update
I solved my issues by moving away from implicit HTML calls (ex: nav
and <nav>
) for specific elements, instead calling my components with classes (ex .nav
and following up with <nav className={s.nav}>
Same here, I can push up a demo project that reproduces the error if that's helpful.
@agconti that would be great if it doesn't take too much time!
@Pegase745 here's a repo which minimally reproduces the error: https://github.com/agconti/next-js-css-modules-unable-to-use--global-with-css-modules
Steps to reproduce:
git clone https://github.com/agconti/next-js-css-modules-unable-to-use--global-with-css-modules
npm i
error ] ./colors.module.css (./node_modules/css-loader/dist/cjs.js??ref--5-oneOf-2-1!./node_modules/postcss-loader/src??__nextjs_postcss!./colors.module.css)
CssSyntax error: Selector ":global" is not pure (pure selectors must contain at least one local class or id) (1:1)
> 1 | :global {
| ^
2 | body: {
3 | background-color: red;
ModuleBuildError: Module build failed (from ./node_modules/css-loader/dist/cjs.js):
CssSyntaxError
Any update on this?
I had no time to dig deep into the next.js css-modules implementation, but wanted to share this:
.container {
--color-fancy: #00deee;
--color-primary: #3b9aa1;
}
Then simply apply them in your descendants module classes:
.foo { color: var(--color-fancy); }
If you want to use separate file for the variables, put the top level class that contains variables in a separate file and then compose:
.foo {
composes: container from "./colors.css";
color: var(--color-fancy);
}
@agconti I've created a fork of your reproduction to hopefully illustrate the issue more accurately - https://github.com/simonsmith/next-js-css-modules-unable-to-use--global-with-css-modules
It's quite correct that an error should be thrown when using :global
as that is the intention of the pure
option, to prevent accidental leaking of styles into other components.
Some examples can be seen in the pure
mode unit tests found in postcss-modules-local-by-default
(which css-loader
uses) - https://github.com/css-modules/postcss-modules-local-by-default/blob/master/test.js#L393-L428
The issue seems to be more when making use of the @value
keyword which I've updated the fork to show. Now we see the following error:
Selector ":import("../colors.css")" is not pure (pure selectors must contain at least one local class or id)
So it seems as though the :import
selector is at fault, despite there being a test to say it should be ignored: https://github.com/css-modules/postcss-modules-local-by-default/blob/4b765b15707e53099340a74203c535539041af93/test.js#L326-L329
Interestingly if I add the following test to postcss-modules-local-by-default
:
{
should: 'ignore :import statemtents',
input: ':import("~/lol.css") { foo: __foo; }',
+ options: {mode: 'pure'},
expected: ':import("~/lol.css") { foo: __foo; }',
},
I see the exact same error! This makes me think it may be a bug in postcss-modules-local-by-default
I can open an issue on that repository to track this cc @evilebottnawi Do you agree?
I solved my issues by moving away from implicit HTML calls (ex:
nav
and<nav>
) for specific elements, instead calling my components with classes (ex.nav
and following up with<nav className={s.nav}>
I've been attempting to migrate our codebase onto Next, but just today discovered that our SCSS implementation breaks because all of our selectors have to be pure (and plenty are not). While I get why pure selectors would be ideal, it makes it impossible for large, enterprise projects full of "legacy" SCSS to migrate. I'll never get funding for a tech debt effort to update 100+ scss files. There should be a flag to turn off pure mode.
Same here. At present we can't migrate to this built-in feature because of this problem.
I don't know to say except "+1"
I've traced https://github.com/zeit/next.js/issues/11629 back to this issue as well.
Our setup is having our colour and breakpoint variables declared in a config which is shared between JS, local and global scss alike. A combination of :export
and @use
allows us to do that. The 'pure' mode on CSS modules breaks this on our end.
I'm also worried what will happen when I try to apply keyframes declared in the global CSS in the modules, I'm sure I would need a :global
flag for that, too.
Finally; I cannot adjust the exported class names that CSS modules generate. This is causing inconsistencies between my regular build and Storybook, which supports my own CSS modules settings.
Basically; we need the ability to adjust CSS loader options.
Overriding the default webpack config worked for me.
To override import behaviour for .module.css
(or sass -- thanks to @nkalinov via this comment):
/**
* Stolen from https://stackoverflow.com/questions/10776600/testing-for-equality-of-regular-expressions
*/
const regexEqual = (x, y) => {
return (
x instanceof RegExp &&
y instanceof RegExp &&
x.source === y.source &&
x.global === y.global &&
x.ignoreCase === y.ignoreCase &&
x.multiline === y.multiline
);
};
module.exports = {
webpack: (config) => {
const oneOf = config.module.rules.find(
(rule) => typeof rule.oneOf === 'object'
);
if (oneOf) {
const moduleCssRule = oneOf.oneOf.find(
(rule) => regexEqual(rule.test, /\.module\.css$/)
// regexEqual(rule.test, /\.module\.(scss|sass)$/)
);
if (moduleCssRule) {
const cssLoader = moduleCssRule.use.find(({ loader }) =>
loader.includes('css-loader')
);
if (cssLoader) {
cssLoader.options.modules.mode = 'local';
}
}
}
return config;
},
};
To override all CSS import behaviour:
module.exports = {
webpack: (config) => {
const oneOf = config.module.rules.find(
(rule) => typeof rule.oneOf === 'object'
);
const fixUse = (use) => {
if (use.loader.indexOf('css-loader') >= 0 && use.options.modules) {
use.options.modules.mode = 'local';
}
};
if (oneOf) {
oneOf.oneOf.forEach((rule) => {
if (Array.isArray(rule.use)) {
rule.use.map(fixUse);
} else if (rule.use && rule.use.loader) {
fixUse(rule.use);
}
});
}
return config;
},
};
If you need to import global CSS, you can configure it to use global
and import the styles via a custom pages/_app.js
file.
@Timer the fix for this issue landed in v3.0.3 of postcss-modules-local-by-default (css-modules/postcss-modules-local-by-default#23). Updating css-loader in @zeit/next-css
to "^4.2.1"
would effectively resolve this issue.
Yes, need to update deps and it can be closed
Fixed in [email protected]
.
@Timer sorry for the ignorance, but when will this release (not as prerelease)?
@Pegase745 @anupsarode @cedric-marcone @tommyboylab @agconti @cleversprocket @HoraceShmorace
You are receiving this error because you are using html tags directly instead of classnames or ids in a file extension that is probably [filename].module.(css | scss | sass)
File extensions with *.module.(css | scss | sass) are css modules and they can only target elements using classnames or ids and not using tag names. Although this is possible in other frameworks like create-react-app, it is not possible in next-js (as of now).
My suggestion is using these html selector css in a separate file that doesn't contain the name like '.module' in it.
Example: [filename].(css | scss | sass) --> styles.scss
And after doing that, instead of importing like this
import styles from './styles.module.scss';
import like this
import './styles.scss';
This will not through any errors.
Thank you for that clarification @naveen-bharathi. That was exactly my problem. I knew that styling html tags wasn't possible in modules, but ignored the error message because my html tag was styled in a non-module .scss file. What I had forgotten was that I was importing the non-module .scss file in a .module.scss file, obviously causing the error.
Most helpful comment
I've been attempting to migrate our codebase onto Next, but just today discovered that our SCSS implementation breaks because all of our selectors have to be pure (and plenty are not). While I get why pure selectors would be ideal, it makes it impossible for large, enterprise projects full of "legacy" SCSS to migrate. I'll never get funding for a tech debt effort to update 100+ scss files. There should be a flag to turn off pure mode.