Skip to content

Commit 3f270d2

Browse files
authored
Allow theme(…) options when using @import (#16514)
This PR improves the developer experience when trying to use `theme(…)` options on an import. Today, if you want to use Tailwind CSS, you can import it as: ```css @import "tailwindcss"; ``` But if you want to use any of the `theme(…)` options, like the `static` theme option, then you had to use this instead: ```css @layer theme, base, components, utilities; @import 'tailwindcss/theme' layer(theme) theme(static); @import 'tailwindcss/preflight' layer(base); @import 'tailwindcss/utilities' layer(utilities); ``` In this scenario you have to be careful, because the `layer(…)` _must_ be the first option after an import (according to the spec). So if you use `@import 'tailwindcss/theme' theme(static) layer(theme);` then that's not going to work either. This PR solves that by allowing you to use the `theme(…)` options directly on the `@import` statement: ```css @import 'tailwindcss' theme(static); ``` The only edge case is when you want to use `theme(reference)`. A typical use case is for projects with `<style>` blocks where you want to _reference_ the CSS variables from the theme. If you use `@import 'tailwindcss' theme(reference);`, then all `@theme` blocks will be references and you can reference theme values. This is good. The bad part is that `@import 'tailwindcss';` also includes preflight CSS. This means that we will import the preflight CSS for every `<style>` block. This is probably not what you want. The solution is to use `@reference 'tailwindcss';` instead which strips all of that information and only gives you access to CSS variables. This PR also makes sure that if you use `theme(reference)` on an import that we still throw an error and suggest you use `@reference` instead. This is not a breaking change because right now if you use `@import` with `theme(…)` options it will already throw an error. ### Test plan: 1. Added dedicated tests to make sure we don't throw anymore. 2. Added test to make sure that we _do_ throw when using `theme(reference)` on an import.
1 parent 88b762b commit 3f270d2

File tree

4 files changed

+96
-9
lines changed

4 files changed

+96
-9
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
- Vite: Ensure setups with multiple Vite builds work as expected ([#16631](https://github.com/tailwindlabs/tailwindcss/pull/16631))
1919
- Vite: Ensure Astro production builds contain classes for client-only components ([#16631](https://github.com/tailwindlabs/tailwindcss/pull/16631))
2020
- Vite: Ensure utility classes are read without escaping special characters ([#16631](https://github.com/tailwindlabs/tailwindcss/pull/16631))
21+
- Allow `theme(…)` options when using `@import` ([#16514](https://github.com/tailwindlabs/tailwindcss/pull/16514))
2122

2223
## [4.0.7] - 2025-02-18
2324

packages/tailwindcss/src/index.test.ts

+80-4
Original file line numberDiff line numberDiff line change
@@ -1021,8 +1021,7 @@ describe('sorting', () => {
10211021
})
10221022
})
10231023

1024-
// Parsing theme values from CSS
1025-
describe('Parsing themes values from CSS', () => {
1024+
describe('Parsing theme values from CSS', () => {
10261025
test('Can read values from `@theme`', async () => {
10271026
expect(
10281027
await compileCss(
@@ -2020,7 +2019,44 @@ describe('Parsing themes values from CSS', () => {
20202019
`)
20212020
})
20222021

2023-
test('`@media theme(…)` can only contain `@theme` rules', () => {
2022+
test('`@import "tailwindcss" theme(static)` will always generate theme values, regardless of whether they were used or not', async () => {
2023+
expect(
2024+
await compileCss(
2025+
css`
2026+
@import 'tailwindcss' theme(static);
2027+
`,
2028+
['bg-tomato'],
2029+
{
2030+
async loadStylesheet() {
2031+
return {
2032+
content: css`
2033+
@theme {
2034+
--color-tomato: #e10c04;
2035+
--color-potato: #ac855b;
2036+
--color-primary: var(--primary);
2037+
}
2038+
2039+
@tailwind utilities;
2040+
`,
2041+
base: '',
2042+
}
2043+
},
2044+
},
2045+
),
2046+
).toMatchInlineSnapshot(`
2047+
":root, :host {
2048+
--color-tomato: #e10c04;
2049+
--color-potato: #ac855b;
2050+
--color-primary: var(--primary);
2051+
}
2052+
2053+
.bg-tomato {
2054+
background-color: var(--color-tomato);
2055+
}"
2056+
`)
2057+
})
2058+
2059+
test('`@media theme(reference)` can only contain `@theme` rules', () => {
20242060
return expect(
20252061
compileCss(
20262062
css`
@@ -2034,7 +2070,10 @@ describe('Parsing themes values from CSS', () => {
20342070
['bg-tomato', 'bg-potato', 'bg-avocado'],
20352071
),
20362072
).rejects.toThrowErrorMatchingInlineSnapshot(
2037-
`[Error: Files imported with \`@import "…" theme(…)\` must only contain \`@theme\` blocks.]`,
2073+
`
2074+
[Error: Files imported with \`@import "…" theme(reference)\` must only contain \`@theme\` blocks.
2075+
Use \`@reference "…";\` instead.]
2076+
`,
20382077
)
20392078
})
20402079

@@ -2073,6 +2112,43 @@ describe('Parsing themes values from CSS', () => {
20732112
`)
20742113
})
20752114

2115+
test('`@import "tailwindcss" theme(inline)` theme values added as `inline` are not wrapped in `var(…)` when used as utility values', async () => {
2116+
expect(
2117+
await compileCss(
2118+
css`
2119+
@import 'tailwindcss' theme(inline);
2120+
`,
2121+
['bg-tomato'],
2122+
{
2123+
async loadStylesheet() {
2124+
return {
2125+
content: css`
2126+
@theme {
2127+
--color-tomato: #e10c04;
2128+
--color-potato: #ac855b;
2129+
--color-primary: var(--primary);
2130+
}
2131+
2132+
@tailwind utilities;
2133+
`,
2134+
base: '',
2135+
}
2136+
},
2137+
},
2138+
),
2139+
).toMatchInlineSnapshot(`
2140+
":root, :host {
2141+
--color-tomato: #e10c04;
2142+
--color-potato: #ac855b;
2143+
--color-primary: var(--primary);
2144+
}
2145+
2146+
.bg-tomato {
2147+
background-color: #e10c04;
2148+
}"
2149+
`)
2150+
})
2151+
20762152
test('theme values added as `static` will always be generated, regardless of whether they were used or not', async () => {
20772153
expect(
20782154
await compileCss(

packages/tailwindcss/src/index.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -380,18 +380,24 @@ async function parseCss(
380380

381381
// Handle `@media theme(…)`
382382
//
383-
// We support `@import "tailwindcss/theme" theme(reference)` as a way to
383+
// We support `@import "tailwindcss" theme(reference)` as a way to
384384
// import an external theme file as a reference, which becomes `@media
385385
// theme(reference) { … }` when the `@import` is processed.
386386
else if (param.startsWith('theme(')) {
387387
let themeParams = param.slice(6, -1)
388+
let hasReference = themeParams.includes('reference')
388389

389390
walk(node.nodes, (child) => {
390391
if (child.kind !== 'at-rule') {
391-
throw new Error(
392-
'Files imported with `@import "…" theme(…)` must only contain `@theme` blocks.',
393-
)
392+
if (hasReference) {
393+
throw new Error(
394+
`Files imported with \`@import "…" theme(reference)\` must only contain \`@theme\` blocks.\nUse \`@reference "…";\` instead.`,
395+
)
396+
}
397+
398+
return WalkAction.Continue
394399
}
400+
395401
if (child.name === '@theme') {
396402
child.params += ' ' + themeParams
397403
return WalkAction.Skip

packages/tailwindcss/src/test-utils/run.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { Features, transform } from 'lightningcss'
22
import { compile } from '..'
33

4-
export async function compileCss(css: string, candidates: string[] = [], options = {}) {
4+
export async function compileCss(
5+
css: string,
6+
candidates: string[] = [],
7+
options: Parameters<typeof compile>[1] = {},
8+
) {
59
let { build } = await compile(css, options)
610
return optimizeCss(build(candidates)).trim()
711
}

0 commit comments

Comments
 (0)