Skip to content

Commit b389483

Browse files
Make @reference emit variable fallbacks instead of CSS variable declarations (tailwindlabs#16774)
Fixes tailwindlabs#16725 When using `@reference "tailwindcss";` inside a separate CSS root (e.g. Svelte `<style>` components, CSS modules, etc.), we have no guarantee that the CSS variables will be defined in the main stylesheet (or if there even is one). To work around potential issues with this we decided in tailwindlabs#16676 that we would emit all used CSS variables from the `@theme` inside the `@reference` block. However, this is not only a bit surprising but also unexpected in CSS modules and Next.js that **requires CSS module files to only create scope-able declarations**. To fix this issue, we decided to not emit CSS variables but instead ensure all `var(…)` calls we create for theme values in reference mode will simply have their fallback value added. This ensures styles work as-expected even if the root Tailwind file does not pick up the variable as being used or _if you don't add a root at all_. Furthermore we do not duplicate any variable declarations across your stylesheets and you still have the ability to change variables at runtime. ## Test plan - Updated snapshots everywhere (see diff) - New Next.js CSS modules integration test
1 parent 59e003e commit b389483

22 files changed

+201
-205
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
### Fixed
1717

1818
- Vite: Don't crash when importing a virtual module in JavaScript that ends in `.css` ([#16780](https://github.com/tailwindlabs/tailwindcss/pull/16780))
19+
- Ensure `@reference "…"` does not emit CSS variables ([#16774](https://github.com/tailwindlabs/tailwindcss/pull/16774))
20+
- Fix an issue where `@reference "…"` would sometimes omit keyframe animations ([#16774](https://github.com/tailwindlabs/tailwindcss/pull/16774))
1921

2022
## [4.0.8] - 2025-02-21
2123

integrations/cli/index.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ test(
643643
}
644644
`,
645645
'index.css': css`
646-
@import 'tailwindcss/theme' theme(reference);
646+
@reference 'tailwindcss/theme';
647647
648648
/* (1) */
649649
/* - Only './src' should be auto-scanned, not the current working directory */
@@ -774,7 +774,7 @@ test(
774774
}
775775
`,
776776
'project-a/src/index.css': css`
777-
@import 'tailwindcss/theme' theme(reference);
777+
@reference 'tailwindcss/theme';
778778
779779
/* Run auto-content detection in ../../project-b */
780780
@import 'tailwindcss/utilities' source('../../project-b');
@@ -1132,7 +1132,7 @@ test(
11321132
}
11331133
`,
11341134
'index.css': css`
1135-
@import 'tailwindcss/theme' theme(reference);
1135+
@reference 'tailwindcss/theme';
11361136
11371137
/* (1) */
11381138
/* - Only './src' should be auto-scanned, not the current working directory */

integrations/cli/standalone.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ test(
3535
<div class="aspect-w-16"></div>
3636
`,
3737
'src/index.css': css`
38-
@import 'tailwindcss/theme' theme(reference);
38+
@reference 'tailwindcss/theme';
3939
@import 'tailwindcss/utilities';
4040
4141
@plugin '@tailwindcss/forms';

integrations/postcss/index.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ test(
659659
}
660660
`,
661661
'index.css': css`
662-
@import 'tailwindcss/theme' theme(reference);
662+
@reference 'tailwindcss/theme';
663663
664664
/* (1) */
665665
/* - Only './src' should be auto-scanned, not the current working directory */
@@ -799,7 +799,7 @@ test(
799799
}
800800
`,
801801
'project-a/src/index.css': css`
802-
@import 'tailwindcss/theme' theme(reference);
802+
@reference 'tailwindcss/theme';
803803
804804
/* Run auto-content detection in ../../project-b */
805805
@import 'tailwindcss/utilities' source('../../project-b');
@@ -1163,7 +1163,7 @@ test(
11631163
}
11641164
`,
11651165
'index.css': css`
1166-
@import 'tailwindcss/theme' theme(reference);
1166+
@reference 'tailwindcss/theme';
11671167
11681168
/* (1) */
11691169
/* - Only './src' should be auto-scanned, not the current working directory */

integrations/postcss/multi-root.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ test(
2525
<div class="one:underline two:underline"></div>
2626
`,
2727
'src/shared.css': css`
28-
@import 'tailwindcss/theme' theme(reference);
28+
@reference 'tailwindcss/theme';
2929
@import 'tailwindcss/utilities';
3030
`,
3131
'src/root1.css': css`

integrations/postcss/next.test.ts

+48-39
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,13 @@ test(
1919
}
2020
`,
2121
'postcss.config.mjs': js`
22-
/** @type {import('postcss-load-config').Config} */
23-
const config = {
22+
export default {
2423
plugins: {
2524
'@tailwindcss/postcss': {},
2625
},
2726
}
28-
29-
export default config
30-
`,
31-
'next.config.mjs': js`
32-
/** @type {import('next').NextConfig} */
33-
const nextConfig = {}
34-
35-
export default nextConfig
3627
`,
28+
'next.config.mjs': js`export default {}`,
3729
'app/layout.js': js`
3830
import './globals.css'
3931
@@ -46,12 +38,21 @@ test(
4638
}
4739
`,
4840
'app/page.js': js`
41+
import styles from './page.module.css'
4942
export default function Page() {
50-
return <h1 className="text-3xl font-bold underline">Hello, Next.js!</h1>
43+
return (
44+
<h1 className={styles.heading + ' text-3xl font-bold underline'}>Hello, Next.js!</h1>
45+
)
46+
}
47+
`,
48+
'app/page.module.css': css`
49+
@reference './globals.css';
50+
.heading {
51+
@apply text-red-500 animate-ping;
5152
}
5253
`,
5354
'app/globals.css': css`
54-
@import 'tailwindcss/theme' theme(reference);
55+
@reference 'tailwindcss/theme';
5556
@import 'tailwindcss/utilities';
5657
`,
5758
},
@@ -60,14 +61,26 @@ test(
6061
await exec('pnpm next build')
6162

6263
let files = await fs.glob('.next/static/css/**/*.css')
63-
expect(files).toHaveLength(1)
64-
let [filename] = files[0]
64+
expect(files).toHaveLength(2)
6565

66-
await fs.expectFileToContain(filename, [
66+
let globalCss: string | null = null
67+
let moduleCss: string | null = null
68+
for (let [filename, content] of files) {
69+
if (content.includes('@keyframes page_ping')) moduleCss = filename
70+
else globalCss = filename
71+
}
72+
73+
await fs.expectFileToContain(globalCss!, [
6774
candidate`underline`,
6875
candidate`font-bold`,
6976
candidate`text-3xl`,
7077
])
78+
79+
await fs.expectFileToContain(moduleCss!, [
80+
'color:var(--color-red-500,oklch(.637 .237 25.331)',
81+
'animation:var(--animate-ping,ping 1s cubic-bezier(0,0,.2,1) infinite)',
82+
/@keyframes page_ping.*{75%,to{transform:scale\(2\);opacity:0}/,
83+
])
7184
},
7285
)
7386

@@ -90,21 +103,13 @@ describe.each(['turbo', 'webpack'])('%s', (bundler) => {
90103
}
91104
`,
92105
'postcss.config.mjs': js`
93-
/** @type {import('postcss-load-config').Config} */
94-
const config = {
106+
export default {
95107
plugins: {
96108
'@tailwindcss/postcss': {},
97109
},
98110
}
99-
100-
export default config
101-
`,
102-
'next.config.mjs': js`
103-
/** @type {import('next').NextConfig} */
104-
const nextConfig = {}
105-
106-
export default nextConfig
107111
`,
112+
'next.config.mjs': js`export default {}`,
108113
'app/layout.js': js`
109114
import './globals.css'
110115
@@ -117,12 +122,19 @@ describe.each(['turbo', 'webpack'])('%s', (bundler) => {
117122
}
118123
`,
119124
'app/page.js': js`
125+
import styles from './page.module.css'
120126
export default function Page() {
121-
return <h1 className="underline">Hello, Next.js!</h1>
127+
return <h1 className={styles.heading + ' underline'}>Hello, Next.js!</h1>
128+
}
129+
`,
130+
'app/page.module.css': css`
131+
@reference './globals.css';
132+
.heading {
133+
@apply text-red-500 animate-ping content-['module'];
122134
}
123135
`,
124136
'app/globals.css': css`
125-
@import 'tailwindcss/theme' theme(reference);
137+
@reference 'tailwindcss/theme';
126138
@import 'tailwindcss/utilities';
127139
`,
128140
},
@@ -142,13 +154,16 @@ describe.each(['turbo', 'webpack'])('%s', (bundler) => {
142154
await retryAssertion(async () => {
143155
let css = await fetchStyles(url)
144156
expect(css).toContain(candidate`underline`)
157+
expect(css).toContain('content: var(--tw-content)')
158+
expect(css).toContain('@keyframes')
145159
})
146160

147161
await fs.write(
148162
'app/page.js',
149163
js`
164+
import styles from './page.module.css'
150165
export default function Page() {
151-
return <h1 className="underline text-red-500">Hello, Next.js!</h1>
166+
return <h1 className={styles.heading + ' underline bg-red-500'}>Hello, Next.js!</h1>
152167
}
153168
`,
154169
)
@@ -157,7 +172,9 @@ describe.each(['turbo', 'webpack'])('%s', (bundler) => {
157172
await retryAssertion(async () => {
158173
let css = await fetchStyles(url)
159174
expect(css).toContain(candidate`underline`)
160-
expect(css).toContain(candidate`text-red-500`)
175+
expect(css).toContain(candidate`bg-red-500`)
176+
expect(css).toContain('content: var(--tw-content)')
177+
expect(css).toContain('@keyframes')
161178
})
162179
},
163180
)
@@ -181,21 +198,13 @@ test(
181198
}
182199
`,
183200
'postcss.config.mjs': js`
184-
/** @type {import('postcss-load-config').Config} */
185-
const config = {
201+
export default {
186202
plugins: {
187203
'@tailwindcss/postcss': {},
188204
},
189205
}
190-
191-
export default config
192-
`,
193-
'next.config.mjs': js`
194-
/** @type {import('next').NextConfig} */
195-
const nextConfig = {}
196-
197-
export default nextConfig
198206
`,
207+
'next.config.mjs': js`export default {}`,
199208
'app/a/[slug]/page.js': js`
200209
export default function Page() {
201210
return <h1 className="content-['[slug]']">Hello, Next.js!</h1>

integrations/postcss/plugins.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ test(
2626
<div className="ui-open:flex"></div>
2727
`,
2828
'src/index.css': css`
29-
@import 'tailwindcss/theme' theme(reference);
29+
@reference 'tailwindcss/theme';
3030
@import 'tailwindcss/utilities';
3131
@plugin '@headlessui/tailwindcss';
3232
`,
@@ -65,7 +65,7 @@ test(
6565
<div className="ui-open:flex"></div>
6666
`,
6767
'src/index.css': css`
68-
@import 'tailwindcss/theme' theme(reference);
68+
@reference 'tailwindcss/theme';
6969
@import 'tailwindcss/utilities';
7070
@plugin '@headlessui/tailwindcss';
7171
`,

integrations/vite/index.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
6262
}
6363
`,
6464
'project-a/src/index.css': css`
65-
@import 'tailwindcss/theme' theme(reference);
65+
@reference 'tailwindcss/theme';
6666
@import 'tailwindcss/utilities';
6767
@config '../tailwind.config.js';
6868
@source '../../project-b/src/**/*.html';
@@ -147,7 +147,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
147147
}
148148
`,
149149
'project-a/src/index.css': css`
150-
@import 'tailwindcss/theme' theme(reference);
150+
@reference 'tailwindcss/theme';
151151
@import 'tailwindcss/utilities';
152152
@config '../tailwind.config.js';
153153
@source '../../project-b/src/**/*.html';
@@ -291,7 +291,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
291291
}
292292
`,
293293
'project-a/src/index.css': css`
294-
@import 'tailwindcss/theme' theme(reference);
294+
@reference 'tailwindcss/theme';
295295
@import 'tailwindcss/utilities';
296296
@import './custom-theme.css';
297297
@config '../tailwind.config.js';

integrations/vite/multi-root.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ test(
4343
</body>
4444
`,
4545
'src/shared.css': css`
46-
@import 'tailwindcss/theme' theme(reference);
46+
@reference 'tailwindcss/theme';
4747
@import 'tailwindcss/utilities';
4848
`,
4949
'src/root1.css': css`
@@ -119,7 +119,7 @@ test(
119119
</body>
120120
`,
121121
'src/shared.css': css`
122-
@import 'tailwindcss/theme' theme(reference);
122+
@reference 'tailwindcss/theme';
123123
@import 'tailwindcss/utilities';
124124
`,
125125
'src/root1.css': css`

integrations/vite/other-transforms.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function createSetup(transformer: 'postcss' | 'lightningcss') {
4747
</body>
4848
`,
4949
'src/index.css': css`
50-
@import 'tailwindcss/theme' theme(reference);
50+
@reference 'tailwindcss/theme';
5151
@import 'tailwindcss/utilities';
5252
5353
.foo {
@@ -111,7 +111,7 @@ describe.each(['postcss', 'lightningcss'] as const)('%s', (transformer) => {
111111
await fs.write(
112112
'src/index.css',
113113
css`
114-
@import 'tailwindcss/theme' theme(reference);
114+
@reference 'tailwindcss/theme';
115115
@import 'tailwindcss/utilities';
116116
117117
.foo {
@@ -155,7 +155,7 @@ describe.each(['postcss', 'lightningcss'] as const)('%s', (transformer) => {
155155
await fs.write(
156156
'src/index.css',
157157
css`
158-
@import 'tailwindcss/theme' theme(reference);
158+
@reference 'tailwindcss/theme';
159159
@import 'tailwindcss/utilities';
160160
161161
.foo {

integrations/vite/resolvers.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
4949
@plugin '#js-alias';
5050
`,
5151
'src/alias.css': css`
52-
@import 'tailwindcss/theme' theme(reference);
52+
@reference 'tailwindcss/theme';
5353
@import 'tailwindcss/utilities';
5454
`,
5555
'src/plugin.js': js`
@@ -117,7 +117,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
117117
@plugin '#js-alias';
118118
`,
119119
'src/alias.css': css`
120-
@import 'tailwindcss/theme' theme(reference);
120+
@reference 'tailwindcss/theme';
121121
@import 'tailwindcss/utilities';
122122
`,
123123
'src/plugin.js': js`

0 commit comments

Comments
 (0)