implement not variant with @media, @supports and @container variants

This commit is contained in:
Robin Malfait 2024-07-12 15:55:15 +02:00 • committed by Jordan Pittman
parent 5ebd5896d7
commit 5f3eced44c
3 changed files with 125 additions and 3 deletions

View file

@ -611,14 +611,28 @@ export function parseVariant(variant: string, designSystem: DesignSystem): Varia
let subVariant = designSystem.parseVariant(value)
if (subVariant === null) return null
if (subVariant.compounds === false) return null
let compounds: boolean | null = null
// Special case: allow compounding in `not` because we can invert
// `@media` queries.
if (root === 'not' && subVariant.compounds === false) {
// However, we do not allow further compounding. E.g.:
// `group-not-print` is invalid.
compounds = false
}
// Discard the variant if it's not allowed to be compounded.
else if (subVariant.compounds === false) {
return null
}
return {
kind: 'compound',
root,
modifier: modifier === null ? null : { kind: 'named', value: modifier },
variant: subVariant,
compounds: designSystem.variants.compounds(root),
compounds: compounds ?? designSystem.variants.compounds(root),
}
}
}

View file

@ -1508,6 +1508,89 @@ test('not', () => {
expect(run(['not-[:checked]/foo:flex'])).toEqual('')
})
test('not with media queries', () => {
expect(
compileCss(
css`
@theme {
--breakpoint-lg: 1024px;
}
@tailwind utilities;
`,
['not-print:flex', 'not-dark:flex', 'not-lg:flex'],
),
).toMatchInlineSnapshot(`
":root {
--breakpoint-lg: 1024px;
}
@media not (width >= 1024px) {
.not-lg\\:flex {
display: flex;
}
}
@media not (prefers-color-scheme: dark) {
.not-dark\\:flex {
display: flex;
}
}
@media not print {
.not-print\\:flex {
display: flex;
}
}"
`)
expect(
run(['group-not-dark:flex', 'peer-not-dark:flex', 'not-print/foo:flex', 'not-dark/foo:flex']),
).toEqual('')
})
test('not with support queries', () => {
expect(run(['not-supports-[display:grid]:flex'])).toMatchInlineSnapshot(`
"@supports not (display: grid) {
.not-supports-\\[display\\:grid\\]\\:flex {
display: flex;
}
}"
`)
expect(
run([
'group-not-supports-[display:grid]:flex',
'peer-not-supports-[display:grid]:flex',
'not-supports-[display:grid]/foo:flex',
]),
).toEqual('')
})
test('not with container queries', () => {
expect(
compileCss(
css`
@theme {
--width-lg: 1024px;
}
@tailwind utilities;
`,
['not-@lg:flex', 'not-@lg/foo:flex'],
),
).toMatchInlineSnapshot(`
":root {
--width-lg: 1024px;
}
@container not (width >= 1024px) {
.not-\\@lg\\:flex {
display: flex;
}
}"
`)
expect(
run(['group-not-@lg:flex', 'peer-not-@lg:flex', 'not-@lg/foo:flex', '@lg/foo:flex']),
).toEqual('')
})
test('has', () => {
expect(
run([

View file

@ -209,7 +209,32 @@ export function createVariants(theme: Theme): Variants {
variants.compound('not', (ruleNode, variant) => {
if (variant.modifier) return null
ruleNode.selector = `&:not(${ruleNode.selector.replace('&', '*')})`
// At-rules
if (ruleNode.selector[0] === '@') {
// @media
if (ruleNode.selector.startsWith('@media ')) {
let queries = segment(ruleNode.selector.slice(7), ',')
ruleNode.selector = `@media ${queries.map((query) => `not ${query}`).join(',')}`
}
// @supports
else if (ruleNode.selector.startsWith('@supports ')) {
let queries = segment(ruleNode.selector.slice(10), ',')
ruleNode.selector = `@supports ${queries.map((query) => `not ${query}`).join(',')}`
}
// @container
else if (ruleNode.selector.startsWith('@container ')) {
let queries = segment(ruleNode.selector.slice(11), ',')
ruleNode.selector = `@container ${queries.map((query) => `not ${query}`).join(',')}`
}
}
// Selectors
else {
ruleNode.selector = `&:not(${ruleNode.selector.replace('&', '*')})`
}
})
variants.compound('group', (ruleNode, variant) => {