Issue with :global() css-module selectors not being pure in NextJS
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.
Demo Sandbox
[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;
Demo Sandbox
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
.
Selector :global .class is not pure (pure selectors must contain at least one local class or id)
You need to use global selector inside your local selector in CSS-modules.
For example, if you have HTML:
<div className={classes.someCSSMoludesClass}>
<div className="some-global-class">
content
</div>
</div>
for rewriting global class "some-global-class" you need to make this inside your CSS-module:
.someCSSModulesClass {
:global(.some-global-class) {
%your properties%
}
}
Don't forget to use selector inside :global.
I had the same problem, but in swiper slider, and resolved it like this.
Maybe you have to write this class in the component that is above
Targeting Pure elements in next.js with CSS modules
Try giving a className or id to ul tag and then write your styles accordingly.
for example:
index.js
<div className={styles.container}>
<ul className={styles.container__list}>
<li>Person One</li>
</ul>
</div>
index.module.css
.container {
background-color: pink;
}
.container__list{
list-style-type: none;
}
Selector :root is not pure (pure selectors must contain at least one local class or id) - NextJS with SASS modules
After scouring the internet for a few hours I found a great solution from here: https://dhanrajsp.me/snippets/customize-css-loader-options-in-nextjs
EDIT: If you're using Next.js 12, check the bottom of the article above, because the solution is a little different.
You'll want to change your next.config.js file to include the following:
/** @type {import('next').NextConfig} */
require("dotenv").config();
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
);
};
// Overrides for css-loader plugin
function cssLoaderOptions(modules) {
const { getLocalIdent, ...others } = modules; // Need to delete getLocalIdent else localIdentName doesn't work
return {
...others,
localIdentName: "[hash:base64:6]",
exportLocalsConvention: "camelCaseOnly",
mode: "local",
};
}
module.exports = {
webpack: (config) => {
const oneOf = config.module.rules.find(
(rule) => typeof rule.oneOf === "object"
);
if (oneOf) {
// Find the module which targets *.scss|*.sass files
const moduleSassRule = oneOf.oneOf.find((rule) =>
regexEqual(rule.test, /\.module\.(scss|sass)$/)
);
if (moduleSassRule) {
// Get the config object for css-loader plugin
const cssLoader = moduleSassRule.use.find(({ loader }) =>
loader.includes("css-loader")
);
if (cssLoader) {
cssLoader.options = {
...cssLoader.options,
modules: cssLoaderOptions(cssLoader.options.modules),
};
}
}
}
return config;
},
};
I'm not seasoned with webpack or how it exactly works, but this solution worked for me. You can also change the regex to include css by doing (scss|sass|css) if you want.
Next.JS using CSS Modules not working as expected
I ran into the same issue you had when I was migrating our UI component library. You might be able to do something like this. import styles from "./welcome.module.css"
. This allows you to do minimum work on the CSS side while still be able to leverage CSS Modules down the road.
:global {
.headliner-container {
margin: 0 20px;
}
}
I do suggest you at least use a component scope class name on the top-level DOM node if possible so CSS override will less likely to happen. I don't know how pure CSS Module might handle it since I used Less for my situation.
import React from 'react';
import styles from "./welcome.module.css"
function Headline() {
return (
<div className={styles.headLineWrapper}>
<section className={'headliner-container'}>
<div className={'main-headline'}></div>
</section>
</div>
);
}
export default Headline;
/*LESS code */
.head-line-wrapper {
:global {
.headliner-container {
margin: 0 20px;
}
}
}
Related Topics
Jqgrid Rowattr Not Applying Class
CSS Hover Over on Thumbnails in Wordpress
Multiple Attributes in Ng-Style
How to Set The Width and Height of a Textarea Using CSS
CSS: Not(), Selectors and Selecting Descendants
Cannot Stretch Svg Background Image, Aspect Ratio Will Be Preserved
Text-Shadow and Box-Shadow While Printing (Chrome)
CSS Change Border Color on Hover
:After,: Before Issues in Internet Explorer 11
How to Detect If Webp Images Are Supported via CSS
What Is The Limit of Character to Use in Alt="Text" According to Wcag 2.0
How to Change The Color of The Text Cursor in an Input Field in Ie
CSS Buttons Rounded on One Side
How to Target Chrome Only, Not All Webkit Powered Browsers
Change Background Colour as Page Scroll Without Jquery
How to Get Firefox to Show an Auto Horizontal Scollbar for a Div