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.
This commit is contained in:
parent
88b762b539
commit
3f270d2d9b
4 changed files with 96 additions and 9 deletions
|
|
@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Vite: Ensure setups with multiple Vite builds work as expected ([#16631](https://github.com/tailwindlabs/tailwindcss/pull/16631))
|
||||
- Vite: Ensure Astro production builds contain classes for client-only components ([#16631](https://github.com/tailwindlabs/tailwindcss/pull/16631))
|
||||
- Vite: Ensure utility classes are read without escaping special characters ([#16631](https://github.com/tailwindlabs/tailwindcss/pull/16631))
|
||||
- Allow `theme(…)` options when using `@import` ([#16514](https://github.com/tailwindlabs/tailwindcss/pull/16514))
|
||||
|
||||
## [4.0.7] - 2025-02-18
|
||||
|
||||
|
|
|
|||
|
|
@ -1021,8 +1021,7 @@ describe('sorting', () => {
|
|||
})
|
||||
})
|
||||
|
||||
// Parsing theme values from CSS
|
||||
describe('Parsing themes values from CSS', () => {
|
||||
describe('Parsing theme values from CSS', () => {
|
||||
test('Can read values from `@theme`', async () => {
|
||||
expect(
|
||||
await compileCss(
|
||||
|
|
@ -2020,7 +2019,44 @@ describe('Parsing themes values from CSS', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
test('`@media theme(…)` can only contain `@theme` rules', () => {
|
||||
test('`@import "tailwindcss" theme(static)` will always generate theme values, regardless of whether they were used or not', async () => {
|
||||
expect(
|
||||
await compileCss(
|
||||
css`
|
||||
@import 'tailwindcss' theme(static);
|
||||
`,
|
||||
['bg-tomato'],
|
||||
{
|
||||
async loadStylesheet() {
|
||||
return {
|
||||
content: css`
|
||||
@theme {
|
||||
--color-tomato: #e10c04;
|
||||
--color-potato: #ac855b;
|
||||
--color-primary: var(--primary);
|
||||
}
|
||||
|
||||
@tailwind utilities;
|
||||
`,
|
||||
base: '',
|
||||
}
|
||||
},
|
||||
},
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
":root, :host {
|
||||
--color-tomato: #e10c04;
|
||||
--color-potato: #ac855b;
|
||||
--color-primary: var(--primary);
|
||||
}
|
||||
|
||||
.bg-tomato {
|
||||
background-color: var(--color-tomato);
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('`@media theme(reference)` can only contain `@theme` rules', () => {
|
||||
return expect(
|
||||
compileCss(
|
||||
css`
|
||||
|
|
@ -2034,7 +2070,10 @@ describe('Parsing themes values from CSS', () => {
|
|||
['bg-tomato', 'bg-potato', 'bg-avocado'],
|
||||
),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`[Error: Files imported with \`@import "…" theme(…)\` must only contain \`@theme\` blocks.]`,
|
||||
`
|
||||
[Error: Files imported with \`@import "…" theme(reference)\` must only contain \`@theme\` blocks.
|
||||
Use \`@reference "…";\` instead.]
|
||||
`,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -2073,6 +2112,43 @@ describe('Parsing themes values from CSS', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
test('`@import "tailwindcss" theme(inline)` theme values added as `inline` are not wrapped in `var(…)` when used as utility values', async () => {
|
||||
expect(
|
||||
await compileCss(
|
||||
css`
|
||||
@import 'tailwindcss' theme(inline);
|
||||
`,
|
||||
['bg-tomato'],
|
||||
{
|
||||
async loadStylesheet() {
|
||||
return {
|
||||
content: css`
|
||||
@theme {
|
||||
--color-tomato: #e10c04;
|
||||
--color-potato: #ac855b;
|
||||
--color-primary: var(--primary);
|
||||
}
|
||||
|
||||
@tailwind utilities;
|
||||
`,
|
||||
base: '',
|
||||
}
|
||||
},
|
||||
},
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
":root, :host {
|
||||
--color-tomato: #e10c04;
|
||||
--color-potato: #ac855b;
|
||||
--color-primary: var(--primary);
|
||||
}
|
||||
|
||||
.bg-tomato {
|
||||
background-color: #e10c04;
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('theme values added as `static` will always be generated, regardless of whether they were used or not', async () => {
|
||||
expect(
|
||||
await compileCss(
|
||||
|
|
|
|||
|
|
@ -380,18 +380,24 @@ async function parseCss(
|
|||
|
||||
// Handle `@media theme(…)`
|
||||
//
|
||||
// We support `@import "tailwindcss/theme" theme(reference)` as a way to
|
||||
// We support `@import "tailwindcss" theme(reference)` as a way to
|
||||
// import an external theme file as a reference, which becomes `@media
|
||||
// theme(reference) { … }` when the `@import` is processed.
|
||||
else if (param.startsWith('theme(')) {
|
||||
let themeParams = param.slice(6, -1)
|
||||
let hasReference = themeParams.includes('reference')
|
||||
|
||||
walk(node.nodes, (child) => {
|
||||
if (child.kind !== 'at-rule') {
|
||||
throw new Error(
|
||||
'Files imported with `@import "…" theme(…)` must only contain `@theme` blocks.',
|
||||
)
|
||||
if (hasReference) {
|
||||
throw new Error(
|
||||
`Files imported with \`@import "…" theme(reference)\` must only contain \`@theme\` blocks.\nUse \`@reference "…";\` instead.`,
|
||||
)
|
||||
}
|
||||
|
||||
return WalkAction.Continue
|
||||
}
|
||||
|
||||
if (child.name === '@theme') {
|
||||
child.params += ' ' + themeParams
|
||||
return WalkAction.Skip
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { Features, transform } from 'lightningcss'
|
||||
import { compile } from '..'
|
||||
|
||||
export async function compileCss(css: string, candidates: string[] = [], options = {}) {
|
||||
export async function compileCss(
|
||||
css: string,
|
||||
candidates: string[] = [],
|
||||
options: Parameters<typeof compile>[1] = {},
|
||||
) {
|
||||
let { build } = await compile(css, options)
|
||||
return optimizeCss(build(candidates)).trim()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue