From 1544ef576d48b9b20bbee14deb6e6bf8206ed242 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 21 Dec 2023 10:34:09 -0500 Subject: [PATCH 01/24] Add dark mode variant option --- src/corePlugins.js | 14 ++++++++++++++ tests/dark-mode.test.js | 23 +++++++++++++++++++++++ types/config.d.ts | 2 ++ 3 files changed, 39 insertions(+) diff --git a/src/corePlugins.js b/src/corePlugins.js index 6b2f88ffcf82..4f396b5918a9 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -225,12 +225,26 @@ export let variantPlugins = { 'Change `darkMode` to `media` or remove it entirely.', 'https://tailwindcss.com/docs/upgrade-guide#remove-dark-mode-configuration', ]) + } else if (mode === 'variant' && className === '.dark') { + mode = false + log.warn('darkmode-variant-without-selector', [ + 'darkMode: "variant" was used without a provided selector.', + 'Change to `darkMode: ["variant", ".your-selector &"]` ', + ]) + } else if (mode === 'variant' && !className.includes('&')) { + mode = false + log.warn('darkmode-variant-without-ampersand', [ + 'A custom dark mode selector was used without an `&` — a `&` is required to correctly scope the custom selector.', + 'Change to `darkMode: ["variant", ".your-selector &"]` ', + ]) } if (mode === 'class') { addVariant('dark', `:is(:where(${className}) &)`) } else if (mode === 'media') { addVariant('dark', '@media (prefers-color-scheme: dark)') + } else if (mode === 'variant') { + addVariant('dark', className) } }, diff --git a/tests/dark-mode.test.js b/tests/dark-mode.test.js index 276c6c9e3c87..924a9f33532c 100644 --- a/tests/dark-mode.test.js +++ b/tests/dark-mode.test.js @@ -119,3 +119,26 @@ it('should default to the `media` mode when mode is set to `false`', () => { `) }) }) + +it('should allow customization of the dark mode variant', () => { + let config = { + darkMode: ['variant', '&:not(.light *)'], + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + 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; + } + `) + }) +}) diff --git a/types/config.d.ts b/types/config.d.ts index 051ccad5b47f..69ea448dfde9 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -75,6 +75,8 @@ type DarkModeConfig = | 'class' // Use the `class` strategy with a custom class instead of `.dark`. | ['class', string] + // Use the `variant` strategy, which allows you to completely customize the selector + | ['variant', string] type Screen = { raw: string } | { min: string } | { max: string } | { min: string; max: string } type ScreensConfig = string[] | KeyValuePair From a94a21257ac64b5cfb8520e2b27da566d8ccba90 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 21 Dec 2023 14:34:17 -0500 Subject: [PATCH 02/24] Tweak warning messages --- src/corePlugins.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/corePlugins.js b/src/corePlugins.js index 4f396b5918a9..3d3c599e7b77 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -229,13 +229,14 @@ export let variantPlugins = { mode = false log.warn('darkmode-variant-without-selector', [ 'darkMode: "variant" was used without a provided selector.', - 'Change to `darkMode: ["variant", ".your-selector &"]` ', + 'When using `variant` for `darkMode`, you must provide a selector.', + 'Example: `darkMode: ["variant", ".your-selector &"]`', ]) } else if (mode === 'variant' && !className.includes('&')) { mode = false log.warn('darkmode-variant-without-ampersand', [ - 'A custom dark mode selector was used without an `&` — a `&` is required to correctly scope the custom selector.', - 'Change to `darkMode: ["variant", ".your-selector &"]` ', + 'When using `variant` for `darkMode`, your selector must contain `&`.', + 'Example `darkMode: ["variant", ".your-selector &"]`', ]) } From 543dacf81a460079f974c020460c8df438988e2f Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 21 Dec 2023 16:27:18 -0500 Subject: [PATCH 03/24] Add legacy dark mode option --- src/corePlugins.js | 3 + src/lib/setupContextUtils.js | 21 ++++++ tests/dark-mode.test.js | 128 ++++++++++++++++++++++++++++++++++- types/config.d.ts | 7 +- 4 files changed, 155 insertions(+), 4 deletions(-) diff --git a/src/corePlugins.js b/src/corePlugins.js index 3d3c599e7b77..9b31afe0e3f7 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -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') { diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index e677399b8b8e..1095145eb06f 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -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] } diff --git a/tests/dark-mode.test.js b/tests/dark-mode.test.js index 924a9f33532c..966bd52adbcd 100644 --- a/tests/dark-mode.test.js +++ b/tests/dark-mode.test.js @@ -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`
` }], + 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`
` }], + 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`
` }], + 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`
` }], + 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`
` }], + 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; + } + `) + }) +}) diff --git a/types/config.d.ts b/types/config.d.ts index 69ea448dfde9..87a7fdfd2e33 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -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 From c055aa4017b8ef10759ff508e56be0ed1dec87cf Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 21 Dec 2023 16:38:25 -0500 Subject: [PATCH 04/24] wip --- src/corePlugins.js | 47 +++++++++++++++++++++++++++++------------ tests/dark-mode.test.js | 21 +++++++++++++++++- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/corePlugins.js b/src/corePlugins.js index 9b31afe0e3f7..b88d1a90f8eb 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -225,19 +225,40 @@ export let variantPlugins = { 'Change `darkMode` to `media` or remove it entirely.', 'https://tailwindcss.com/docs/upgrade-guide#remove-dark-mode-configuration', ]) - } else if (mode === 'variant' && className === '.dark') { - mode = false - log.warn('darkmode-variant-without-selector', [ - 'darkMode: "variant" was used without a provided selector.', - 'When using `variant` for `darkMode`, you must provide a selector.', - 'Example: `darkMode: ["variant", ".your-selector &"]`', - ]) - } else if (mode === 'variant' && !className.includes('&')) { - mode = false - log.warn('darkmode-variant-without-ampersand', [ - 'When using `variant` for `darkMode`, your selector must contain `&`.', - 'Example `darkMode: ["variant", ".your-selector &"]`', - ]) + } + + if (mode === 'variant') { + let formats + if (Array.isArray(className)) { + formats = className + } else if (typeof className === 'function') { + formats = className + } else if (typeof className === 'string') { + formats = [className] + } + + // TODO: We could also add these warnings if the user passes a function that returns string | string[] + // But this is an advanced enough use case that it's probably not necessary + if (Array.isArray(formats)) { + for (let format of formats) { + if (format === '.dark') { + mode = false + log.warn('darkmode-variant-without-selector', [ + 'darkMode: "variant" was used without a provided selector.', + 'When using `variant` for `darkMode`, you must provide a selector.', + 'Example: `darkMode: ["variant", ".your-selector &"]`', + ]) + } else if (!format.includes('&')) { + mode = false + log.warn('darkmode-variant-without-ampersand', [ + 'When using `variant` for `darkMode`, your selector must contain `&`.', + 'Example `darkMode: ["variant", ".your-selector &"]`', + ]) + } + } + } + + className = formats } if (mode === 'class') { diff --git a/tests/dark-mode.test.js b/tests/dark-mode.test.js index 966bd52adbcd..69ce41fcd1fd 100644 --- a/tests/dark-mode.test.js +++ b/tests/dark-mode.test.js @@ -255,9 +255,28 @@ it('should support parallel selectors for the dark mode variant', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` - .dark\:font-bold:not(.light *) { + .dark\:font-bold:not(.light *), + .dark\:font-bold:not(.extralight *) { font-weight: 700; } + `) + }) +}) + +it('should support fn selectors for the dark mode variant', () => { + let config = { + darkMode: ['variant', () => ['&:not(.light *)', '&:not(.extralight *)']], + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .dark\:font-bold:not(.light *), .dark\:font-bold:not(.extralight *) { font-weight: 700; } From 13ad6ccabf4c6415c1eff70ac9f979ab80ecf197 Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:44:46 -0500 Subject: [PATCH 05/24] Use `class` for legacy behavior, `selector` for new behavior --- src/corePlugins.js | 11 +++--- src/lib/setupContextUtils.js | 11 ++++-- tests/apply.test.js | 16 ++++++-- tests/dark-mode.test.js | 39 ++++++++++++------- .../__snapshots__/darkVariants.test.js.snap | 4 +- 5 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/corePlugins.js b/src/corePlugins.js index b88d1a90f8eb..7408fe640c14 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -244,7 +244,6 @@ export let variantPlugins = { if (format === '.dark') { mode = false log.warn('darkmode-variant-without-selector', [ - 'darkMode: "variant" was used without a provided selector.', 'When using `variant` for `darkMode`, you must provide a selector.', 'Example: `darkMode: ["variant", ".your-selector &"]`', ]) @@ -261,15 +260,15 @@ export let variantPlugins = { className = formats } - if (mode === 'class') { - addVariant('dark', `:is(:where(${className}) &)`) - } else if (mode === 'legacy') { - // Exists for pre v3.4 compatibility - addVariant('dark', `:is(${className} &)`) + if (mode === 'selector') { + addVariant('dark', [`:is(:where(${className}) &)`, `&:where(${className})`]) } else if (mode === 'media') { addVariant('dark', '@media (prefers-color-scheme: dark)') } else if (mode === 'variant') { addVariant('dark', className) + } else if (mode === 'class') { + // Exists for pre v3.4 compatibility + addVariant('dark', `:is(${className} &)`) } }, diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 1095145eb06f..ad1f0f9e00ae 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -757,18 +757,21 @@ function resolvePlugins(context, root) { variantPlugins['supportsVariants'], variantPlugins['reducedMotionVariants'], variantPlugins['prefersContrastVariants'], - variantPlugins['printVariant'], variantPlugins['screenVariants'], variantPlugins['orientationVariants'], variantPlugins['directionVariants'], variantPlugins['darkVariants'], variantPlugins['forcedColorsVariants'], + variantPlugins['printVariant'], ] // This is a compatibility fix for the pre 3.4 dark mode behavior + // The `class` strategy is deprecated in favor of `selector` which has the new 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') + let isLegacyDarkMode = + context.tailwindConfig.darkMode === 'class' || + (Array.isArray(context.tailwindConfig.darkMode) && + context.tailwindConfig.darkMode[0] === 'class') if (isLegacyDarkMode) { afterVariants = [ @@ -776,8 +779,8 @@ function resolvePlugins(context, root) { variantPlugins['directionVariants'], variantPlugins['reducedMotionVariants'], variantPlugins['prefersContrastVariants'], - variantPlugins['printVariant'], variantPlugins['darkVariants'], + variantPlugins['printVariant'], variantPlugins['screenVariants'], variantPlugins['orientationVariants'], diff --git a/tests/apply.test.js b/tests/apply.test.js index 6c79db2b74e1..f9c46d3714fe 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -1972,7 +1972,7 @@ it('should maintain the correct selector when applying other utilities', () => { it('pseudo elements inside apply are moved outside of :is() or :has()', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html`
`, @@ -2017,8 +2017,11 @@ it('pseudo elements inside apply are moved outside of :is() or :has()', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` :is(:where(.dark) .foo):before, + .foo:where(.dark):before, :is(:where([dir='rtl']) :is(:where(.dark) .bar)):before, - :is(:where([dir='rtl']) :is(:where(.dark) .baz:hover)):before { + :is(:where([dir='rtl']) .bar:where(.dark)):before, + :is(:where([dir='rtl']) :is(:where(.dark) .baz:hover)):before, + :is(:where([dir='rtl']) .baz:hover:where(.dark)):before { background-color: #000; } :-webkit-any( @@ -2029,7 +2032,14 @@ it('pseudo elements inside apply are moved outside of :is() or :has()', () => { :is(:where([dir='rtl']) :is(:where(.dark) .qux))::file-selector-button:hover { background-color: #000; } - :is(:where([dir='rtl']) :is(:where(.dark) .steve):hover):before { + :-webkit-any(:where([dir='rtl']) .qux)::-webkit-file-upload-button:hover:where() { + background-color: #000; + } + :is(:where([dir='rtl']) .qux)::file-selector-button:hover:where() { + background-color: #000; + } + :is(:where([dir='rtl']) :is(:where(.dark) .steve):hover):before, + :is(:where([dir='rtl']) .steve:where(.dark):hover):before { background-color: #000; } :-webkit-any( diff --git a/tests/dark-mode.test.js b/tests/dark-mode.test.js index 69ce41fcd1fd..1136c5bd744a 100644 --- a/tests/dark-mode.test.js +++ b/tests/dark-mode.test.js @@ -2,7 +2,7 @@ import { run, html, css, defaults } from './util/run' it('should be possible to use the darkMode "class" mode', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [{ raw: html`
` }], corePlugins: { preflight: false }, } @@ -16,7 +16,8 @@ it('should be possible to use the darkMode "class" mode', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` ${defaults} - :is(:where(.dark) .dark\:font-bold) { + :is(:where(.dark) .dark\:font-bold), + .dark\:font-bold:where(.dark) { font-weight: 700; } `) @@ -25,7 +26,7 @@ it('should be possible to use the darkMode "class" mode', () => { it('should be possible to change the class name', () => { let config = { - darkMode: ['class', '.test-dark'], + darkMode: ['selector', '.test-dark'], content: [{ raw: html`
` }], corePlugins: { preflight: false }, } @@ -39,7 +40,8 @@ it('should be possible to change the class name', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` ${defaults} - :is(:where(.test-dark) .dark\:font-bold) { + :is(:where(.test-dark) .dark\:font-bold), + .dark\:font-bold:where(.test-dark) { font-weight: 700; } `) @@ -120,9 +122,9 @@ it('should default to the `media` mode when mode is set to `false`', () => { }) }) -it('should support legacy dark mode behavior', () => { +it('should support the deprecated `class` dark mode behavior', () => { let config = { - darkMode: 'legacy', + darkMode: 'class', content: [{ raw: html`
` }], corePlugins: { preflight: false }, } @@ -140,9 +142,9 @@ it('should support legacy dark mode behavior', () => { }) }) -it('should support custom classes with legacy dark mode', () => { +it('should support custom classes with deprecated `class` dark mode', () => { let config = { - darkMode: ['legacy', '.my-dark'], + darkMode: ['class', '.my-dark'], content: [{ raw: html`
` }], corePlugins: { preflight: false }, } @@ -160,10 +162,14 @@ it('should support custom classes with legacy dark mode', () => { }) }) -it('should use legacy sorting when using darkMode: legacy', () => { +it('should use legacy sorting when using `darkMode: class`', () => { let config = { - darkMode: 'legacy', - content: [{ raw: html`
` }], + darkMode: 'class', + content: [ + { + raw: html`
`, + }, + ], corePlugins: { preflight: false }, } @@ -193,8 +199,12 @@ it('should use legacy sorting when using darkMode: legacy', () => { it('should use modern sorting otherwise', () => { let config = { - darkMode: 'class', - content: [{ raw: html`
` }], + darkMode: 'selector', + content: [ + { + raw: html`
`, + }, + ], corePlugins: { preflight: false }, } @@ -214,7 +224,8 @@ it('should use modern sorting otherwise', () => { color: rgb(134 239 172 / var(--tw-text-opacity)); } } - :is(:where(.dark) .dark\:text-green-100) { + :is(:where(.dark) .dark\:text-green-100), + .dark\:text-green-100:where(.dark) { --tw-text-opacity: 1; color: rgb(220 252 231 / var(--tw-text-opacity)); } diff --git a/tests/plugins/variants/__snapshots__/darkVariants.test.js.snap b/tests/plugins/variants/__snapshots__/darkVariants.test.js.snap index 5414de562dbf..0a3dda916fa9 100644 --- a/tests/plugins/variants/__snapshots__/darkVariants.test.js.snap +++ b/tests/plugins/variants/__snapshots__/darkVariants.test.js.snap @@ -12,7 +12,7 @@ exports[`should test the 'darkVariants' plugin 1`] = ` exports[`should test the 'darkVariants' plugin 2`] = ` " -:is(:where(.dark) .dark\\:flex) { +:is(.dark .dark\\:flex) { display: flex; } " @@ -20,7 +20,7 @@ exports[`should test the 'darkVariants' plugin 2`] = ` exports[`should test the 'darkVariants' plugin 3`] = ` " -:is(:where(.my-dark-mode) .dark\\:flex) { +:is(.my-dark-mode .dark\\:flex) { display: flex; } " From c221bbcaa90dbf0da461cbe0800641139637d4f3 Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:44:58 -0500 Subject: [PATCH 06/24] Add simplified failing apply/where test case --- tests/apply.test.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/apply.test.js b/tests/apply.test.js index f9c46d3714fe..9759f3bfc407 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -2063,6 +2063,39 @@ it('pseudo elements inside apply are moved outside of :is() or :has()', () => { }) }) +it('for some reason this produces empty where clauses', () => { + let config = { + darkMode: 'selector', + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addVariant }) { + addVariant('wut', `&:where(.wut)`) + }, + ], + } + + let input = css` + .bob::file-selector-button { + @apply wut:bg-black/100; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bob::-webkit-file-upload-button:where(.wut) { + background-color: #000; + } + .bob::file-selector-button:where(.wut) { + background-color: #000; + } + `) + }) +}) + test('::ng-deep, ::deep, ::v-deep pseudo elements are left alone', () => { let config = { darkMode: 'class', From 24d1923293d8784664cf334e8f02bebc676e359c Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:58:41 -0500 Subject: [PATCH 07/24] Switch to `where` list, apply changes to `dir` variants --- src/corePlugins.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/corePlugins.js b/src/corePlugins.js index 7408fe640c14..0ab36d57e71a 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -206,8 +206,8 @@ export let variantPlugins = { }, directionVariants: ({ addVariant }) => { - addVariant('ltr', ':is(:where([dir="ltr"]) &)') - addVariant('rtl', ':is(:where([dir="rtl"]) &)') + addVariant('ltr', '&:where([dir="ltr"], [dir="ltr"] *)') + addVariant('rtl', '&:where([dir="rtl"], [dir="rtl"] *)') }, reducedMotionVariants: ({ addVariant }) => { @@ -261,7 +261,7 @@ export let variantPlugins = { } if (mode === 'selector') { - addVariant('dark', [`:is(:where(${className}) &)`, `&:where(${className})`]) + addVariant('dark', `&:where(${className}), ${className}) *)`) } else if (mode === 'media') { addVariant('dark', '@media (prefers-color-scheme: dark)') } else if (mode === 'variant') { From 14b8e0e050ae2baf5ed645d492d99e44b530b920 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 22 Dec 2023 14:04:42 -0500 Subject: [PATCH 08/24] =?UTF-8?q?Don=E2=80=99t=20let=20`:where`,=20`:is:`,?= =?UTF-8?q?=20or=20`:has`=20be=20attached=20to=20pseudo=20elements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/pseudoElements.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/pseudoElements.js b/src/util/pseudoElements.js index 5795cdd42045..e518801f42ba 100644 --- a/src/util/pseudoElements.js +++ b/src/util/pseudoElements.js @@ -60,6 +60,10 @@ let elementProperties = { ':first-letter': ['terminal', 'jumpable'], ':first-line': ['terminal', 'jumpable'], + ':where': [], + ':is': [], + ':has': [], + // The default value is used when the pseudo-element is not recognized // Because it's not recognized, we don't know if it's terminal or not // So we assume it can be moved AND can have user-action pseudo classes attached to it From aed8a36f26469afc4d725df67c392a65ab819d0a Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Fri, 22 Dec 2023 15:31:44 -0500 Subject: [PATCH 09/24] Updating tests... --- src/corePlugins.js | 2 +- tests/apply.test.js | 75 +++++-------------- tests/custom-separator.test.js | 8 +- tests/dark-mode.test.js | 13 ++-- tests/important-selector.test.js | 30 ++++---- .../directionVariants.test.js.snap | 2 +- tests/prefix.test.js | 10 +-- tests/variants.test.css | 30 ++++---- tests/variants.test.js | 13 ++-- 9 files changed, 70 insertions(+), 113 deletions(-) diff --git a/src/corePlugins.js b/src/corePlugins.js index 0ab36d57e71a..d3e45062d611 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -261,7 +261,7 @@ export let variantPlugins = { } if (mode === 'selector') { - addVariant('dark', `&:where(${className}), ${className}) *)`) + addVariant('dark', `&:where(${className}, ${className} *)`) } else if (mode === 'media') { addVariant('dark', '@media (prefers-color-scheme: dark)') } else if (mode === 'variant') { diff --git a/tests/apply.test.js b/tests/apply.test.js index 9759f3bfc407..2562328caddf 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -34,7 +34,7 @@ let sharedHtml = html` test('@apply', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [{ raw: sharedHtml }], } @@ -215,14 +215,14 @@ test('@apply', () => { text-align: left; } } - :is(:where(.dark) .apply-dark-variant) { + .apply-dark-variant:where(.dark, .dark *) { text-align: center; } - :is(:where(.dark) .apply-dark-variant:hover) { + .apply-dark-variant:hover:where(.dark, .dark *) { text-align: right; } @media (min-width: 1024px) { - :is(:where(.dark) .apply-dark-variant) { + .apply-dark-variant:where(.dark, .dark *) { text-align: left; } } @@ -2016,38 +2016,30 @@ it('pseudo elements inside apply are moved outside of :is() or :has()', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` - :is(:where(.dark) .foo):before, - .foo:where(.dark):before, - :is(:where([dir='rtl']) :is(:where(.dark) .bar)):before, - :is(:where([dir='rtl']) .bar:where(.dark)):before, - :is(:where([dir='rtl']) :is(:where(.dark) .baz:hover)):before, - :is(:where([dir='rtl']) .baz:hover:where(.dark)):before { + .foo:where(.dark, .dark *):before, + .bar:where(.dark, .dark *):where([dir='rtl'], [dir='rtl'] *):before, + .baz:hover:where(.dark, .dark *):where([dir='rtl'], [dir='rtl'] *):before { background-color: #000; } - :-webkit-any( - :where([dir='rtl']) :-webkit-any(:where(.dark) .qux) + .qux:where(.dark, .dark *):where( + [dir='rtl'], + [dir='rtl'] * )::-webkit-file-upload-button:hover { background-color: #000; } - :is(:where([dir='rtl']) :is(:where(.dark) .qux))::file-selector-button:hover { + .qux:where(.dark, .dark *):where([dir='rtl'], [dir='rtl'] *)::file-selector-button:hover { background-color: #000; } - :-webkit-any(:where([dir='rtl']) .qux)::-webkit-file-upload-button:hover:where() { + .steve:where(.dark, .dark *):hover:where([dir='rtl'], [dir='rtl'] *):before { background-color: #000; } - :is(:where([dir='rtl']) .qux)::file-selector-button:hover:where() { + .bob:where(.dark, .dark *):hover:where( + [dir='rtl'], + [dir='rtl'] * + )::-webkit-file-upload-button { background-color: #000; } - :is(:where([dir='rtl']) :is(:where(.dark) .steve):hover):before, - :is(:where([dir='rtl']) .steve:where(.dark):hover):before { - background-color: #000; - } - :-webkit-any( - :where([dir='rtl']) :-webkit-any(:where(.dark) .bob) - )::-webkit-file-upload-button:hover { - background-color: #000; - } - :is(:where([dir='rtl']) :is(:where(.dark) .bob))::file-selector-button:hover { + .bob:where(.dark, .dark *):hover:where([dir='rtl'], [dir='rtl'] *)::file-selector-button { background-color: #000; } :has([dir='rtl'] .foo:hover):before { @@ -2063,39 +2055,6 @@ it('pseudo elements inside apply are moved outside of :is() or :has()', () => { }) }) -it('for some reason this produces empty where clauses', () => { - let config = { - darkMode: 'selector', - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addVariant }) { - addVariant('wut', `&:where(.wut)`) - }, - ], - } - - let input = css` - .bob::file-selector-button { - @apply wut:bg-black/100; - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bob::-webkit-file-upload-button:where(.wut) { - background-color: #000; - } - .bob::file-selector-button:where(.wut) { - background-color: #000; - } - `) - }) -}) - test('::ng-deep, ::deep, ::v-deep pseudo elements are left alone', () => { let config = { darkMode: 'class', diff --git a/tests/custom-separator.test.js b/tests/custom-separator.test.js index a839c547942b..61b41ddf167d 100644 --- a/tests/custom-separator.test.js +++ b/tests/custom-separator.test.js @@ -2,7 +2,7 @@ import { run, html, css } from './util/run' test('custom separator', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` @@ -32,10 +32,10 @@ test('custom separator', () => { text-align: right; } } - :is(:where([dir='rtl']) .rtl_active_text-center:active) { + .rtl_active_text-center:active:where([dir='rtl'], [dir='rtl'] *) { text-align: center; } - :is(:where(.dark) .dark_focus_text-left:focus) { + .dark_focus_text-left:focus:where(.dark, .dark *) { text-align: left; } `) @@ -44,7 +44,7 @@ test('custom separator', () => { test('dash is not supported', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [{ raw: 'lg-hover-font-bold' }], separator: '-', } diff --git a/tests/dark-mode.test.js b/tests/dark-mode.test.js index 1136c5bd744a..342a3939ad3e 100644 --- a/tests/dark-mode.test.js +++ b/tests/dark-mode.test.js @@ -1,6 +1,6 @@ import { run, html, css, defaults } from './util/run' -it('should be possible to use the darkMode "class" mode', () => { +it('should be possible to use the darkMode "selector" mode', () => { let config = { darkMode: 'selector', content: [{ raw: html`
` }], @@ -16,15 +16,14 @@ it('should be possible to use the darkMode "class" mode', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` ${defaults} - :is(:where(.dark) .dark\:font-bold), - .dark\:font-bold:where(.dark) { + .dark\:font-bold:where(.dark, .dark *) { font-weight: 700; } `) }) }) -it('should be possible to change the class name', () => { +it('should be possible to change the selector', () => { let config = { darkMode: ['selector', '.test-dark'], content: [{ raw: html`
` }], @@ -40,8 +39,7 @@ it('should be possible to change the class name', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` ${defaults} - :is(:where(.test-dark) .dark\:font-bold), - .dark\:font-bold:where(.test-dark) { + .dark\:font-bold:where(.test-dark, .test-dark *) { font-weight: 700; } `) @@ -224,8 +222,7 @@ it('should use modern sorting otherwise', () => { color: rgb(134 239 172 / var(--tw-text-opacity)); } } - :is(:where(.dark) .dark\:text-green-100), - .dark\:text-green-100:where(.dark) { + .dark\:text-green-100:where(.dark, .dark *) { --tw-text-opacity: 1; color: rgb(220 252 231 / var(--tw-text-opacity)); } diff --git a/tests/important-selector.test.js b/tests/important-selector.test.js index aea4f1547ca5..f3d31a0276b1 100644 --- a/tests/important-selector.test.js +++ b/tests/important-selector.test.js @@ -3,7 +3,7 @@ import { run, html, css, defaults } from './util/run' test('important selector', () => { let config = { important: '#app', - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` @@ -145,30 +145,28 @@ test('important selector', () => { text-align: right; } } - #app :is(:where([dir='rtl']) .rtl\:active\:text-center:active) { + #app .rtl\:active\:text-center:active:where([dir='rtl'], [dir='rtl'] *) { text-align: center; } - #app :is(:where(.dark) .dark\:before\:underline):before { + #app .dark\:before\:underline:where(.dark, .dark *):before { content: var(--tw-content); text-decoration-line: underline; } - #app :is(:where(.dark) .dark\:focus\:text-left:focus) { + #app .dark\:focus\:text-left:focus:where(.dark, .dark *) { text-align: left; } #app - :-webkit-any( - :where([dir='rtl']) - :-webkit-any( - :where(.dark) .hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100 - ) - )::-webkit-file-upload-button:hover { + .hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100:where( + .dark, + .dark * + ):where([dir='rtl'], [dir='rtl'] *)::-webkit-file-upload-button:hover { background-color: #000; } #app - :is( - :where([dir='rtl']) - :is(:where(.dark) .hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100) - )::file-selector-button:hover { + .hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100:where( + .dark, + .dark * + ):where([dir='rtl'], [dir='rtl'] *)::file-selector-button:hover { background-color: #000; } `) @@ -178,7 +176,7 @@ test('important selector', () => { test('pseudo-elements are appended after the `:-webkit-any()`', () => { let config = { important: '#app', - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html`
`, @@ -196,7 +194,7 @@ test('pseudo-elements are appended after the `:-webkit-any()`', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` ${defaults} - #app :is(:where(.dark) .dark\:before\:flex):before { + #app .dark\:before\:flex:where(.dark, .dark *):before { content: var(--tw-content); display: flex; } diff --git a/tests/plugins/variants/__snapshots__/directionVariants.test.js.snap b/tests/plugins/variants/__snapshots__/directionVariants.test.js.snap index 3f3f79cf96ed..840ff25b0618 100644 --- a/tests/plugins/variants/__snapshots__/directionVariants.test.js.snap +++ b/tests/plugins/variants/__snapshots__/directionVariants.test.js.snap @@ -2,7 +2,7 @@ exports[`should test the 'directionVariants' plugin 1`] = ` " -:is(:where([dir="ltr"]) .ltr\\:flex), :is(:where([dir="rtl"]) .rtl\\:flex) { +.ltr\\:flex:where([dir="ltr"], [dir="ltr"] *), .rtl\\:flex:where([dir="rtl"], [dir="rtl"] *) { display: flex; } " diff --git a/tests/prefix.test.js b/tests/prefix.test.js index a0ce18cbd5fa..ab580d192145 100644 --- a/tests/prefix.test.js +++ b/tests/prefix.test.js @@ -3,7 +3,7 @@ import { run, html, css, defaults } from './util/run' test('prefix', () => { let config = { prefix: 'tw-', - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` @@ -126,7 +126,7 @@ test('prefix', () => { .custom-component { font-weight: 700; } - :is(:where(.tw-dark) .tw-group:hover .custom-component) { + .tw-group:hover .custom-component:where(.tw-dark, .tw-dark *) { font-weight: 400; } .tw--ml-4 { @@ -169,14 +169,14 @@ test('prefix', () => { text-align: right; } } - :is(:where([dir='rtl']) .rtl\:active\:tw-text-center:active) { + .rtl\:active\:tw-text-center:active:where([dir='rtl'], [dir='rtl'] *) { text-align: center; } - :is(:where(.tw-dark) .dark\:tw-bg-\[rgb\(255\,0\,0\)\]) { + .dark\:tw-bg-\[rgb\(255\,0\,0\)\]:where(.tw-dark, .tw-dark *) { --tw-bg-opacity: 1; background-color: rgb(255 0 0 / var(--tw-bg-opacity)); } - :is(:where(.tw-dark) .dark\:focus\:tw-text-left:focus) { + .dark\:focus\:tw-text-left:focus:where(.tw-dark, .tw-dark *) { text-align: left; } `) diff --git a/tests/variants.test.css b/tests/variants.test.css index bf3275257dc9..b8e87257cf79 100644 --- a/tests/variants.test.css +++ b/tests/variants.test.css @@ -293,11 +293,6 @@ display: flex; } } -@media print { - .print\:flex { - display: flex; - } -} @media (min-width: 640px) { .sm\:flex, .sm\:active\:flex:active { @@ -335,28 +330,28 @@ display: flex; } } -:is(:where([dir='ltr']) .ltr\:flex), -:is(:where([dir='rtl']) .rtl\:flex), -:is(:where(.dark) .dark\:flex), -:is( - :where(.dark) .group:disabled:focus:hover .dark\:group-disabled\:group-focus\:group-hover\:flex - ), -:is(:where(.dark) .peer:disabled:focus:hover ~ .dark\:peer-disabled\:peer-focus\:peer-hover\:flex) { +.ltr\:flex:where([dir='ltr'], [dir='ltr'] *), +.rtl\:flex:where([dir='rtl'], [dir='rtl'] *), +.dark\:flex:where(.dark, .dark *), +.group:disabled:focus:hover + .dark\:group-disabled\:group-focus\:group-hover\:flex:where(.dark, .dark *), +.peer:disabled:focus:hover + ~ .dark\:peer-disabled\:peer-focus\:peer-hover\:flex:where(.dark, .dark *) { display: flex; } @media (min-width: 1024px) { - :is(:where(.dark) .lg\:dark\:flex) { + .lg\:dark\:flex:where(.dark, .dark *) { display: flex; } } @media (min-width: 1280px) { - :is(:where(.dark) .xl\:dark\:disabled\:flex:disabled) { + .xl\:dark\:disabled\:flex:disabled:where(.dark, .dark *) { display: flex; } } @media (min-width: 1536px) { @media (prefers-reduced-motion: no-preference) { - :is(:where(.dark) .\32 xl\:dark\:motion-safe\:focus-within\:flex:focus-within) { + .\32 xl\:dark\:motion-safe\:focus-within\:flex:focus-within:where(.dark, .dark *) { display: flex; } } @@ -366,3 +361,8 @@ display: flex; } } +@media print { + .print\:flex { + display: flex; + } +} diff --git a/tests/variants.test.js b/tests/variants.test.js index 513b950a6238..bccebf8a593f 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -5,7 +5,7 @@ import { run, html, css, defaults } from './util/run' test('variants', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [path.resolve(__dirname, './variants.test.html')], corePlugins: { preflight: false }, } @@ -1122,7 +1122,7 @@ test('arbitrary variant selectors should not re-order scrollbar pseudo classes', test('stacking dark and rtl variants', async () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html`
`, @@ -1138,7 +1138,7 @@ test('stacking dark and rtl variants', async () => { let result = await run(input, config) expect(result.css).toMatchFormattedCss(css` - :is(:where(.dark) :is(:where([dir='rtl']) .dark\:rtl\:italic)) { + .dark\:rtl\:italic:where([dir='rtl'], [dir='rtl'] *):where(.dark, .dark *) { font-style: italic; } `) @@ -1146,7 +1146,7 @@ test('stacking dark and rtl variants', async () => { test('stacking dark and rtl variants with pseudo elements', async () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html`
`, @@ -1162,7 +1162,10 @@ test('stacking dark and rtl variants with pseudo elements', async () => { let result = await run(input, config) expect(result.css).toMatchFormattedCss(css` - :is(:where(.dark) :is(:where([dir='rtl']) .dark\:rtl\:placeholder\:italic))::placeholder { + .dark\:rtl\:placeholder\:italic:where([dir='rtl'], [dir='rtl'] *):where( + .dark, + .dark * + )::placeholder { font-style: italic; } `) From 2433d997164e95b5099c8346ac73766fb2700a25 Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Fri, 22 Dec 2023 15:39:24 -0500 Subject: [PATCH 10/24] Finish updating tests --- tests/important-boolean.test.js | 6 +++--- tests/kitchen-sink.test.js | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/important-boolean.test.js b/tests/important-boolean.test.js index ec49b42439f5..116d97ec8ff6 100644 --- a/tests/important-boolean.test.js +++ b/tests/important-boolean.test.js @@ -7,7 +7,7 @@ import { run, html, css, defaults } from './util/run' test('important boolean', () => { let config = { important: true, - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` @@ -147,10 +147,10 @@ test('important boolean', () => { text-align: right !important; } } - :is(:where([dir='rtl']) .rtl\:active\:text-center:active) { + .rtl\:active\:text-center:active:where([dir='rtl'], [dir='rtl'] *) { text-align: center !important; } - :is(:where(.dark) .dark\:focus\:text-left:focus) { + .dark\:focus\:text-left:focus:where(.dark, .dark *) { text-align: left !important; } `) diff --git a/tests/kitchen-sink.test.js b/tests/kitchen-sink.test.js index 2151f22db952..a869ad4c8f33 100644 --- a/tests/kitchen-sink.test.js +++ b/tests/kitchen-sink.test.js @@ -2,7 +2,7 @@ import { run, html, css, defaults } from './util/run' test('it works', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` @@ -304,7 +304,7 @@ test('it works', () => { } .drop-empty-rules:hover, .group:hover .apply-group, - :is(:where(.dark) .apply-dark-mode) { + .apply-dark-mode:where(.dark, .dark *) { font-weight: 700; } .apply-with-existing:hover { @@ -339,7 +339,7 @@ test('it works', () => { .apply-order-b { margin: 1.5rem 1.25rem 1.25rem; } - :is(:where(.dark) .group:hover .apply-dark-group-example-a) { + .group:hover .apply-dark-group-example-a:where(.dark, .dark *) { --tw-bg-opacity: 1; background-color: rgb(34 197 94 / var(--tw-bg-opacity)); } @@ -806,12 +806,12 @@ test('it works', () => { text-align: left; } } - :is(:where(.dark) .dark\:custom-util) { + .dark\:custom-util:where(.dark, .dark *) { background: #abcdef; } @media (min-width: 768px) { @media (prefers-reduced-motion: no-preference) { - :is(:where(.dark) .md\:dark\:motion-safe\:foo\:active\:custom-util:active) { + .md\:dark\:motion-safe\:foo\:active\:custom-util:active:where(.dark, .dark *) { background: #abcdef !important; } } From 185250438ccb2f61ba876d4676823c1807891122 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 12:16:10 -0500 Subject: [PATCH 11/24] Remove `variant` dark mode strategy --- src/corePlugins.js | 35 ----------------------- tests/dark-mode.test.js | 62 ----------------------------------------- 2 files changed, 97 deletions(-) diff --git a/src/corePlugins.js b/src/corePlugins.js index d3e45062d611..ab2af510752d 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -227,45 +227,10 @@ export let variantPlugins = { ]) } - if (mode === 'variant') { - let formats - if (Array.isArray(className)) { - formats = className - } else if (typeof className === 'function') { - formats = className - } else if (typeof className === 'string') { - formats = [className] - } - - // TODO: We could also add these warnings if the user passes a function that returns string | string[] - // But this is an advanced enough use case that it's probably not necessary - if (Array.isArray(formats)) { - for (let format of formats) { - if (format === '.dark') { - mode = false - log.warn('darkmode-variant-without-selector', [ - 'When using `variant` for `darkMode`, you must provide a selector.', - 'Example: `darkMode: ["variant", ".your-selector &"]`', - ]) - } else if (!format.includes('&')) { - mode = false - log.warn('darkmode-variant-without-ampersand', [ - 'When using `variant` for `darkMode`, your selector must contain `&`.', - 'Example `darkMode: ["variant", ".your-selector &"]`', - ]) - } - } - } - - className = formats - } - if (mode === 'selector') { addVariant('dark', `&:where(${className}, ${className} *)`) } else if (mode === 'media') { addVariant('dark', '@media (prefers-color-scheme: dark)') - } else if (mode === 'variant') { - addVariant('dark', className) } else if (mode === 'class') { // Exists for pre v3.4 compatibility addVariant('dark', `:is(${className} &)`) diff --git a/tests/dark-mode.test.js b/tests/dark-mode.test.js index 342a3939ad3e..8695bde27530 100644 --- a/tests/dark-mode.test.js +++ b/tests/dark-mode.test.js @@ -229,65 +229,3 @@ it('should use modern sorting otherwise', () => { `) }) }) - -it('should allow customization of the dark mode variant', () => { - let config = { - darkMode: ['variant', '&:not(.light *)'], - content: [{ raw: html`
` }], - 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; - } - `) - }) -}) - -it('should support parallel selectors for the dark mode variant', () => { - let config = { - darkMode: ['variant', ['&:not(.light *)', '&:not(.extralight *)']], - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .dark\:font-bold:not(.light *), - .dark\:font-bold:not(.extralight *) { - font-weight: 700; - } - `) - }) -}) - -it('should support fn selectors for the dark mode variant', () => { - let config = { - darkMode: ['variant', () => ['&:not(.light *)', '&:not(.extralight *)']], - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .dark\:font-bold:not(.light *), - .dark\:font-bold:not(.extralight *) { - font-weight: 700; - } - `) - }) -}) From d3fd11a83188943bdedeae1baf9ec24ac7f2129d Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 12:16:16 -0500 Subject: [PATCH 12/24] Update types --- types/config.d.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/types/config.d.ts b/types/config.d.ts index 87a7fdfd2e33..07bc423d63a6 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -75,13 +75,10 @@ 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 - // It takes a string or an array of strings, which are passed directly to `addVariant()` - | ['variant', string | string[]] + // Use the `selector` strategy — same as `class` but uses `:where()` for more predicable behavior + | 'selector' + // Use the `selector` strategy with a custom selector instead of `.dark`. + | ['selector', string] type Screen = { raw: string } | { min: string } | { max: string } | { min: string; max: string } type ScreensConfig = string[] | KeyValuePair From 3a2f2a2ec9a0ce4767f97b41bfb758deaa0cd0d4 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 12:26:29 -0500 Subject: [PATCH 13/24] Update comments --- src/corePlugins.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/corePlugins.js b/src/corePlugins.js index ab2af510752d..6de21ae820b3 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -228,11 +228,12 @@ export let variantPlugins = { } if (mode === 'selector') { + // New preferred behavior addVariant('dark', `&:where(${className}, ${className} *)`) } else if (mode === 'media') { addVariant('dark', '@media (prefers-color-scheme: dark)') } else if (mode === 'class') { - // Exists for pre v3.4 compatibility + // Old behavior addVariant('dark', `:is(${className} &)`) } }, From a18f96b21f733b609bc204cde187c74773d399fc Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 12:27:33 -0500 Subject: [PATCH 14/24] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4371274c84cd..6772c6e76a7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,10 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Don't remove keyframe stops when using important utilities ([#12639](https://github.com/tailwindlabs/tailwindcss/pull/12639)) - Don't add spaces to gradients and grid track names when followed by `calc()` ([#12704](https://github.com/tailwindlabs/tailwindcss/pull/12704)) +- Restore old behavior for `darkMode: "class"` ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) - Improve glob handling for folders with `(`, `)`, `[` or `]` in the file path ([#12715](https://github.com/tailwindlabs/tailwindcss/pull/12715)) ### Added +- Add new `selector` strategy for dark mode ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) - [Oxide] New Rust template parsing engine ([#10252](https://github.com/tailwindlabs/tailwindcss/pull/10252)) - [Oxide] Support `@import "tailwindcss"` using top-level `index.css` file ([#11205](https://github.com/tailwindlabs/tailwindcss/pull/11205), ([#11260](https://github.com/tailwindlabs/tailwindcss/pull/11260))) - [Oxide] Use `lightningcss` for nesting and vendor prefixes in PostCSS plugin ([#10399](https://github.com/tailwindlabs/tailwindcss/pull/10399)) From 64985021175f8babdc2525145b39aa9c244478a5 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:02:29 -0500 Subject: [PATCH 15/24] Revert "Remove `variant` dark mode strategy" This reverts commit 185250438ccb2f61ba876d4676823c1807891122. --- src/corePlugins.js | 35 +++++++++++++++++++++++ tests/dark-mode.test.js | 62 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/src/corePlugins.js b/src/corePlugins.js index 6de21ae820b3..81a1b9f12dee 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -227,11 +227,46 @@ export let variantPlugins = { ]) } + if (mode === 'variant') { + let formats + if (Array.isArray(className)) { + formats = className + } else if (typeof className === 'function') { + formats = className + } else if (typeof className === 'string') { + formats = [className] + } + + // TODO: We could also add these warnings if the user passes a function that returns string | string[] + // But this is an advanced enough use case that it's probably not necessary + if (Array.isArray(formats)) { + for (let format of formats) { + if (format === '.dark') { + mode = false + log.warn('darkmode-variant-without-selector', [ + 'When using `variant` for `darkMode`, you must provide a selector.', + 'Example: `darkMode: ["variant", ".your-selector &"]`', + ]) + } else if (!format.includes('&')) { + mode = false + log.warn('darkmode-variant-without-ampersand', [ + 'When using `variant` for `darkMode`, your selector must contain `&`.', + 'Example `darkMode: ["variant", ".your-selector &"]`', + ]) + } + } + } + + className = formats + } + if (mode === 'selector') { // New preferred behavior addVariant('dark', `&:where(${className}, ${className} *)`) } else if (mode === 'media') { addVariant('dark', '@media (prefers-color-scheme: dark)') + } else if (mode === 'variant') { + addVariant('dark', className) } else if (mode === 'class') { // Old behavior addVariant('dark', `:is(${className} &)`) diff --git a/tests/dark-mode.test.js b/tests/dark-mode.test.js index 8695bde27530..342a3939ad3e 100644 --- a/tests/dark-mode.test.js +++ b/tests/dark-mode.test.js @@ -229,3 +229,65 @@ it('should use modern sorting otherwise', () => { `) }) }) + +it('should allow customization of the dark mode variant', () => { + let config = { + darkMode: ['variant', '&:not(.light *)'], + content: [{ raw: html`
` }], + 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; + } + `) + }) +}) + +it('should support parallel selectors for the dark mode variant', () => { + let config = { + darkMode: ['variant', ['&:not(.light *)', '&:not(.extralight *)']], + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .dark\:font-bold:not(.light *), + .dark\:font-bold:not(.extralight *) { + font-weight: 700; + } + `) + }) +}) + +it('should support fn selectors for the dark mode variant', () => { + let config = { + darkMode: ['variant', () => ['&:not(.light *)', '&:not(.extralight *)']], + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .dark\:font-bold:not(.light *), + .dark\:font-bold:not(.extralight *) { + font-weight: 700; + } + `) + }) +}) From 25728c08ecd478cd103a35bc60c878c29bda2fa0 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:03:16 -0500 Subject: [PATCH 16/24] Add variant back to types --- types/config.d.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/config.d.ts b/types/config.d.ts index 07bc423d63a6..f0c342a78569 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -79,6 +79,9 @@ type DarkModeConfig = | 'selector' // Use the `selector` strategy with a custom selector instead of `.dark`. | ['selector', string] + // Use the `variant` strategy, which allows you to completely customize the selector + // 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 From 10e690c58f9ab912a54ade17f85ae9c245712eef Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:24:04 -0500 Subject: [PATCH 17/24] wip --- src/lib/setupContextUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index ad1f0f9e00ae..56e1dfd80182 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -776,16 +776,16 @@ function resolvePlugins(context, root) { if (isLegacyDarkMode) { afterVariants = [ variantPlugins['supportsVariants'], - variantPlugins['directionVariants'], variantPlugins['reducedMotionVariants'], variantPlugins['prefersContrastVariants'], variantPlugins['darkVariants'], - variantPlugins['printVariant'], variantPlugins['screenVariants'], variantPlugins['orientationVariants'], + variantPlugins['directionVariants'], // Forced colors didn't exist before 3.4 so it can keep its position at the end variantPlugins['forcedColorsVariants'], + variantPlugins['printVariant'], ] } From 44fc78aaefef0115dbab8bfb68c48a538d426164 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:26:00 -0500 Subject: [PATCH 18/24] Update comments --- src/lib/setupContextUtils.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 56e1dfd80182..d6380211b933 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -766,8 +766,7 @@ function resolvePlugins(context, root) { ] // This is a compatibility fix for the pre 3.4 dark mode behavior - // The `class` strategy is deprecated in favor of `selector` which has the new behavior - // You can enable the old behavior by setting `darkMode: ['legacy', selector]` + // `class` retains the old behavior, but `selector` keeps the new behavior let isLegacyDarkMode = context.tailwindConfig.darkMode === 'class' || (Array.isArray(context.tailwindConfig.darkMode) && @@ -782,8 +781,6 @@ function resolvePlugins(context, root) { variantPlugins['screenVariants'], variantPlugins['orientationVariants'], variantPlugins['directionVariants'], - - // Forced colors didn't exist before 3.4 so it can keep its position at the end variantPlugins['forcedColorsVariants'], variantPlugins['printVariant'], ] From 4921319a408a9a1c2a8e185b070e248085e6f39f Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:26:26 -0500 Subject: [PATCH 19/24] Update tests --- tests/apply.test.js | 16 ++++++++-------- tests/import-processing-c.js | 2 +- tests/import-processing.test.js | 4 ++-- tests/important-modifier-prefix.test.js | 2 +- tests/important-modifier.test.js | 2 +- tests/modify-selectors.test.js | 2 +- tests/opacity.test.js | 4 ++-- .../__snapshots__/darkVariants.test.js.snap | 16 ++++++++++++++++ tests/plugins/variants/darkVariants.test.js | 11 +++++++++++ 9 files changed, 43 insertions(+), 16 deletions(-) diff --git a/tests/apply.test.js b/tests/apply.test.js index 2562328caddf..1415019e4acb 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -452,7 +452,7 @@ test('@apply', () => { test('@apply error with unknown utility', async () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [{ raw: sharedHtml }], } @@ -472,7 +472,7 @@ test('@apply error with unknown utility', async () => { test('@apply error with nested @screen', async () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [{ raw: sharedHtml }], } @@ -496,7 +496,7 @@ test('@apply error with nested @screen', async () => { test('@apply error with nested @anyatrulehere', async () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [{ raw: sharedHtml }], } @@ -520,7 +520,7 @@ test('@apply error with nested @anyatrulehere', async () => { test('@apply error when using .group utility', async () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [{ raw: '
' }], } @@ -543,7 +543,7 @@ test('@apply error when using .group utility', async () => { test('@apply error when using a prefixed .group utility', async () => { let config = { prefix: 'tw-', - darkMode: 'class', + darkMode: 'selector', content: [{ raw: html`
` }], } @@ -565,7 +565,7 @@ test('@apply error when using a prefixed .group utility', async () => { test('@apply error when using .peer utility', async () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [{ raw: '
' }], } @@ -588,7 +588,7 @@ test('@apply error when using .peer utility', async () => { test('@apply error when using a prefixed .peer utility', async () => { let config = { prefix: 'tw-', - darkMode: 'class', + darkMode: 'selector', content: [{ raw: html`
` }], } @@ -2057,7 +2057,7 @@ it('pseudo elements inside apply are moved outside of :is() or :has()', () => { test('::ng-deep, ::deep, ::v-deep pseudo elements are left alone', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html`
`, diff --git a/tests/import-processing-c.js b/tests/import-processing-c.js index 164503d5ec5d..3bfef1ae395a 100644 --- a/tests/import-processing-c.js +++ b/tests/import-processing-c.js @@ -1,5 +1,5 @@ module.exports = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: `
`, diff --git a/tests/import-processing.test.js b/tests/import-processing.test.js index ef59be3486e9..ad14d9bcfdbc 100644 --- a/tests/import-processing.test.js +++ b/tests/import-processing.test.js @@ -3,7 +3,7 @@ import { html, css, run } from './util/run' describe('import processing', () => { it('should be possible to import another css file', async () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html`
`, @@ -27,7 +27,7 @@ describe('import processing', () => { it('should be possible to import another css file after @tailwind directive', async () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html`
`, diff --git a/tests/important-modifier-prefix.test.js b/tests/important-modifier-prefix.test.js index 2c6a8bb62642..3c34d4f2a99c 100644 --- a/tests/important-modifier-prefix.test.js +++ b/tests/important-modifier-prefix.test.js @@ -4,7 +4,7 @@ test('important modifier with prefix', () => { let config = { important: false, prefix: 'tw-', - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` diff --git a/tests/important-modifier.test.js b/tests/important-modifier.test.js index da5ac5d7d4e6..57177a8c3408 100644 --- a/tests/important-modifier.test.js +++ b/tests/important-modifier.test.js @@ -3,7 +3,7 @@ import { run, html, css } from './util/run' test('important modifier', () => { let config = { important: false, - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` diff --git a/tests/modify-selectors.test.js b/tests/modify-selectors.test.js index e6ce3cf147c6..364b84a7ec7e 100644 --- a/tests/modify-selectors.test.js +++ b/tests/modify-selectors.test.js @@ -4,7 +4,7 @@ import { run, html, css } from './util/run' test('modify selectors', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` diff --git a/tests/opacity.test.js b/tests/opacity.test.js index 04b23aea783c..8f688210cead 100644 --- a/tests/opacity.test.js +++ b/tests/opacity.test.js @@ -2,7 +2,7 @@ import { run, html, css } from './util/run' test('opacity', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` @@ -42,7 +42,7 @@ test('opacity', () => { test('colors defined as functions work when opacity plugins are disabled', () => { let config = { - darkMode: 'class', + darkMode: 'selector', content: [ { raw: html` diff --git a/tests/plugins/variants/__snapshots__/darkVariants.test.js.snap b/tests/plugins/variants/__snapshots__/darkVariants.test.js.snap index 0a3dda916fa9..771a41d19d4a 100644 --- a/tests/plugins/variants/__snapshots__/darkVariants.test.js.snap +++ b/tests/plugins/variants/__snapshots__/darkVariants.test.js.snap @@ -25,3 +25,19 @@ exports[`should test the 'darkVariants' plugin 3`] = ` } " `; + +exports[`should test the 'darkVariants' plugin 4`] = ` +" +.dark\\:flex:where(.dark, .dark *) { + display: flex; +} +" +`; + +exports[`should test the 'darkVariants' plugin 5`] = ` +" +.dark\\:flex:where(.my-dark-mode, .my-dark-mode *) { + display: flex; +} +" +`; diff --git a/tests/plugins/variants/darkVariants.test.js b/tests/plugins/variants/darkVariants.test.js index 2080df0880ec..0bd6aa21fe8d 100644 --- a/tests/plugins/variants/darkVariants.test.js +++ b/tests/plugins/variants/darkVariants.test.js @@ -12,3 +12,14 @@ quickVariantPluginTest('darkVariants', { quickVariantPluginTest('darkVariants', { darkMode: ['class', '.my-dark-mode'], }).toMatchSnapshot() + +// Selector dark mode +quickVariantPluginTest('darkVariants', { + darkMode: 'selector', +}).toMatchSnapshot() + +// Selector dark mode with custom selector +quickVariantPluginTest('darkVariants', { + darkMode: ['selector', '.my-dark-mode'], +}).toMatchSnapshot() + From 0e02e860542e94facf8eb11fb2d8c621f1cc9c02 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:26:53 -0500 Subject: [PATCH 20/24] Rename variable --- src/corePlugins.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/corePlugins.js b/src/corePlugins.js index 81a1b9f12dee..7e04044308fe 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -216,7 +216,7 @@ export let variantPlugins = { }, darkVariants: ({ config, addVariant }) => { - let [mode, className = '.dark'] = [].concat(config('darkMode', 'media')) + let [mode, selector = '.dark'] = [].concat(config('darkMode', 'media')) if (mode === false) { mode = 'media' @@ -229,12 +229,12 @@ export let variantPlugins = { if (mode === 'variant') { let formats - if (Array.isArray(className)) { - formats = className - } else if (typeof className === 'function') { - formats = className - } else if (typeof className === 'string') { - formats = [className] + if (Array.isArray(selector)) { + formats = selector + } else if (typeof selector === 'function') { + formats = selector + } else if (typeof selector === 'string') { + formats = [selector] } // TODO: We could also add these warnings if the user passes a function that returns string | string[] @@ -257,19 +257,19 @@ export let variantPlugins = { } } - className = formats + selector = formats } if (mode === 'selector') { // New preferred behavior - addVariant('dark', `&:where(${className}, ${className} *)`) + addVariant('dark', `&:where(${selector}, ${selector} *)`) } else if (mode === 'media') { addVariant('dark', '@media (prefers-color-scheme: dark)') } else if (mode === 'variant') { - addVariant('dark', className) + addVariant('dark', selector) } else if (mode === 'class') { // Old behavior - addVariant('dark', `:is(${className} &)`) + addVariant('dark', `:is(${selector} &)`) } }, From 9c889fe0e65597070b7ca88a408aec86fc74f291 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:30:57 -0500 Subject: [PATCH 21/24] Update changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6772c6e76a7a..ef37f910b87a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Don't remove keyframe stops when using important utilities ([#12639](https://github.com/tailwindlabs/tailwindcss/pull/12639)) - Don't add spaces to gradients and grid track names when followed by `calc()` ([#12704](https://github.com/tailwindlabs/tailwindcss/pull/12704)) -- Restore old behavior for `darkMode: "class"` ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) +- Restore old behavior for `class` dark mode strategy ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) - Improve glob handling for folders with `(`, `)`, `[` or `]` in the file path ([#12715](https://github.com/tailwindlabs/tailwindcss/pull/12715)) ### Added -- Add new `selector` strategy for dark mode ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) +- Add new `selector` and `variant` strategies for dark mode ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) - [Oxide] New Rust template parsing engine ([#10252](https://github.com/tailwindlabs/tailwindcss/pull/10252)) - [Oxide] Support `@import "tailwindcss"` using top-level `index.css` file ([#11205](https://github.com/tailwindlabs/tailwindcss/pull/11205), ([#11260](https://github.com/tailwindlabs/tailwindcss/pull/11260))) - [Oxide] Use `lightningcss` for nesting and vendor prefixes in PostCSS plugin ([#10399](https://github.com/tailwindlabs/tailwindcss/pull/10399)) From c16b1d63d182775ffecf89dd4373eb5f233261bb Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:32:56 -0500 Subject: [PATCH 22/24] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef37f910b87a..6169556468d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add new `selector` and `variant` strategies for dark mode ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) +- Support `rtl` and `ltr` variants on same element as `dir` attribute ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) - [Oxide] New Rust template parsing engine ([#10252](https://github.com/tailwindlabs/tailwindcss/pull/10252)) - [Oxide] Support `@import "tailwindcss"` using top-level `index.css` file ([#11205](https://github.com/tailwindlabs/tailwindcss/pull/11205), ([#11260](https://github.com/tailwindlabs/tailwindcss/pull/11260))) - [Oxide] Use `lightningcss` for nesting and vendor prefixes in PostCSS plugin ([#10399](https://github.com/tailwindlabs/tailwindcss/pull/10399)) From fe0488cb32082b9ebf28be26e132b6ea06026136 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:33:17 -0500 Subject: [PATCH 23/24] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6169556468d3..22e81b2bffb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add new `selector` and `variant` strategies for dark mode ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) -- Support `rtl` and `ltr` variants on same element as `dir` attribute ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) - [Oxide] New Rust template parsing engine ([#10252](https://github.com/tailwindlabs/tailwindcss/pull/10252)) - [Oxide] Support `@import "tailwindcss"` using top-level `index.css` file ([#11205](https://github.com/tailwindlabs/tailwindcss/pull/11205), ([#11260](https://github.com/tailwindlabs/tailwindcss/pull/11260))) - [Oxide] Use `lightningcss` for nesting and vendor prefixes in PostCSS plugin ([#10399](https://github.com/tailwindlabs/tailwindcss/pull/10399)) @@ -28,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Support `rtl` and `ltr` variants on same element as `dir` attribute ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717)) - [Oxide] Deprecate `--no-autoprefixer` flag in the CLI ([#11280](https://github.com/tailwindlabs/tailwindcss/pull/11280)) - [Oxide] Make the Rust based parser the default ([#11394](https://github.com/tailwindlabs/tailwindcss/pull/11394)) From eca8fff928a73bcf476ca9fc7386ae82fc4aaea8 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 5 Jan 2024 14:36:06 -0500 Subject: [PATCH 24/24] Fix CS --- tests/plugins/variants/darkVariants.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/plugins/variants/darkVariants.test.js b/tests/plugins/variants/darkVariants.test.js index 0bd6aa21fe8d..c13ceb5b1f77 100644 --- a/tests/plugins/variants/darkVariants.test.js +++ b/tests/plugins/variants/darkVariants.test.js @@ -22,4 +22,3 @@ quickVariantPluginTest('darkVariants', { quickVariantPluginTest('darkVariants', { darkMode: ['selector', '.my-dark-mode'], }).toMatchSnapshot() -