mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
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 185250438ccb2f61ba876d4676823c1807891122. * 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 <4323180+adamwathan@users.noreply.github.com>
This commit is contained in:
parent
78fedd5cc0
commit
3fb57e55ab
@ -11,6 +11,15 @@ 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 `class` dark mode strategy ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717))
|
||||
|
||||
### Added
|
||||
|
||||
- Add new `selector` and `variant` strategies for dark mode ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717))
|
||||
|
||||
### Changed
|
||||
|
||||
- Support `rtl` and `ltr` variants on same element as `dir` attribute ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717))
|
||||
|
||||
## [3.4.0] - 2023-12-19
|
||||
|
||||
|
||||
@ -207,8 +207,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 }) => {
|
||||
@ -217,7 +217,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'
|
||||
@ -228,10 +228,49 @@ export let variantPlugins = {
|
||||
])
|
||||
}
|
||||
|
||||
if (mode === 'class') {
|
||||
addVariant('dark', `:is(:where(${className}) &)`)
|
||||
if (mode === 'variant') {
|
||||
let formats
|
||||
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[]
|
||||
// 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 &"]`',
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selector = formats
|
||||
}
|
||||
|
||||
if (mode === 'selector') {
|
||||
// New preferred behavior
|
||||
addVariant('dark', `&:where(${selector}, ${selector} *)`)
|
||||
} else if (mode === 'media') {
|
||||
addVariant('dark', '@media (prefers-color-scheme: dark)')
|
||||
} else if (mode === 'variant') {
|
||||
addVariant('dark', selector)
|
||||
} else if (mode === 'class') {
|
||||
// Old behavior
|
||||
addVariant('dark', `:is(${selector} &)`)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -767,14 +767,35 @@ 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
|
||||
// `class` retains the old behavior, but `selector` keeps the new behavior
|
||||
let isLegacyDarkMode =
|
||||
context.tailwindConfig.darkMode === 'class' ||
|
||||
(Array.isArray(context.tailwindConfig.darkMode) &&
|
||||
context.tailwindConfig.darkMode[0] === 'class')
|
||||
|
||||
if (isLegacyDarkMode) {
|
||||
afterVariants = [
|
||||
variantPlugins['supportsVariants'],
|
||||
variantPlugins['reducedMotionVariants'],
|
||||
variantPlugins['prefersContrastVariants'],
|
||||
variantPlugins['darkVariants'],
|
||||
variantPlugins['screenVariants'],
|
||||
variantPlugins['orientationVariants'],
|
||||
variantPlugins['directionVariants'],
|
||||
variantPlugins['forcedColorsVariants'],
|
||||
variantPlugins['printVariant'],
|
||||
]
|
||||
}
|
||||
|
||||
return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -35,7 +35,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
test('@apply', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [{ raw: sharedHtml }],
|
||||
}
|
||||
|
||||
@ -216,14 +216,14 @@ crosscheck(({ stable, oxide }) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -513,14 +513,14 @@ crosscheck(({ stable, oxide }) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -755,7 +755,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
test('@apply error with unknown utility', async () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [{ raw: sharedHtml }],
|
||||
}
|
||||
|
||||
@ -775,7 +775,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
test('@apply error with nested @screen', async () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [{ raw: sharedHtml }],
|
||||
}
|
||||
|
||||
@ -799,7 +799,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
test('@apply error with nested @anyatrulehere', async () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [{ raw: sharedHtml }],
|
||||
}
|
||||
|
||||
@ -823,7 +823,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
test('@apply error when using .group utility', async () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [{ raw: '<div class="foo"></div>' }],
|
||||
}
|
||||
|
||||
@ -846,7 +846,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
test('@apply error when using a prefixed .group utility', async () => {
|
||||
let config = {
|
||||
prefix: 'tw-',
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [{ raw: html`<div class="foo"></div>` }],
|
||||
}
|
||||
|
||||
@ -868,7 +868,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
test('@apply error when using .peer utility', async () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [{ raw: '<div class="foo"></div>' }],
|
||||
}
|
||||
|
||||
@ -891,7 +891,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
test('@apply error when using a prefixed .peer utility', async () => {
|
||||
let config = {
|
||||
prefix: 'tw-',
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [{ raw: html`<div class="foo"></div>` }],
|
||||
}
|
||||
|
||||
@ -2360,7 +2360,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
it('pseudo elements inside apply are moved outside of :is() or :has()', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html` <div class="foo bar baz qux steve bob"></div> `,
|
||||
@ -2404,18 +2404,18 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
:is(:where(.dark) .foo)::before,
|
||||
:is(:where([dir='rtl']) :is(:where(.dark) .bar))::before,
|
||||
:is(:where([dir='rtl']) :is(:where(.dark) .baz:hover))::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;
|
||||
}
|
||||
: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;
|
||||
}
|
||||
:is(:where([dir='rtl']) :is(:where(.dark) .steve):hover):before {
|
||||
.steve:where(.dark, .dark *):hover:where([dir='rtl'], [dir='rtl'] *):before {
|
||||
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 {
|
||||
@ -2430,7 +2430,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
stable.test('::ng-deep, ::deep, ::v-deep pseudo elements are left alone', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html` <div class="foo bar"></div> `,
|
||||
|
||||
@ -3,7 +3,7 @@ import { crosscheck, run, html, css } from './util/run'
|
||||
crosscheck(() => {
|
||||
test('custom separator', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
@ -33,10 +33,10 @@ crosscheck(() => {
|
||||
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;
|
||||
}
|
||||
`)
|
||||
@ -45,7 +45,7 @@ crosscheck(() => {
|
||||
|
||||
test('dash is not supported', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [{ raw: 'lg-hover-font-bold' }],
|
||||
separator: '-',
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { crosscheck, run, html, css, defaults } from './util/run'
|
||||
|
||||
crosscheck(() => {
|
||||
crosscheck(({ oxide, stable }) => {
|
||||
it('should be possible to use the darkMode "class" mode', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
@ -17,7 +17,7 @@ crosscheck(() => {
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
${defaults}
|
||||
:is(:where(.dark) .dark\:font-bold) {
|
||||
:is(.dark .dark\:font-bold) {
|
||||
font-weight: 700;
|
||||
}
|
||||
`)
|
||||
@ -40,7 +40,7 @@ crosscheck(() => {
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
${defaults}
|
||||
:is(:where(.test-dark) .dark\:font-bold) {
|
||||
:is(.test-dark .dark\:font-bold) {
|
||||
font-weight: 700;
|
||||
}
|
||||
`)
|
||||
@ -120,4 +120,202 @@ crosscheck(() => {
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
it('should support the deprecated `class` dark mode behavior', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
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 deprecated `class` dark mode', () => {
|
||||
let config = {
|
||||
darkMode: ['class', '.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: class`', () => {
|
||||
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) => {
|
||||
stable.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));
|
||||
}
|
||||
}
|
||||
`)
|
||||
oxide.expect(result.css).toMatchFormattedCss(css`
|
||||
.hover\:text-green-200:hover {
|
||||
color: #bbf7d0;
|
||||
}
|
||||
:is(.dark .dark\:text-green-100) {
|
||||
color: #dcfce7;
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:text-green-300 {
|
||||
color: #86efac;
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
it('should use modern sorting otherwise', () => {
|
||||
let config = {
|
||||
darkMode: 'selector',
|
||||
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) => {
|
||||
stable.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));
|
||||
}
|
||||
}
|
||||
.dark\:text-green-100:where(.dark, .dark *) {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(220 252 231 / var(--tw-text-opacity));
|
||||
}
|
||||
`)
|
||||
oxide.expect(result.css).toMatchFormattedCss(css`
|
||||
.hover\:text-green-200:hover {
|
||||
color: #bbf7d0;
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:text-green-300 {
|
||||
color: #86efac;
|
||||
}
|
||||
}
|
||||
.dark\:text-green-100:where(.dark, .dark *) {
|
||||
color: #dcfce7;
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
it('should allow customization of the dark mode variant', () => {
|
||||
let config = {
|
||||
darkMode: ['variant', '&:not(.light *)'],
|
||||
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;
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
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 *),
|
||||
.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`<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 *),
|
||||
.dark\:font-bold:not(.extralight *) {
|
||||
font-weight: 700;
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -8,7 +8,7 @@ crosscheck(() => {
|
||||
test('important boolean', () => {
|
||||
let config = {
|
||||
important: true,
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
@ -148,10 +148,10 @@ crosscheck(() => {
|
||||
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;
|
||||
}
|
||||
`)
|
||||
|
||||
@ -5,7 +5,7 @@ crosscheck(() => {
|
||||
let config = {
|
||||
important: false,
|
||||
prefix: 'tw-',
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`<!-- The string "!*" can cause problems if we don't handle it, let's include it -->
|
||||
|
||||
@ -4,7 +4,7 @@ crosscheck(() => {
|
||||
test('important modifier', () => {
|
||||
let config = {
|
||||
important: false,
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
|
||||
@ -4,7 +4,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
test('important selector', () => {
|
||||
let config = {
|
||||
important: '#app',
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
@ -146,19 +146,22 @@ crosscheck(({ stable, oxide }) => {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
#app :is(:is(:where([dir='rtl']) .rtl\:active\:text-center:active)) {
|
||||
#app :is(.rtl\:active\:text-center:active:where([dir='rtl'], [dir='rtl'] *)) {
|
||||
text-align: center;
|
||||
}
|
||||
#app :is(:where(.dark) .dark\:before\:underline):before {
|
||||
#app :is(.dark\:before\:underline:where(.dark, .dark *)):before {
|
||||
content: var(--tw-content);
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
#app :is(:is(:where(.dark) .dark\:focus\:text-left:focus)) {
|
||||
#app :is(.dark\:focus\:text-left:focus:where(.dark, .dark *)) {
|
||||
text-align: left;
|
||||
}
|
||||
#app
|
||||
:is(
|
||||
:where([dir='rtl']) :is(:where(.dark) .hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100)
|
||||
.hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100:where(
|
||||
.dark,
|
||||
.dark *
|
||||
):where([dir='rtl'], [dir='rtl'] *)
|
||||
)::file-selector-button:hover {
|
||||
background-color: #000;
|
||||
}
|
||||
@ -169,7 +172,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
test('pseudo-elements are appended after the `:is()`', () => {
|
||||
let config = {
|
||||
important: '#app',
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html` <div class="dark:before:bg-black"></div> `,
|
||||
@ -187,7 +190,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
return run(input, config).then((result) => {
|
||||
stable.expect(result.css).toMatchFormattedCss(css`
|
||||
${defaults}
|
||||
#app :is(:where(.dark) .dark\:before\:bg-black)::before {
|
||||
#app .dark\:before\:bg-black:where(.dark, .dark *)::before {
|
||||
content: var(--tw-content);
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
@ -195,7 +198,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
`)
|
||||
oxide.expect(result.css).toMatchFormattedCss(css`
|
||||
${defaults}
|
||||
#app :is(:where(.dark) .dark\:before\:bg-black)::before {
|
||||
#app .dark\:before\:bg-black:where(.dark, .dark *)::before {
|
||||
content: var(--tw-content);
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { crosscheck, run, html, css, defaults } from './util/run'
|
||||
crosscheck(({ stable, oxide }) => {
|
||||
test('it works', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
@ -304,8 +304,10 @@ crosscheck(({ stable, oxide }) => {
|
||||
margin-right: auto;
|
||||
}
|
||||
.drop-empty-rules:hover,
|
||||
.group:hover .apply-group,
|
||||
:is(:where(.dark) .apply-dark-mode) {
|
||||
.group:hover .apply-group {
|
||||
font-weight: 700;
|
||||
}
|
||||
.apply-dark-mode:where(.dark, .dark *) {
|
||||
font-weight: 700;
|
||||
}
|
||||
.apply-with-existing:hover {
|
||||
@ -340,7 +342,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
.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));
|
||||
}
|
||||
@ -802,12 +804,12 @@ crosscheck(({ stable, oxide }) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -877,8 +879,10 @@ crosscheck(({ stable, oxide }) => {
|
||||
margin-right: auto;
|
||||
}
|
||||
.drop-empty-rules:hover,
|
||||
.group:hover .apply-group,
|
||||
:is(:where(.dark) .apply-dark-mode) {
|
||||
.group:hover .apply-group {
|
||||
font-weight: 700;
|
||||
}
|
||||
.apply-dark-mode:where(.dark, .dark *) {
|
||||
font-weight: 700;
|
||||
}
|
||||
.apply-with-existing:hover {
|
||||
@ -912,7 +916,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
.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 *) {
|
||||
background-color: #22c55e;
|
||||
}
|
||||
@media (min-width: 640px) {
|
||||
@ -1364,12 +1368,12 @@ crosscheck(({ stable, oxide }) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import { crosscheck, run, html, css } from './util/run'
|
||||
crosscheck(() => {
|
||||
test('modify selectors', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
|
||||
@ -3,7 +3,7 @@ import { crosscheck, run, html, css } from './util/run'
|
||||
crosscheck(({ stable, oxide }) => {
|
||||
test('opacity', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
@ -43,7 +43,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
test('colors defined as functions work when opacity plugins are disabled', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
|
||||
@ -5,7 +5,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
stable.test('prefix', () => {
|
||||
let config = {
|
||||
prefix: 'tw-',
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
@ -128,7 +128,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
.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 {
|
||||
@ -171,14 +171,14 @@ crosscheck(({ stable, oxide }) => {
|
||||
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;
|
||||
}
|
||||
`)
|
||||
|
||||
@ -3,25 +3,25 @@ import { applyImportantSelector } from '../../src/util/applyImportantSelector'
|
||||
|
||||
crosscheck(() => {
|
||||
it.each`
|
||||
before | after
|
||||
${'.foo'} | ${'#app :is(.foo)'}
|
||||
${'.foo .bar'} | ${'#app :is(.foo .bar)'}
|
||||
${'.foo:hover'} | ${'#app :is(.foo:hover)'}
|
||||
${'.foo .bar:hover'} | ${'#app :is(.foo .bar:hover)'}
|
||||
${'.foo::before'} | ${'#app :is(.foo)::before'}
|
||||
${'.foo::before'} | ${'#app :is(.foo)::before'}
|
||||
${'.foo::file-selector-button'} | ${'#app :is(.foo)::file-selector-button'}
|
||||
${'.foo::-webkit-progress-bar'} | ${'#app :is(.foo)::-webkit-progress-bar'}
|
||||
${'.foo:hover::before'} | ${'#app :is(.foo:hover)::before'}
|
||||
before | after
|
||||
${'.foo'} | ${'#app :is(.foo)'}
|
||||
${'.foo .bar'} | ${'#app :is(.foo .bar)'}
|
||||
${'.foo:hover'} | ${'#app :is(.foo:hover)'}
|
||||
${'.foo .bar:hover'} | ${'#app :is(.foo .bar:hover)'}
|
||||
${'.foo::before'} | ${'#app :is(.foo)::before'}
|
||||
${'.foo::before'} | ${'#app :is(.foo)::before'}
|
||||
${'.foo::file-selector-button'} | ${'#app :is(.foo)::file-selector-button'}
|
||||
${'.foo::-webkit-progress-bar'} | ${'#app :is(.foo)::-webkit-progress-bar'}
|
||||
${'.foo:hover::before'} | ${'#app :is(.foo:hover)::before'}
|
||||
${':is(:where(.dark) :is(:where([dir="rtl"]) .foo::before))'} | ${'#app :is(:where(.dark) :is(:where([dir="rtl"]) .foo))::before'}
|
||||
${':is(:where(.dark) .foo) .bar'} | ${'#app :is(:is(:where(.dark) .foo) .bar)'}
|
||||
${':is(.foo) :is(.bar)'} | ${'#app :is(:is(.foo) :is(.bar))'}
|
||||
${':is(.foo)::before'} | ${'#app :is(.foo)::before'}
|
||||
${'.foo:before'} | ${'#app :is(.foo):before'}
|
||||
${'.foo::some-uknown-pseudo'} | ${'#app :is(.foo)::some-uknown-pseudo'}
|
||||
${'.foo::some-uknown-pseudo:hover'} | ${'#app :is(.foo)::some-uknown-pseudo:hover'}
|
||||
${'.foo:focus::some-uknown-pseudo:hover'} | ${'#app :is(.foo:focus)::some-uknown-pseudo:hover'}
|
||||
${'.foo:hover::some-uknown-pseudo:focus'} | ${'#app :is(.foo:hover)::some-uknown-pseudo:focus'}
|
||||
${':is(:where(.dark) .foo) .bar'} | ${'#app :is(:is(:where(.dark) .foo) .bar)'}
|
||||
${':is(.foo) :is(.bar)'} | ${'#app :is(:is(.foo) :is(.bar))'}
|
||||
${':is(.foo)::before'} | ${'#app :is(.foo)::before'}
|
||||
${'.foo:before'} | ${'#app :is(.foo):before'}
|
||||
${'.foo::some-uknown-pseudo'} | ${'#app :is(.foo)::some-uknown-pseudo'}
|
||||
${'.foo::some-uknown-pseudo:hover'} | ${'#app :is(.foo)::some-uknown-pseudo:hover'}
|
||||
${'.foo:focus::some-uknown-pseudo:hover'} | ${'#app :is(.foo:focus)::some-uknown-pseudo:hover'}
|
||||
${'.foo:hover::some-uknown-pseudo:focus'} | ${'#app :is(.foo:hover)::some-uknown-pseudo:focus'}
|
||||
`('should generate "$after" from "$before"', ({ before, after }) => {
|
||||
expect(applyImportantSelector(before, '#app')).toEqual(after)
|
||||
})
|
||||
|
||||
@ -319,11 +319,6 @@
|
||||
background-color: #fde047;
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
.print\:bg-yellow-300 {
|
||||
background-color: #fde047;
|
||||
}
|
||||
}
|
||||
@media (min-width: 640px) {
|
||||
.sm\:shadow-md,
|
||||
.sm\:active\:shadow-md:active {
|
||||
@ -389,26 +384,38 @@
|
||||
background-color: #fde047;
|
||||
}
|
||||
}
|
||||
:is(:where([dir="ltr"]) .ltr\:shadow-md),
|
||||
:is(:where([dir="rtl"]) .rtl\:shadow-md),
|
||||
:is(:where(.dark) .dark\:shadow-md),
|
||||
:is(
|
||||
:where(.dark)
|
||||
.group:disabled:focus:hover
|
||||
.dark\:group-disabled\:group-focus\:group-hover\:shadow-md
|
||||
),
|
||||
:is(
|
||||
:where(.dark)
|
||||
.peer:disabled:focus:hover
|
||||
~ .dark\:peer-disabled\:peer-focus\:peer-hover\:shadow-md
|
||||
) {
|
||||
.ltr\:shadow-md:where([dir="ltr"], [dir="ltr"] *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
.rtl\:shadow-md:where([dir="rtl"], [dir="rtl"] *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
.dark\:shadow-md:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
.group:disabled:focus:hover .dark\:group-disabled\:group-focus\:group-hover\:shadow-md:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
.peer:disabled:focus:hover ~ .dark\:peer-disabled\:peer-focus\:peer-hover\:shadow-md:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
:is(:where(.dark) .lg\:dark\:shadow-md) {
|
||||
.lg\:dark\:shadow-md:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color),
|
||||
0 2px 4px -2px var(--tw-shadow-color);
|
||||
@ -417,7 +424,7 @@
|
||||
}
|
||||
}
|
||||
@media (min-width: 1280px) {
|
||||
:is(:where(.dark) .xl\:dark\:disabled\:shadow-md:disabled) {
|
||||
.xl\:dark\:disabled\:shadow-md:disabled:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color),
|
||||
0 2px 4px -2px var(--tw-shadow-color);
|
||||
@ -427,7 +434,7 @@
|
||||
}
|
||||
@media (min-width: 1536px) {
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:is(:where(.dark) .\32 xl\:dark\:motion-safe\:focus-within\:shadow-md:focus-within) {
|
||||
.\32 xl\:dark\:motion-safe\:focus-within\:shadow-md:focus-within:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color),
|
||||
0 2px 4px -2px var(--tw-shadow-color);
|
||||
@ -441,3 +448,8 @@
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
.print\:bg-yellow-300 {
|
||||
background-color: #fde047;
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,12 +337,6 @@
|
||||
background-color: rgb(253 224 71 / var(--tw-bg-opacity));
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
.print\:bg-yellow-300 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(253 224 71 / var(--tw-bg-opacity));
|
||||
}
|
||||
}
|
||||
@media (min-width: 640px) {
|
||||
.sm\:shadow-md,
|
||||
.sm\:active\:shadow-md:active {
|
||||
@ -410,26 +404,38 @@
|
||||
background-color: rgb(253 224 71 / var(--tw-bg-opacity));
|
||||
}
|
||||
}
|
||||
:is(:where([dir="ltr"]) .ltr\:shadow-md),
|
||||
:is(:where([dir="rtl"]) .rtl\:shadow-md),
|
||||
:is(:where(.dark) .dark\:shadow-md),
|
||||
:is(
|
||||
:where(.dark)
|
||||
.group:disabled:focus:hover
|
||||
.dark\:group-disabled\:group-focus\:group-hover\:shadow-md
|
||||
),
|
||||
:is(
|
||||
:where(.dark)
|
||||
.peer:disabled:focus:hover
|
||||
~ .dark\:peer-disabled\:peer-focus\:peer-hover\:shadow-md
|
||||
) {
|
||||
.ltr\:shadow-md:where([dir="ltr"], [dir="ltr"] *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
.rtl\:shadow-md:where([dir="rtl"], [dir="rtl"] *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
.dark\:shadow-md:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
.group:disabled:focus:hover .dark\:group-disabled\:group-focus\:group-hover\:shadow-md:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
.peer:disabled:focus:hover ~ .dark\:peer-disabled\:peer-focus\:peer-hover\:shadow-md:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
:is(:where(.dark) .lg\:dark\:shadow-md) {
|
||||
.lg\:dark\:shadow-md:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color),
|
||||
0 2px 4px -2px var(--tw-shadow-color);
|
||||
@ -438,7 +444,7 @@
|
||||
}
|
||||
}
|
||||
@media (min-width: 1280px) {
|
||||
:is(:where(.dark) .xl\:dark\:disabled\:shadow-md:disabled) {
|
||||
.xl\:dark\:disabled\:shadow-md:disabled:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color),
|
||||
0 2px 4px -2px var(--tw-shadow-color);
|
||||
@ -448,7 +454,7 @@
|
||||
}
|
||||
@media (min-width: 1536px) {
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:is(:where(.dark) .\32 xl\:dark\:motion-safe\:focus-within\:shadow-md:focus-within) {
|
||||
.\32 xl\:dark\:motion-safe\:focus-within\:shadow-md:focus-within:where(.dark, .dark *) {
|
||||
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color),
|
||||
0 2px 4px -2px var(--tw-shadow-color);
|
||||
@ -461,4 +467,10 @@
|
||||
.forced-colors\:flex {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
.print\:bg-yellow-300 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(253 224 71 / var(--tw-bg-opacity));
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { crosscheck, run, html, css, defaults } from './util/run'
|
||||
crosscheck(({ stable, oxide }) => {
|
||||
test('variants', () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [path.resolve(__dirname, './variants.test.html')],
|
||||
corePlugins: { preflight: false },
|
||||
}
|
||||
@ -1156,7 +1156,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
test('stacking dark and rtl variants', async () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`<div class="dark:rtl:italic" />`,
|
||||
@ -1172,7 +1172,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
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;
|
||||
}
|
||||
`)
|
||||
@ -1180,7 +1180,7 @@ crosscheck(({ stable, oxide }) => {
|
||||
|
||||
test('stacking dark and rtl variants with pseudo elements', async () => {
|
||||
let config = {
|
||||
darkMode: 'class',
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
{
|
||||
raw: html`<div class="dark:rtl:placeholder:italic" />`,
|
||||
@ -1196,7 +1196,10 @@ crosscheck(({ stable, oxide }) => {
|
||||
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;
|
||||
}
|
||||
`)
|
||||
|
||||
7
types/config.d.ts
vendored
7
types/config.d.ts
vendored
@ -74,6 +74,13 @@ type DarkModeConfig =
|
||||
| 'class'
|
||||
// Use the `class` strategy with a custom class instead of `.dark`.
|
||||
| ['class', 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]
|
||||
// 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<string, string | Screen | Screen[]>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user