Unexpected CSS Module Ordering in Dev/Prod When Using Tree-Shaking #72846
Labels
bug
Issue was opened via the bug report template.
linear: turbopack
Confirmed issue that is tracked by the Turbopack team.
Turbopack
Related to Turbopack with Next.js.
Webpack
Related to Webpack with Next.js.
Link to the code that reproduces this issue
https://github.com/jantimon/reproduction-webpack-css-order
To Reproduce
Clone the repository and checkout the
turbo
branchpnpm install
pnpm run dev
see that the button is blue (but should be orange)
Current vs. Expected behavior
While analyzing a CSS ordering problem in our monorepo, I traced it down to an interesting combination of module graph building and tree-shaking. The core of the issue appears to be in how the module graph handles CSS imports when
sideEffects: false
is set (orsideEffects: ["*.css"]
Looking at webpack's buildChunkGraph.js (https://github.com/webpack/webpack/blob/5e21745e98eb90a029e1f5374d4e4ac338fbe7c7/lib/buildChunkGraph.js#L683-L708), I found that the module traversal order changes once webpack is able to remove a barrel file.
That’s quite a bad DX for most developers because it means that the CSS order changes can be caused by JavaScript refactoring that seems completely unrelated to styles
Here's a concrete example from the reproduction - changing from:
to:
can unexpectedly reorder CSS across the entire application. This means that code cleanup like splitting up barrel files or moving components between packages can silently break styles in seemingly unrelated components.
I've done some testing across different bundlers to understand how they handle this scenario:
What's interesting is that both Vite and Parcel manage to maintain consistent CSS ordering while still being able to tree-shake. So we might be able to find a middle ground that keeps the benefits of tree-shaking and allows a consistent CSS order
To better understand the issue, I've created a minimal reproduction: https://github.com/jantimon/reproduction-webpack-css-order
The tricky part is that this only manifests when several conditions align:
When building with
sideEffects: false
, the CSS order becomes unpredictable. Here's the output:Here are the module graphs for the 3 scenarios.
The postOrder is the index which is used for the css order:
sideEffects: true
exampleno barrel example
sideEffects:false
exampleFor me common suggestions like "just use Tailwind" or "increase specificity" miss the point - vanilla CSS with simple, understandable selectors should be an option. The unpredictable ordering creates harder to read code where developers need to constantly guard against CSS specificity bugs using
&&&
or!important
.The reproduction repo includes branches for different scenarios and bundlers, making it easy to verify the behavior:
Provide environment information
Which area(s) are affected? (Select all that apply)
Turbopack, Webpack
Which stage(s) are affected? (Select all that apply)
next dev (local), next build (local), next start (local)
Additional context
Related webpack issue:
webpack/webpack#18961
The text was updated successfully, but these errors were encountered: