HTML elements aren't being assigned classnames when the Tailwind JIT engine is used with CSS modules and @layer components. This happens in both dev and prod
I think most people can remove @layer but I'm reporting this anyway because this seems like a regression
Reproduction
yarn create next-app --example with-tailwindcss, then cd into the project dir
3. `yarn run dev`, notice that the root div has no class
This issue occurs:
- if you switch to webpack 5
This issue does not occur:
- if you disable JIT
- if you remove `@layer components`
- if you use `@apply` in a `@layer` directive in a global stylesheet, not in a CSS module (I think)
- if you use `@tailwindcss/jit` (I think)
I think this is probably expected if I understand it right — CSS modules are processed in total isolation and it's not possible for a @layer rule there to be hoisted out into Tailwind's main @tailwind components layer, those two trees of CSS are just totally unable to communicate or manipulate each other.
So looking into it more, the reason this works without JIT is that the layer stuff is just ignored completely. So it's exactly the same as not using @layer at all. We should probably throw an explicit error or something if a layer is detected in a file where it's not possible for it to work.
I think this is probably expected if I understand it right — CSS modules are processed in total isolation and it's not possible for a @layer rule there to be hoisted out into Tailwind's main @tailwind components layer, those two trees of CSS are just totally unable to communicate or manipulate each other.
This makes sense, but I still don't think I understand why JIT behaves differently than "traditional" compiling. I would expect that the traditional compiler and the JIT compiler would receive the same CSS input, so I think the difference is that in traditional mode, Tailwind simply removes @layer, while JIT completely skips @layer blocks—is this true?
I don't think I really need @layer if I'm using CSS modules, but I still feel like traditional mode and JIT mode should return identical outputs in all cases.
The difference is just that they are completely different chunks of code so there are just differences in how they work.
In the JIT engine, anything in a layer is read and converted into an in-memory Tailwind plugin so it can be used to generate styles on demand, and this happens only when the build server first starts. Then, all layer rules are deleted from the source file, including all the CSS within them, since instead those rules are inserted on demand when detected in the HTML.
In the traditional engine, layers are just blocks of CSS, and there's a step that gathers them all up and colocates code for the same layers together, and then late in the build stage the layer rules are converted to purgecss control comments which we use to make sure we only purge layers, and not regular custom CSS that lives outside of a layer.
So it's just totally different code that needs to do totally different things for each engine — just bound to be differences in the failure modes like you've detected here.
So in the traditional engine, what's happening with your CSS module is that the layer statements are being converted to comments, which means they end up just doing nothing. In the JIT engine, the layers are deleted since they are captured into memory as plugins, and now even though the HTML contains rules that are coming from those layer plugins, the code is never output because there is no @tailwind components block in the CSS module for the layer code to be inserted into.
I agree that the output should be the same, this is just one of those cases where an error situation I didn't really think much about ends with different output in each engine because of the design of the separate engines.
Both engines should probably error, but it's a little tricky because there's a real @layer rule coming to CSS and it might not be wise for us to assume all use of layer is Tailwind related. Can probably safely make assumptions about the base, components, and utilities layer names I suppose though.
What version of Tailwind CSS are you using?
v2.1.1
What version of Node.js are you using?
v12.14.1
What browser are you using?
Chrome
What operating system are you using?
macOS
Reproduction repository
repro steps below
The Bug
HTML elements aren't being assigned classnames when the Tailwind JIT engine is used with CSS modules and
@layer components
. This happens in both dev and prodI think most people can remove
@layer
but I'm reporting this anyway because this seems like a regressionReproduction
yarn create next-app --example with-tailwindcss
, thencd
into the project dir+import styles from "./index.module.css"; + export default function Home() { return (
-"@tailwindcss/jit@0.1.3":
"@types/node@*": version "14.14.37" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" @@ -2213,7 +2200,7 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0"
-tailwindcss@^2.0.2: +tailwindcss@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.1.1.tgz#642f6038c9283a8e1454da34585b8b7c1a1e8877" integrity sha512-zZ6axGqpSZOCBS7wITm/WNHkBzDt5CIZlDlx0eCVldwTxFPELCVGbgh7Xpb3/kZp3cUxOmK7bZUjqhuMrbN6xQ==
I am seeing the same symptoms in a similar setup (but using just
css-loader
andmini-css-extract-plugin
).I think this is probably expected if I understand it right — CSS modules are processed in total isolation and it's not possible for a
@layer
rule there to be hoisted out into Tailwind's main@tailwind components
layer, those two trees of CSS are just totally unable to communicate or manipulate each other.What's the desired behavior here exactly?
So looking into it more, the reason this works without JIT is that the layer stuff is just ignored completely. So it's exactly the same as not using
@layer
at all. We should probably throw an explicit error or something if a layer is detected in a file where it's not possible for it to work.This makes sense, but I still don't think I understand why JIT behaves differently than "traditional" compiling. I would expect that the traditional compiler and the JIT compiler would receive the same CSS input, so I think the difference is that in traditional mode, Tailwind simply removes
@layer
, while JIT completely skips@layer
blocks—is this true?I don't think I really need
@layer
if I'm using CSS modules, but I still feel like traditional mode and JIT mode should return identical outputs in all cases.The difference is just that they are completely different chunks of code so there are just differences in how they work.
In the JIT engine, anything in a
layer
is read and converted into an in-memory Tailwind plugin so it can be used to generate styles on demand, and this happens only when the build server first starts. Then, alllayer
rules are deleted from the source file, including all the CSS within them, since instead those rules are inserted on demand when detected in the HTML.In the traditional engine, layers are just blocks of CSS, and there's a step that gathers them all up and colocates code for the same layers together, and then late in the build stage the layer rules are converted to purgecss control comments which we use to make sure we only purge layers, and not regular custom CSS that lives outside of a layer.
So it's just totally different code that needs to do totally different things for each engine — just bound to be differences in the failure modes like you've detected here.
So in the traditional engine, what's happening with your CSS module is that the layer statements are being converted to comments, which means they end up just doing nothing. In the JIT engine, the layers are deleted since they are captured into memory as plugins, and now even though the HTML contains rules that are coming from those layer plugins, the code is never output because there is no
@tailwind components
block in the CSS module for the layer code to be inserted into.I agree that the output should be the same, this is just one of those cases where an error situation I didn't really think much about ends with different output in each engine because of the design of the separate engines.
Both engines should probably error, but it's a little tricky because there's a real
@layer
rule coming to CSS and it might not be wise for us to assume all use oflayer
is Tailwind related. Can probably safely make assumptions about thebase
,components
, andutilities
layer names I suppose though.In the next version this will throw an explicit error 👍🏻