Migrate data theme keys (#18816)

This PR is similar to and a follow up of #18815, but this time to
migrate the `data` theme keys.

Let's imagine you have the following Tailwind CSS v3 configuration:
```ts
export default {
  content: ['./src/**/*.html'],
  theme: {
    extend: {
      data: {
        // Automatically handled by bare values
        foo: 'foo',
    //  ^^^   ^^^       ← same names

        // Not automatically handled by bare values
        bar: 'baz',
    //  ^^^   ^^^       ← different names

        // Completely custom
        checked: 'ui~="checked"',
      },
    },
  },
}
```

Then we would generate the following Tailwind CSS v4 CSS:

```css
@custom-variant data-bar (&[data-baz]);
@custom-variant data-checked (&[data-ui~="checked"]);
```

Notice how we didn't generate a custom variant for `data-foo` because
those are automatically handled by bare values.
This commit is contained in:
Robin Malfait 2025-08-28 16:45:18 +02:00 committed by GitHub
parent 9e498a3e78
commit 82034ec327
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 94 additions and 1 deletions

View File

@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Discard matched variants with non-string values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799))
- Show suggestions for known `matchVariant` values ([#18798](https://github.com/tailwindlabs/tailwindcss/pull/18798))
- Migrate `aria` theme keys to `@custom-variant` ([#18815](https://github.com/tailwindlabs/tailwindcss/pull/18815))
- Migrate `data` theme keys to `@custom-variant` ([#18816](https://github.com/tailwindlabs/tailwindcss/pull/18816))
## [4.1.12] - 2025-08-13

View File

@ -1048,6 +1048,80 @@ test(
},
)
test(
'migrate data theme keys to custom variants',
{
fs: {
'package.json': json`
{
"dependencies": {
"tailwindcss": "^3",
"@tailwindcss/upgrade": "workspace:^"
}
}
`,
'tailwind.config.ts': ts`
export default {
content: {
relative: true,
files: ['./src/**/*.html'],
},
theme: {
extend: {
data: {
// Automatically handled by bare values
foo: 'foo',
// Not automatically handled by bare values because names differ
bar: 'baz',
// Custom
checked: 'ui~="checked"',
},
},
},
}
`,
'src/input.css': css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`,
},
},
async ({ exec, fs, expect }) => {
await exec('npx @tailwindcss/upgrade')
expect(await fs.dumpFiles('src/*.css')).toMatchInlineSnapshot(`
"
--- src/input.css ---
@import 'tailwindcss';
@custom-variant data-bar (&[data-baz]);
@custom-variant data-checked (&[data-ui~="checked"]);
/*
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);
}
}
"
`)
},
)
describe('border compatibility', () => {
test(
'migrate border compatibility',

View File

@ -152,6 +152,18 @@ async function migrateTheme(
}
delete resolvedConfig.theme.aria
}
if ('data' in resolvedConfig.theme) {
for (let [key, value] of Object.entries(resolvedConfig.theme.data ?? {})) {
// Will be handled by bare values if the names match.
// E.g.: `data-foo:flex` should produce `[data-foo]`
if (key === value) continue
// Create custom variant
variants.set(`data-${key}`, `&[data-${value}]`)
}
delete resolvedConfig.theme.data
}
}
// Convert theme values to CSS custom properties
@ -223,7 +235,13 @@ async function migrateTheme(
if (variants.size > 0) {
css += '\n@tw-bucket custom-variant {\n'
let previousRoot = ''
for (let [name, selector] of variants) {
let root = name.split('-')[0]
if (previousRoot !== root) css += '\n'
previousRoot = root
css += `@custom-variant ${name} (${selector});\n`
}
css += '}\n'
@ -389,7 +407,7 @@ const ALLOWED_THEME_KEYS = [
// Used by @tailwindcss/container-queries
'containers',
]
const BLOCKED_THEME_KEYS = ['supports', 'data']
const BLOCKED_THEME_KEYS = ['supports']
function onlyAllowedThemeValues(theme: ThemeConfig): boolean {
for (let key of Object.keys(theme)) {
if (!ALLOWED_THEME_KEYS.includes(key)) {