Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3fb57e5

Browse files
Restore old behavior for class dark mode, add new selector and variant options for dark mode (#12717)
* Add dark mode variant option * Tweak warning messages * Add legacy dark mode option * wip * Use `class` for legacy behavior, `selector` for new behavior * Add simplified failing apply/where test case * Switch to `where` list, apply changes to `dir` variants * Don’t let `:where`, `:is:`, or `:has` be attached to pseudo elements * Updating tests... * Finish updating tests * Remove `variant` dark mode strategy * Update types * Update comments * Update changelog * Revert "Remove `variant` dark mode strategy" This reverts commit 1852504. * Add variant back to types * wip * Update comments * Update tests * Rename variable * Update changelog * Update changelog * Update changelog * Fix CS --------- Co-authored-by: Adam Wathan <[email protected]>
1 parent 78fedd5 commit 3fb57e5

20 files changed

+446
-134
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Don't remove keyframe stops when using important utilities ([#12639](https://github.com/tailwindlabs/tailwindcss/pull/12639))
1313
- Don't add spaces to gradients and grid track names when followed by `calc()` ([#12704](https://github.com/tailwindlabs/tailwindcss/pull/12704))
14+
- Restore old behavior for `class` dark mode strategy ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717))
15+
16+
### Added
17+
18+
- Add new `selector` and `variant` strategies for dark mode ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717))
19+
20+
### Changed
21+
22+
- Support `rtl` and `ltr` variants on same element as `dir` attribute ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717))
1423

1524
## [3.4.0] - 2023-12-19
1625

src/corePlugins.js

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,8 @@ export let variantPlugins = {
207207
},
208208

209209
directionVariants: ({ addVariant }) => {
210-
addVariant('ltr', ':is(:where([dir="ltr"]) &)')
211-
addVariant('rtl', ':is(:where([dir="rtl"]) &)')
210+
addVariant('ltr', '&:where([dir="ltr"], [dir="ltr"] *)')
211+
addVariant('rtl', '&:where([dir="rtl"], [dir="rtl"] *)')
212212
},
213213

214214
reducedMotionVariants: ({ addVariant }) => {
@@ -217,7 +217,7 @@ export let variantPlugins = {
217217
},
218218

219219
darkVariants: ({ config, addVariant }) => {
220-
let [mode, className = '.dark'] = [].concat(config('darkMode', 'media'))
220+
let [mode, selector = '.dark'] = [].concat(config('darkMode', 'media'))
221221

222222
if (mode === false) {
223223
mode = 'media'
@@ -228,10 +228,49 @@ export let variantPlugins = {
228228
])
229229
}
230230

231-
if (mode === 'class') {
232-
addVariant('dark', `:is(:where(${className}) &)`)
231+
if (mode === 'variant') {
232+
let formats
233+
if (Array.isArray(selector)) {
234+
formats = selector
235+
} else if (typeof selector === 'function') {
236+
formats = selector
237+
} else if (typeof selector === 'string') {
238+
formats = [selector]
239+
}
240+
241+
// TODO: We could also add these warnings if the user passes a function that returns string | string[]
242+
// But this is an advanced enough use case that it's probably not necessary
243+
if (Array.isArray(formats)) {
244+
for (let format of formats) {
245+
if (format === '.dark') {
246+
mode = false
247+
log.warn('darkmode-variant-without-selector', [
248+
'When using `variant` for `darkMode`, you must provide a selector.',
249+
'Example: `darkMode: ["variant", ".your-selector &"]`',
250+
])
251+
} else if (!format.includes('&')) {
252+
mode = false
253+
log.warn('darkmode-variant-without-ampersand', [
254+
'When using `variant` for `darkMode`, your selector must contain `&`.',
255+
'Example `darkMode: ["variant", ".your-selector &"]`',
256+
])
257+
}
258+
}
259+
}
260+
261+
selector = formats
262+
}
263+
264+
if (mode === 'selector') {
265+
// New preferred behavior
266+
addVariant('dark', `&:where(${selector}, ${selector} *)`)
233267
} else if (mode === 'media') {
234268
addVariant('dark', '@media (prefers-color-scheme: dark)')
269+
} else if (mode === 'variant') {
270+
addVariant('dark', selector)
271+
} else if (mode === 'class') {
272+
// Old behavior
273+
addVariant('dark', `:is(${selector} &)`)
235274
}
236275
},
237276

src/lib/setupContextUtils.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,14 +767,35 @@ function resolvePlugins(context, root) {
767767
variantPlugins['supportsVariants'],
768768
variantPlugins['reducedMotionVariants'],
769769
variantPlugins['prefersContrastVariants'],
770-
variantPlugins['printVariant'],
771770
variantPlugins['screenVariants'],
772771
variantPlugins['orientationVariants'],
773772
variantPlugins['directionVariants'],
774773
variantPlugins['darkVariants'],
775774
variantPlugins['forcedColorsVariants'],
775+
variantPlugins['printVariant'],
776776
]
777777

778+
// This is a compatibility fix for the pre 3.4 dark mode behavior
779+
// `class` retains the old behavior, but `selector` keeps the new behavior
780+
let isLegacyDarkMode =
781+
context.tailwindConfig.darkMode === 'class' ||
782+
(Array.isArray(context.tailwindConfig.darkMode) &&
783+
context.tailwindConfig.darkMode[0] === 'class')
784+
785+
if (isLegacyDarkMode) {
786+
afterVariants = [
787+
variantPlugins['supportsVariants'],
788+
variantPlugins['reducedMotionVariants'],
789+
variantPlugins['prefersContrastVariants'],
790+
variantPlugins['darkVariants'],
791+
variantPlugins['screenVariants'],
792+
variantPlugins['orientationVariants'],
793+
variantPlugins['directionVariants'],
794+
variantPlugins['forcedColorsVariants'],
795+
variantPlugins['printVariant'],
796+
]
797+
}
798+
778799
return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
779800
}
780801

src/util/pseudoElements.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ let elementProperties = {
6060
':first-letter': ['terminal', 'jumpable'],
6161
':first-line': ['terminal', 'jumpable'],
6262

63+
':where': [],
64+
':is': [],
65+
':has': [],
66+
6367
// The default value is used when the pseudo-element is not recognized
6468
// Because it's not recognized, we don't know if it's terminal or not
6569
// So we assume it can be moved AND can have user-action pseudo classes attached to it

tests/apply.test.js

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ crosscheck(({ stable, oxide }) => {
3535

3636
test('@apply', () => {
3737
let config = {
38-
darkMode: 'class',
38+
darkMode: 'selector',
3939
content: [{ raw: sharedHtml }],
4040
}
4141

@@ -216,14 +216,14 @@ crosscheck(({ stable, oxide }) => {
216216
text-align: left;
217217
}
218218
}
219-
:is(:where(.dark) .apply-dark-variant) {
219+
.apply-dark-variant:where(.dark, .dark *) {
220220
text-align: center;
221221
}
222-
:is(:where(.dark) .apply-dark-variant:hover) {
222+
.apply-dark-variant:hover:where(.dark, .dark *) {
223223
text-align: right;
224224
}
225225
@media (min-width: 1024px) {
226-
:is(:where(.dark) .apply-dark-variant) {
226+
.apply-dark-variant:where(.dark, .dark *) {
227227
text-align: left;
228228
}
229229
}
@@ -513,14 +513,14 @@ crosscheck(({ stable, oxide }) => {
513513
text-align: left;
514514
}
515515
}
516-
:is(:where(.dark) .apply-dark-variant) {
516+
.apply-dark-variant:where(.dark, .dark *) {
517517
text-align: center;
518518
}
519-
:is(:where(.dark) .apply-dark-variant:hover) {
519+
.apply-dark-variant:hover:where(.dark, .dark *) {
520520
text-align: right;
521521
}
522522
@media (min-width: 1024px) {
523-
:is(:where(.dark) .apply-dark-variant) {
523+
.apply-dark-variant:where(.dark, .dark *) {
524524
text-align: left;
525525
}
526526
}
@@ -755,7 +755,7 @@ crosscheck(({ stable, oxide }) => {
755755

756756
test('@apply error with unknown utility', async () => {
757757
let config = {
758-
darkMode: 'class',
758+
darkMode: 'selector',
759759
content: [{ raw: sharedHtml }],
760760
}
761761

@@ -775,7 +775,7 @@ crosscheck(({ stable, oxide }) => {
775775

776776
test('@apply error with nested @screen', async () => {
777777
let config = {
778-
darkMode: 'class',
778+
darkMode: 'selector',
779779
content: [{ raw: sharedHtml }],
780780
}
781781

@@ -799,7 +799,7 @@ crosscheck(({ stable, oxide }) => {
799799

800800
test('@apply error with nested @anyatrulehere', async () => {
801801
let config = {
802-
darkMode: 'class',
802+
darkMode: 'selector',
803803
content: [{ raw: sharedHtml }],
804804
}
805805

@@ -823,7 +823,7 @@ crosscheck(({ stable, oxide }) => {
823823

824824
test('@apply error when using .group utility', async () => {
825825
let config = {
826-
darkMode: 'class',
826+
darkMode: 'selector',
827827
content: [{ raw: '<div class="foo"></div>' }],
828828
}
829829

@@ -846,7 +846,7 @@ crosscheck(({ stable, oxide }) => {
846846
test('@apply error when using a prefixed .group utility', async () => {
847847
let config = {
848848
prefix: 'tw-',
849-
darkMode: 'class',
849+
darkMode: 'selector',
850850
content: [{ raw: html`<div class="foo"></div>` }],
851851
}
852852

@@ -868,7 +868,7 @@ crosscheck(({ stable, oxide }) => {
868868

869869
test('@apply error when using .peer utility', async () => {
870870
let config = {
871-
darkMode: 'class',
871+
darkMode: 'selector',
872872
content: [{ raw: '<div class="foo"></div>' }],
873873
}
874874

@@ -891,7 +891,7 @@ crosscheck(({ stable, oxide }) => {
891891
test('@apply error when using a prefixed .peer utility', async () => {
892892
let config = {
893893
prefix: 'tw-',
894-
darkMode: 'class',
894+
darkMode: 'selector',
895895
content: [{ raw: html`<div class="foo"></div>` }],
896896
}
897897

@@ -2360,7 +2360,7 @@ crosscheck(({ stable, oxide }) => {
23602360

23612361
it('pseudo elements inside apply are moved outside of :is() or :has()', () => {
23622362
let config = {
2363-
darkMode: 'class',
2363+
darkMode: 'selector',
23642364
content: [
23652365
{
23662366
raw: html` <div class="foo bar baz qux steve bob"></div> `,
@@ -2404,18 +2404,18 @@ crosscheck(({ stable, oxide }) => {
24042404

24052405
return run(input, config).then((result) => {
24062406
expect(result.css).toMatchFormattedCss(css`
2407-
:is(:where(.dark) .foo)::before,
2408-
:is(:where([dir='rtl']) :is(:where(.dark) .bar))::before,
2409-
:is(:where([dir='rtl']) :is(:where(.dark) .baz:hover))::before {
2407+
.foo:where(.dark, .dark *)::before,
2408+
.bar:where(.dark, .dark *):where([dir='rtl'], [dir='rtl'] *)::before,
2409+
.baz:hover:where(.dark, .dark *):where([dir='rtl'], [dir='rtl'] *)::before {
24102410
background-color: #000;
24112411
}
2412-
:is(:where([dir='rtl']) :is(:where(.dark) .qux))::file-selector-button:hover {
2412+
.qux:where(.dark, .dark *):where([dir='rtl'], [dir='rtl'] *)::file-selector-button:hover {
24132413
background-color: #000;
24142414
}
2415-
:is(:where([dir='rtl']) :is(:where(.dark) .steve):hover):before {
2415+
.steve:where(.dark, .dark *):hover:where([dir='rtl'], [dir='rtl'] *):before {
24162416
background-color: #000;
24172417
}
2418-
:is(:where([dir='rtl']) :is(:where(.dark) .bob))::file-selector-button:hover {
2418+
.bob:where(.dark, .dark *):hover:where([dir='rtl'], [dir='rtl'] *)::file-selector-button {
24192419
background-color: #000;
24202420
}
24212421
:has([dir='rtl'] .foo:hover):before {
@@ -2430,7 +2430,7 @@ crosscheck(({ stable, oxide }) => {
24302430

24312431
stable.test('::ng-deep, ::deep, ::v-deep pseudo elements are left alone', () => {
24322432
let config = {
2433-
darkMode: 'class',
2433+
darkMode: 'selector',
24342434
content: [
24352435
{
24362436
raw: html` <div class="foo bar"></div> `,

tests/custom-separator.test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { crosscheck, run, html, css } from './util/run'
33
crosscheck(() => {
44
test('custom separator', () => {
55
let config = {
6-
darkMode: 'class',
6+
darkMode: 'selector',
77
content: [
88
{
99
raw: html`
@@ -33,10 +33,10 @@ crosscheck(() => {
3333
text-align: right;
3434
}
3535
}
36-
:is(:where([dir='rtl']) .rtl_active_text-center:active) {
36+
.rtl_active_text-center:active:where([dir='rtl'], [dir='rtl'] *) {
3737
text-align: center;
3838
}
39-
:is(:where(.dark) .dark_focus_text-left:focus) {
39+
.dark_focus_text-left:focus:where(.dark, .dark *) {
4040
text-align: left;
4141
}
4242
`)
@@ -45,7 +45,7 @@ crosscheck(() => {
4545

4646
test('dash is not supported', () => {
4747
let config = {
48-
darkMode: 'class',
48+
darkMode: 'selector',
4949
content: [{ raw: 'lg-hover-font-bold' }],
5050
separator: '-',
5151
}

0 commit comments

Comments
 (0)