&__element (BEM-style nesting) compiles to invalid selector :is(.block)__element in v4.3.3
#20443
Replies: 7 comments 1 reply
|
Could you provide a git repo where it was working? In Lightning CSS playground (i.e. before 4.3), it compiles to: __element.block {
color: red;
}And in Vite with __element.block{color:red}Regardless, the nesting was always meant to follow native CSS behavior and not Sass-like behavior. |
|
@wongjn the same code was working with version 4.3.0, when I upgraded to the latest version (4.3.3), compiled CSS was broken (I haven't tried 4.3.1, 4.3.2). I'm using |
|
This is not postcss-nested, and the regression is narrower than the thread suggests. It landed in 4.3.3 specifically. I reproduced it with .block {
&__element { color: red; }
&:hover { color: blue; }
}/* tailwindcss 4.3.3 */
:is(.block)__element { color: red; }
.block:hover { color: blue; }Bisecting the same input across versions:
So through 4.3.2 Tailwind passed this nesting through untouched, which is why @ondrej-kaspar's setup worked: postcss-nested got it afterwards and expanded it Sass-style. Confirmed both ways with postcss-nested 8.0.1 in the pipeline: Plugin order does not change that, since the Tailwind plugin compiles the file itself rather than handing an AST along. postcss-nested on its own still resolves it correctly to On the Sass versus native question. @wongjn is right that this should follow native CSS, and native CSS does not concatenate: Worth noting the failure is silent. Through For anyone hitting this now, 4.3.2 is the last version that works with a postcss-nested setup, and writing Reproduced on Node 22, Linux x64, |
|
PostCSS doesn't run plugins in order because they are using a visitor-like API instead. If you were using a production build that triggered Lightning CSS, then .block {
&__element { color: red; }
}Would've been turned into: __element.block {
color: red;
}For normal builds, prior to Tailwind CSS 4.3.3, it was left untouched. As mentioned before, we did always "require" valid CSS, and If you want to keep using the postcss-nested plugin, you have to make sure that it's executed before Tailwind CSS sees it. You can try doing something like this in your postcss.config.js file: const postcss = require("postcss");
const postcssNested = require("postcss-nested");
module.exports = {
plugins: [
{
postcssPlugin: "postcss-nested-first",
Once(root, { result }) {
return postcss([postcssNested()]).process(root, {
from: result.opts.from,
});
},
},
require("@tailwindcss/postcss"),
],
};Let me know if this works for you |
|
Thanks @RobinMalfait, the visitor API point explains the thing I could not account for. I had tried postcss-nested on both sides of Tailwind and got Recording the version detail for anyone who lands here from a search, since it is the part that makes this look like a regression rather than a config problem: through 4.3.2 the nesting was passed through untouched, so a postcss-nested setup kept working by accident. 4.3.3 is the first version that resolves it, which is why the upgrade broke it. Agreed that |
Why this happensTailwind CSS v4 replaces the legacy PostCSS/Sass preprocessing pipeline with an engine built on top of native CSS Nesting (via Lightning CSS). In the native CSS Nesting specification:
How to Fix1. Write the explicit compound selector (Recommended)Because Tailwind v4 scans and handles standard CSS classes efficiently, writing the full BEM selector without prefix concatenation is the cleanest and most performant approach: @layer components {
.block {
/* base block styles */
}
.block__element {
/* element styles */
}
}2. Define it with @UtilityIf you are declaring reusable utility styles directly in your CSS files: @utility block__element {
/* your styling rules */
}(Note: If your project structure strictly mandates Sass-style string concatenation with &__, you will need to run a dedicated preprocessor like sass before the Tailwind v4 build step). |
|
This happens because Tailwind v4 switched to native CSS nesting rather than using Sass-like string preprocessing. Under the native CSS Nesting Module Level 1 specification, the In v3 and early v4 builds, Lightning CSS or Sass did raw string interpolation ( For now, the workaround in v4 is to write out the full class name ( |
Uh oh!
There was an error while loading. Please reload this page.
Since the native CSS nesting handling was introduced in v4.3.0 (#20124), the following input:
compiles to:
which is invalid CSS — :is() cannot be directly concatenated with an identifier like that.
Expected behavior
Since & is immediately followed by an identifier (no combinator or pseudo-class), it should be treated as a compound selector and produce:
This is how it worked before v4.3.0 when nesting was resolved by Lightning CSS, and how &:hover-style nesting (with a pseudo-class after &) still correctly resolves to :is(.block):hover.
Environment
Tailwind CSS v4.3.3
Reproduction
https://play.tailwindcss.com/nLeNDYZr4j?file=css
All reactions