Skip to content

Commit

Permalink
implement not variant with @media, @supports and @container v…
Browse files Browse the repository at this point in the history
…ariants
  • Loading branch information
RobinMalfait authored and thecrypticace committed Jul 16, 2024
1 parent 23e6d43 commit b1f3ff3
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 3 deletions.
18 changes: 16 additions & 2 deletions packages/tailwindcss/src/candidate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
}
Expand Down
83 changes: 83 additions & 0 deletions packages/tailwindcss/src/variants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand Down
27 changes: 26 additions & 1 deletion packages/tailwindcss/src/variants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,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) => {
Expand Down

0 comments on commit b1f3ff3

Please sign in to comment.