Reputation: 33
So I'm migrating an app from CRA to NextJS and I have encountered an error for the .module.scss files of some components and pages:
Syntax error: Selector ":global(.label-primary)" is not pure (pure selectors must contain at least one local class or id)
I get this error for all the :global and :local css-module selectors. Based on what I have searched I can fix this issue by wrapping the selector in a class and editing the jsx aswell. but wouldn't that defeat it's purpose? And how is this working on the CRA version of the app and not on NextJS?
EDIT: One solution I have for this is moving :global() selectors to the global css files that are imported in _app.js but my question is that is there any way that we can have so these styles would be usable like they are right now ( :global(...) )?
Upvotes: 3
Views: 12381
Reputation: 1
I created a plugin to solve this problem
const rules = config.module.rules
.find((rule) => typeof rule.oneOf === 'object')
?.oneOf.filter((rule) => Array.isArray(rule.use));
if (rules) {
rules.forEach((rule) => {
rule.use.forEach((moduleLoader) => {
if (
moduleLoader.loader?.includes('css-loader') &&
!moduleLoader.loader.includes('postcss-loader') &&
typeof moduleLoader.options.modules === 'object'
) {
moduleLoader.options = {
...moduleLoader.options,
modules: {
...moduleLoader.options.modules,
mode: 'local', // Keep CSS Modules functionality
auto: true, // Automatically detect whether to use CSS Modules
exportGlobals: true, // Allow exporting global styles
},
};
}
});
});
}
return config;
This plugin solves this problem by overwriting the webpack configuration of next.js and making the css-loader configuration mode 'local' instead of 'prue'
Upvotes: 0
Reputation: 109
If you are looking for a solution to do global styles for CSS-Modules with Nextjs v14+ (Works in App-router & TurboPack) too.
add the class in the CSS file like this
:global(.my__global__class) {
color: violet;
}
In the JSX use classname="" the normal classname not the CSS-Module one
// Wrong ❌ - Not-working
className={styles["my__global__class"]}
// Right ✅ Working
className="my__global__class"
Upvotes: 0
Reputation: 432
Another approach is to make a container, wrap it, and carry it out like follows:
import style from '../styles/style.module.css'
<div className={styles.container}>
<p>Hello World</p>
</div>
.container p {
font-size: 20px;
}
And yes, you only need to include your tags and conditions in the CSS file if you have a lot of them.
Upvotes: 0
Reputation: 31
I had the same problem, the right writing is
.root:global {
color:red
}
Upvotes: 3
Reputation: 13024
No there isn't any solution as of yet other than overriding the webpack config itself. It was working in CRA because they probably have mode
: local
, while Next.js has pure
.
I haven't tried overriding css-loader
webpack config, so I am simply suggesting a workaround. Since, you are using SCSS, you can wrap your pseudo-global [1] styles like this:
.root :global {
.foo {
color: red;
}
}
Now wrap your component/page in a div
and set the class as styles.root
on that element. Then, on all the child elements you can directly set className="foo"
.
import styles from "../styles/index.module.scss";
const IndexPage = () => (
<div className={styles.root}>
<div className="foo">This text should be red!</div>
</div>
);
export default IndexPage;
Note that, you need to consider issues regarding specificity after this method, also this doesn't directly work with animations, you need to separate the keyframes
and then make them global.
[1]: This method doesn't make the styles truly global as the styles are still scoped. The class foo
will work only when some parent has styles.root
as class. This is preferrable only if you didn't intend to use your :global(.selector)
from other components, and were using them just because you wanted to manipulate the class names using JS without the styles object.
If you want these to be truly global, add styles.root
to document.documentElement
in an useEffect
hook like this:
import { useEffect } from "react";
import styles from "../styles/index.module.scss";
const IndexPage = () => {
useEffect(() => {
document.documentElement.classList.add(styles.root);
return () => {
document.documentElement.classList.remove(styles.root);
};
}, []);
return (
<div className="foo">
This text should be red, even if you put it in another component until the
page is same. If you want it across pages inject it in _app or _document.
</div>
);
};
export default IndexPage;
PS: Injecting class to html
in _app
or _document
is not exactly same as using a global stylesheet, as it may happen that you have multi-page application, then only the CSS of the components on a particular page will be requested because of automatic CSS code-splitting done by Next.js. If that's not the case and all your pages share same CSS, then there is no need to complicate things, just go with the conventional method of importing styles in _app
.
Upvotes: 4