Skip to content

Commit 82034ec

Browse files
authored
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.
1 parent 9e498a3 commit 82034ec

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
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
- Discard matched variants with non-string values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799))
1919
- Show suggestions for known `matchVariant` values ([#18798](https://github.com/tailwindlabs/tailwindcss/pull/18798))
2020
- Migrate `aria` theme keys to `@custom-variant` ([#18815](https://github.com/tailwindlabs/tailwindcss/pull/18815))
21+
- Migrate `data` theme keys to `@custom-variant` ([#18816](https://github.com/tailwindlabs/tailwindcss/pull/18816))
2122

2223
## [4.1.12] - 2025-08-13
2324

integrations/upgrade/js-config.test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,80 @@ test(
10481048
},
10491049
)
10501050

1051+
test(
1052+
'migrate data theme keys to custom variants',
1053+
{
1054+
fs: {
1055+
'package.json': json`
1056+
{
1057+
"dependencies": {
1058+
"tailwindcss": "^3",
1059+
"@tailwindcss/upgrade": "workspace:^"
1060+
}
1061+
}
1062+
`,
1063+
'tailwind.config.ts': ts`
1064+
export default {
1065+
content: {
1066+
relative: true,
1067+
files: ['./src/**/*.html'],
1068+
},
1069+
theme: {
1070+
extend: {
1071+
data: {
1072+
// Automatically handled by bare values
1073+
foo: 'foo',
1074+
1075+
// Not automatically handled by bare values because names differ
1076+
bar: 'baz',
1077+
1078+
// Custom
1079+
checked: 'ui~="checked"',
1080+
},
1081+
},
1082+
},
1083+
}
1084+
`,
1085+
'src/input.css': css`
1086+
@tailwind base;
1087+
@tailwind components;
1088+
@tailwind utilities;
1089+
`,
1090+
},
1091+
},
1092+
async ({ exec, fs, expect }) => {
1093+
await exec('npx @tailwindcss/upgrade')
1094+
1095+
expect(await fs.dumpFiles('src/*.css')).toMatchInlineSnapshot(`
1096+
"
1097+
--- src/input.css ---
1098+
@import 'tailwindcss';
1099+
1100+
@custom-variant data-bar (&[data-baz]);
1101+
@custom-variant data-checked (&[data-ui~="checked"]);
1102+
1103+
/*
1104+
The default border color has changed to \`currentcolor\` in Tailwind CSS v4,
1105+
so we've added these compatibility styles to make sure everything still
1106+
looks the same as it did with Tailwind CSS v3.
1107+
1108+
If we ever want to remove these styles, we need to add an explicit border
1109+
color utility to any element that depends on these defaults.
1110+
*/
1111+
@layer base {
1112+
*,
1113+
::after,
1114+
::before,
1115+
::backdrop,
1116+
::file-selector-button {
1117+
border-color: var(--color-gray-200, currentcolor);
1118+
}
1119+
}
1120+
"
1121+
`)
1122+
},
1123+
)
1124+
10511125
describe('border compatibility', () => {
10521126
test(
10531127
'migrate border compatibility',

packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,18 @@ async function migrateTheme(
152152
}
153153
delete resolvedConfig.theme.aria
154154
}
155+
156+
if ('data' in resolvedConfig.theme) {
157+
for (let [key, value] of Object.entries(resolvedConfig.theme.data ?? {})) {
158+
// Will be handled by bare values if the names match.
159+
// E.g.: `data-foo:flex` should produce `[data-foo]`
160+
if (key === value) continue
161+
162+
// Create custom variant
163+
variants.set(`data-${key}`, `&[data-${value}]`)
164+
}
165+
delete resolvedConfig.theme.data
166+
}
155167
}
156168

157169
// Convert theme values to CSS custom properties
@@ -223,7 +235,13 @@ async function migrateTheme(
223235

224236
if (variants.size > 0) {
225237
css += '\n@tw-bucket custom-variant {\n'
238+
239+
let previousRoot = ''
226240
for (let [name, selector] of variants) {
241+
let root = name.split('-')[0]
242+
if (previousRoot !== root) css += '\n'
243+
previousRoot = root
244+
227245
css += `@custom-variant ${name} (${selector});\n`
228246
}
229247
css += '}\n'
@@ -389,7 +407,7 @@ const ALLOWED_THEME_KEYS = [
389407
// Used by @tailwindcss/container-queries
390408
'containers',
391409
]
392-
const BLOCKED_THEME_KEYS = ['supports', 'data']
410+
const BLOCKED_THEME_KEYS = ['supports']
393411
function onlyAllowedThemeValues(theme: ThemeConfig): boolean {
394412
for (let key of Object.keys(theme)) {
395413
if (!ALLOWED_THEME_KEYS.includes(key)) {

0 commit comments

Comments
 (0)