Add legacy dark mode option

This commit is contained in:
Jordan Pittman 2023-12-21 16:27:18 -05:00
parent a94a21257a
commit 543dacf81a
4 changed files with 155 additions and 4 deletions

View file

@ -242,6 +242,9 @@ export let variantPlugins = {
if (mode === 'class') {
addVariant('dark', `:is(:where(${className}) &)`)
} else if (mode === 'legacy') {
// Exists for pre v3.4 compatibility
addVariant('dark', `:is(${className} &)`)
} else if (mode === 'media') {
addVariant('dark', '@media (prefers-color-scheme: dark)')
} else if (mode === 'variant') {

View file

@ -765,6 +765,27 @@ function resolvePlugins(context, root) {
variantPlugins['forcedColorsVariants'],
]
// This is a compatibility fix for the pre 3.4 dark mode behavior
// You can enable the old behavior by setting `darkMode: ['legacy', selector]`
let isLegacyDarkMode = context.tailwindConfig.darkMode === 'legacy'
|| (Array.isArray(context.tailwindConfig.darkMode) && context.tailwindConfig.darkMode[0] === 'legacy')
if (isLegacyDarkMode) {
afterVariants = [
variantPlugins['supportsVariants'],
variantPlugins['directionVariants'],
variantPlugins['reducedMotionVariants'],
variantPlugins['prefersContrastVariants'],
variantPlugins['printVariant'],
variantPlugins['darkVariants'],
variantPlugins['screenVariants'],
variantPlugins['orientationVariants'],
// Forced colors didn't exist before 3.4 so it can keep its position at the end
variantPlugins['forcedColorsVariants'],
]
}
return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
}

View file

@ -120,6 +120,108 @@ it('should default to the `media` mode when mode is set to `false`', () => {
})
})
it('should support legacy dark mode behavior', () => {
let config = {
darkMode: 'legacy',
content: [{ raw: html`<div class="dark:font-bold"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
:is(.dark .dark\:font-bold) {
font-weight: 700;
}
`)
})
})
it('should support custom classes with legacy dark mode', () => {
let config = {
darkMode: ['legacy', '.my-dark'],
content: [{ raw: html`<div class="dark:font-bold"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
:is(.my-dark .dark\:font-bold) {
font-weight: 700;
}
`)
})
})
it('should use legacy sorting when using darkMode: legacy', () => {
let config = {
darkMode: 'legacy',
content: [{ raw: html`<div class="dark:text-green-100 hover:text-green-200 lg:text-green-300"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.hover\:text-green-200:hover {
--tw-text-opacity: 1;
color: rgb(187 247 208 / var(--tw-text-opacity));
}
:is(.dark .dark\:text-green-100) {
--tw-text-opacity: 1;
color: rgb(220 252 231 / var(--tw-text-opacity));
}
@media (min-width: 1024px) {
.lg\:text-green-300 {
--tw-text-opacity: 1;
color: rgb(134 239 172 / var(--tw-text-opacity));
}
}
`)
})
})
it('should use modern sorting otherwise', () => {
let config = {
darkMode: 'class',
content: [{ raw: html`<div class="dark:text-green-100 hover:text-green-200 lg:text-green-300"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.hover\:text-green-200:hover {
--tw-text-opacity: 1;
color: rgb(187 247 208 / var(--tw-text-opacity));
}
@media (min-width: 1024px) {
.lg\:text-green-300 {
--tw-text-opacity: 1;
color: rgb(134 239 172 / var(--tw-text-opacity));
}
}
:is(:where(.dark) .dark\:text-green-100) {
--tw-text-opacity: 1;
color: rgb(220 252 231 / var(--tw-text-opacity));
}
`)
})
})
it('should allow customization of the dark mode variant', () => {
let config = {
darkMode: ['variant', '&:not(.light *)'],
@ -128,17 +230,37 @@ it('should allow customization of the dark mode variant', () => {
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.dark\:font-bold:not(.light *) {
font-weight: 700;
}
`)
})
})
it('should support parallel selectors for the dark mode variant', () => {
let config = {
darkMode: ['variant', ['&:not(.light *)', '&:not(.extralight *)']],
content: [{ raw: html`<div class="dark:font-bold"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.dark\:font-bold:not(.light *) {
font-weight: 700;
}
.dark\:font-bold:not(.extralight *) {
font-weight: 700;
}
`)
})
})

7
types/config.d.ts vendored
View file

@ -75,8 +75,13 @@ type DarkModeConfig =
| 'class'
// Use the `class` strategy with a custom class instead of `.dark`.
| ['class', string]
// Use the `legacy` strategy — same as `class` but restores pre-v3.4 behavior
| 'legacy'
// Use the `legacy` strategy with a custom class instead of `.dark`
| ['legacy', string]
// Use the `variant` strategy, which allows you to completely customize the selector
| ['variant', string]
// It takes a string or an array of strings, which are passed directly to `addVariant()`
| ['variant', string | string[]]
type Screen = { raw: string } | { min: string } | { max: string } | { min: string; max: string }
type ScreensConfig = string[] | KeyValuePair<string, string | Screen | Screen[]>