Upgrade: Handle darkMode value with block syntax (#16507)

Closes #16171

This PR handles `darkMode` variant configs containing braces (so
creating sub-rules) the same way we handle it in the interop layer.
Since the interop layer runs inside the `addVariant` API that we do not
run here, I instead oped to copy the one liner.

## Test plan

Updated one of the migration tests to include a rule that wasn't working
before. Ensured the new output works via
https://play.tailwindcss.com/nR99uhKtv3
This commit is contained in:
Philipp Spiess 2025-02-14 16:48:52 +01:00 • committed by GitHub
parent 63b9be9483
commit a1e083afc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 50 additions and 2 deletions

View file

@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure drop shadow utilities don't inherit unexpectedly ([#16471](https://github.com/tailwindlabs/tailwindcss/pull/16471))
- Export backwards compatible config and plugin types from `tailwindcss/plugin` ([#16505](https://github.com/tailwindlabs/tailwindcss/pull/16505))
- Upgrade: Report errors when updating dependencies ([#16504](https://github.com/tailwindlabs/tailwindcss/pull/16504))
- Upgrade: Ensure a `darkMode` JS config setting with block syntax converts to use `@slot` ([#16507](https://github.com/tailwindlabs/tailwindcss/pull/16507))
## [4.0.6] - 2025-02-10

View file

@ -318,6 +318,14 @@ test(
import customPlugin from './custom-plugin'
export default {
darkMode: [
'variant',
[
'@media not print { .dark & }',
'@media not eink { .dark & }',
'&:where(.dark, .dark *)',
],
],
plugins: [
typography,
customPlugin({
@ -379,6 +387,22 @@ test(
is-arr-mixed: null, true, false, 1234567, 1.35, 'foo', 'bar', 'true';
}
@custom-variant dark {
@media not print {
.dark & {
@slot;
}
}
@media not eink {
.dark & {
@slot;
}
}
&:where(.dark, .dark *) {
@slot;
}
}
/*
The default border color has changed to \`currentColor\` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still

View file

@ -196,7 +196,7 @@ async function migrateTheme(
}
function migrateDarkMode(unresolvedConfig: Config & { darkMode: any }): string {
let variant: string = ''
let variant: string | string[] = ''
let addVariant = (_name: string, _variant: string) => (variant = _variant)
let config = () => unresolvedConfig.darkMode
darkModePlugin({ config, addVariant })
@ -204,7 +204,30 @@ function migrateDarkMode(unresolvedConfig: Config & { darkMode: any }): string {
if (variant === '') {
return ''
}
return `\n@tw-bucket custom-variant {\n@custom-variant dark (${variant});\n}\n`
if (!Array.isArray(variant)) {
variant = [variant]
}
if (variant.length === 1 && !variant[0].includes('{')) {
return `\n@tw-bucket custom-variant {\n@custom-variant dark (${variant[0]});\n}\n`
}
let customVariant = ''
for (let variantName of variant) {
// Convert to the block syntax if a block is used
if (variantName.includes('{')) {
customVariant += variantName.replace('}', '{ @slot }}') + '\n'
} else {
customVariant += variantName + '{ @slot }\n'
}
}
if (customVariant !== '') {
return `\n@tw-bucket custom-variant {\n@custom-variant dark {${customVariant}};\n}\n`
}
return ''
}
// Returns a string identifier used to section theme declarations