mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2026-01-25 16:44:12 +00:00
This PR replaces `@variant` with `@custom-variant` for registering
custom variants via your CSS.
In addition, this PR introduces `@variant` that can be used in your CSS
to use a variant while writing custom CSS.
E.g.:
```css
.btn {
background: white;
@variant dark {
background: black;
}
}
```
Compiles to:
```css
.btn {
background: white;
}
@media (prefers-color-scheme: dark) {
.btn {
background: black;
}
}
```
For backwards compatibility, the `@variant` rules that don't have a body
and are
defined inline:
```css
@variant hocus (&:hover, &:focus);
```
And `@variant` rules that are defined with a body and a `@slot`:
```css
@variant hocus {
&:hover, &:focus {
@slot;
}
}
```
Will automatically be upgraded to `@custom-variant` internally, so no
breaking changes are introduced with this PR.
---
TODO:
- [x] ~~Decide whether we want to allow multiple variants and if so,
what syntax should be used. If not, nesting `@variant <variant> {}` will
be the way to go.~~ Only a single `@variant <variant>` can be used, if
you want to use multiple, nesting should be used:
```css
.foo {
@variant hover {
@variant focus {
color: red;
}
}
}
```
324 lines
6.6 KiB
TypeScript
324 lines
6.6 KiB
TypeScript
import { __unstable__loadDesignSystem } from '@tailwindcss/node'
|
|
import dedent from 'dedent'
|
|
import postcss from 'postcss'
|
|
import { expect, it } from 'vitest'
|
|
import { formatNodes } from './format-nodes'
|
|
import { migratePreflight } from './migrate-preflight'
|
|
import { sortBuckets } from './sort-buckets'
|
|
|
|
const css = dedent
|
|
|
|
async function migrate(input: string) {
|
|
let designSystem = await __unstable__loadDesignSystem(
|
|
css`
|
|
@import 'tailwindcss';
|
|
`,
|
|
{ base: __dirname },
|
|
)
|
|
|
|
return postcss()
|
|
.use(migratePreflight({ designSystem }))
|
|
.use(sortBuckets())
|
|
.use(formatNodes())
|
|
.process(input, { from: expect.getState().testPath })
|
|
.then((result) => result.css)
|
|
}
|
|
|
|
it("should add compatibility CSS after the `@import 'tailwindcss'`", async () => {
|
|
expect(
|
|
await migrate(css`
|
|
@import 'tailwindcss';
|
|
`),
|
|
).toMatchInlineSnapshot(`
|
|
"@import 'tailwindcss';
|
|
|
|
/*
|
|
The default border color has changed to \`currentColor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentColor);
|
|
}
|
|
}"
|
|
`)
|
|
})
|
|
|
|
it('should add the compatibility CSS after the last `@import`', async () => {
|
|
expect(
|
|
await migrate(css`
|
|
@import 'tailwindcss';
|
|
@import './foo.css';
|
|
@import './bar.css';
|
|
`),
|
|
).toMatchInlineSnapshot(`
|
|
"@import 'tailwindcss';
|
|
@import './foo.css';
|
|
@import './bar.css';
|
|
|
|
/*
|
|
The default border color has changed to \`currentColor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentColor);
|
|
}
|
|
}"
|
|
`)
|
|
})
|
|
|
|
it('should add the compatibility CSS after the last import, even if a body-less `@layer` exists', async () => {
|
|
expect(
|
|
await migrate(css`
|
|
@charset "UTF-8";
|
|
@layer foo, bar, baz, base;
|
|
|
|
/**!
|
|
* License header
|
|
*/
|
|
|
|
@import 'tailwindcss';
|
|
@import './foo.css';
|
|
@import './bar.css';
|
|
`),
|
|
).toMatchInlineSnapshot(`
|
|
"@charset "UTF-8";
|
|
@layer foo, bar, baz, base;
|
|
|
|
/**!
|
|
* License header
|
|
*/
|
|
|
|
@import 'tailwindcss';
|
|
@import './foo.css';
|
|
@import './bar.css';
|
|
|
|
/*
|
|
The default border color has changed to \`currentColor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentColor);
|
|
}
|
|
}"
|
|
`)
|
|
})
|
|
|
|
it('should add the compatibility CSS before the first `@layer base` (if the "tailwindcss" import exists)', async () => {
|
|
expect(
|
|
await migrate(css`
|
|
@import 'tailwindcss';
|
|
|
|
@custom-variant foo {
|
|
}
|
|
|
|
@utility bar {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
|
|
@utility baz {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
`),
|
|
).toMatchInlineSnapshot(`
|
|
"@import 'tailwindcss';
|
|
|
|
@custom-variant foo {
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentColor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentColor);
|
|
}
|
|
}
|
|
|
|
@utility bar {
|
|
}
|
|
|
|
@utility baz {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
|
|
@layer base {
|
|
}"
|
|
`)
|
|
})
|
|
|
|
it('should add the compatibility CSS before the first `@layer base` (if the "tailwindcss/preflight" import exists)', async () => {
|
|
expect(
|
|
await migrate(css`
|
|
@import 'tailwindcss/preflight';
|
|
|
|
@custom-variant foo {
|
|
}
|
|
|
|
@utility bar {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
|
|
@utility baz {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
`),
|
|
).toMatchInlineSnapshot(`
|
|
"@import 'tailwindcss/preflight';
|
|
|
|
@custom-variant foo {
|
|
}
|
|
|
|
/*
|
|
The default border color has changed to \`currentColor\` in Tailwind CSS v4,
|
|
so we've added these compatibility styles to make sure everything still
|
|
looks the same as it did with Tailwind CSS v3.
|
|
|
|
If we ever want to remove these styles, we need to add an explicit border
|
|
color utility to any element that depends on these defaults.
|
|
*/
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-gray-200, currentColor);
|
|
}
|
|
}
|
|
|
|
@utility bar {
|
|
}
|
|
|
|
@utility baz {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
|
|
@layer base {
|
|
}"
|
|
`)
|
|
})
|
|
|
|
it('should not add the backwards compatibility CSS when no `@import "tailwindcss"` or `@import "tailwindcss/preflight"` exists', async () => {
|
|
expect(
|
|
await migrate(css`
|
|
@custom-variant foo {
|
|
}
|
|
|
|
@utility bar {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
|
|
@utility baz {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
`),
|
|
).toMatchInlineSnapshot(`
|
|
"@custom-variant foo {
|
|
}
|
|
|
|
@utility bar {
|
|
}
|
|
|
|
@utility baz {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
|
|
@layer base {
|
|
}"
|
|
`)
|
|
})
|
|
|
|
it('should not add the backwards compatibility CSS when another `@import "tailwindcss"` import exists such as theme or utilities', async () => {
|
|
expect(
|
|
await migrate(css`
|
|
@import 'tailwindcss/theme';
|
|
|
|
@custom-variant foo {
|
|
}
|
|
|
|
@utility bar {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
|
|
@utility baz {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
`),
|
|
).toMatchInlineSnapshot(`
|
|
"@import 'tailwindcss/theme';
|
|
|
|
@custom-variant foo {
|
|
}
|
|
|
|
@utility bar {
|
|
}
|
|
|
|
@utility baz {
|
|
}
|
|
|
|
@layer base {
|
|
}
|
|
|
|
@layer base {
|
|
}"
|
|
`)
|
|
})
|