fix(next16): wrap CSS Modules in a cascade layer so Tailwind wins

Under Turbopack, per-component CSS Module styles are injected after the global Tailwind sheet. Both module rules and Tailwind components/utilities were unlayered, so source order let module rules override Tailwind utilities (symptom: horizontally stretched images on the image detail page, where EdgeMedia .responsive {height:auto} beat the Tailwind sizing class).

Fix: wrap every *.module.css/.module.scss into a 'modules' cascade layer via postcss-assign-layer. Tailwind components/utilities stay unlayered, so they beat any layer regardless of injection order. No explicit @layer order declaration is used: module CSS loads after Mantine's imports, so '@layer modules' naturally sits above '@layer mantine', and both sit below unlayered Tailwind. Final cascade: preflight < theme < utilities < mantine < modules < unlayered Tailwind (wins).

An explicit '@layer mantine, modules;' declaration was deliberately avoided: pinning 'mantine' as the first-declared layer dropped it below Tailwind's Preflight base layer, so Preflight's button reset beat Mantine styling (e.g. CategoryTags buttons lost their fill).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Briant Diehl
2026-06-08 15:49:59 -06:00
parent 4c4d100df6
commit 5b2f15b2f2
3 changed files with 39 additions and 0 deletions
+1
View File
@@ -326,6 +326,7 @@
"pg-format": "^1.0.4",
"playwright": "^1.57.0",
"postcss": "^8.5.3",
"postcss-assign-layer": "^0.4.0",
"postcss-preset-mantine": "^1.17.0",
"postcss-simple-vars": "^7.0.1",
"prettier": "^2.8.8",
+28
View File
@@ -761,6 +761,9 @@ importers:
postcss:
specifier: ^8.5.3
version: 8.5.6
postcss-assign-layer:
specifier: ^0.4.0
version: 0.4.0(postcss@8.5.6)
postcss-preset-mantine:
specifier: ^1.17.0
version: 1.18.0(postcss@8.5.6)
@@ -3059,6 +3062,10 @@ packages:
'@rolldown/pluginutils@1.0.0-beta.27':
resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
'@rollup/pluginutils@4.2.1':
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
engines: {node: '>= 8.0.0'}
'@rollup/rollup-android-arm-eabi@4.56.0':
resolution: {integrity: sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==}
cpu: [arm]
@@ -6040,6 +6047,9 @@ packages:
estree-util-visit@2.0.0:
resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==}
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
@@ -8180,6 +8190,12 @@ packages:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
postcss-assign-layer@0.4.0:
resolution: {integrity: sha512-ART9ENWnvEyW3yyfJe83nz5j/IsbQHBgSMsUNFmC/+6zy0uC9YTriHKV6TEjOOHXRzXS2yNPj0ksvYkEwwkv5w==}
engines: {node: '>=12.0.0'}
peerDependencies:
postcss: ^8.3.0
postcss-calc@10.1.1:
resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==}
engines: {node: ^18.12 || ^20.9 || >=22.0}
@@ -13255,6 +13271,11 @@ snapshots:
'@rolldown/pluginutils@1.0.0-beta.27': {}
'@rollup/pluginutils@4.2.1':
dependencies:
estree-walker: 2.0.2
picomatch: 2.3.1
'@rollup/rollup-android-arm-eabi@4.56.0':
optional: true
@@ -16702,6 +16723,8 @@ snapshots:
'@types/estree-jsx': 1.0.5
'@types/unist': 3.0.3
estree-walker@2.0.2: {}
estree-walker@3.0.3:
dependencies:
'@types/estree': 1.0.8
@@ -19366,6 +19389,11 @@ snapshots:
possible-typed-array-names@1.1.0: {}
postcss-assign-layer@0.4.0(postcss@8.5.6):
dependencies:
'@rollup/pluginutils': 4.2.1
postcss: 8.5.6
postcss-calc@10.1.1(postcss@8.5.6):
dependencies:
postcss: 8.5.6
+10
View File
@@ -12,6 +12,16 @@ module.exports = {
},
},
autoprefixer: {},
// Wrap every CSS Module's output in a `modules` cascade layer. Unlayered
// Tailwind utilities (see src/styles/globals.css) then always beat module
// styles regardless of Turbopack's CSS injection order — the root cause of
// module rules overriding Tailwind after the Next 16 / Turbopack switch.
// Value is the array the plugin expects; runs before cssnano so the
// @layer wrapper is in place before minification.
'postcss-assign-layer': [
{ include: '**/*.module.css', layerName: 'modules' },
{ include: '**/*.module.scss', layerName: 'modules' },
],
...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {})
}
}