From faf0669a27e5bc9decabdb2ec94529df57d59047 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Sun, 11 Sep 2022 17:56:15 -0400 Subject: [PATCH 1/3] Replaces classes in utility selectors like :where and :has --- src/util/formatVariantSelector.js | 31 ++++++++++--- tests/variants.test.js | 73 +++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 7 deletions(-) diff --git a/src/util/formatVariantSelector.js b/src/util/formatVariantSelector.js index 47d9ade7a4fc..ffcc5f037eed 100644 --- a/src/util/formatVariantSelector.js +++ b/src/util/formatVariantSelector.js @@ -81,6 +81,29 @@ function resortSelector(sel) { return sel } +function eliminateIrrelevantSelectors(sel, base) { + let hasClassesMatchingCandidate = false + + sel.walk((child) => { + if (child.type === 'class' && child.value === base) { + hasClassesMatchingCandidate = true + return false // Stop walking + } + }) + + if (!hasClassesMatchingCandidate) { + sel.remove() + } + + // We do NOT recursively eliminate sub selectors that don't have the base class + // as this is NOT a safe operation. For example, if we have: + // `.space-x-2 > :not([hidden]) ~ :not([hidden])` + // We cannot remove the [hidden] from the :not() because it would change the + // meaning of the selector. + + // TODO: Can we do this for :matches, :is, and :where? +} + export function finalizeSelector( format, { @@ -115,13 +138,7 @@ export function finalizeSelector( // Remove extraneous selectors that do not include the base class/candidate being matched against // For example if we have a utility defined `.a, .b { color: red}` // And the formatted variant is sm:b then we want the final selector to be `.sm\:b` and not `.a, .sm\:b` - ast.each((node) => { - let hasClassesMatchingCandidate = node.some((n) => n.type === 'class' && n.value === base) - - if (!hasClassesMatchingCandidate) { - node.remove() - } - }) + ast.each((sel) => eliminateIrrelevantSelectors(sel, base)) // Normalize escaped classes, e.g.: // diff --git a/tests/variants.test.js b/tests/variants.test.js index 5d9131786920..5e1f9818ea20 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -944,3 +944,76 @@ test('multi-class utilities handle selector-mutating variants correctly', () => `) }) }) + +test('class inside pseudo-class function :has', () => { + let config = { + content: [ + { raw: html`
` }, + // { raw: html`
` }, + // { raw: html`
` }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + :where(.foo) { + color: red; + } + :matches(.foo, .bar, .baz) { + color: orange; + } + :is(.foo) { + color: yellow; + } + html:has(.foo) { + color: green; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + :where(.foo) { + color: red; + } + :matches(.foo, .bar, .baz) { + color: orange; + } + :is(.foo) { + color: yellow; + } + html:has(.foo) { + color: green; + } + + :where(.hover\:foo:hover) { + color: red; + } + :matches(.hover\:foo:hover, .bar, .baz) { + color: orange; + } + :is(.hover\:foo:hover) { + color: yellow; + } + html:has(.hover\:foo:hover) { + color: green; + } + @media (min-width: 640px) { + :where(.sm\:foo) { + color: red; + } + :matches(.sm\:foo, .bar, .baz) { + color: orange; + } + :is(.sm\:foo) { + color: yellow; + } + html:has(.sm\:foo) { + color: green; + } + } + `) + }) +}) From e62fe589064151bdedc601a95957175ec4ba750d Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Sun, 11 Sep 2022 17:58:42 -0400 Subject: [PATCH 2/3] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 596245edc9d8..a3d2cf2bc40e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Handle variants on complex selector utilities ([#9262](https://github.com/tailwindlabs/tailwindcss/pull/9262)) - Don't mutate shared config objects ([#9294](https://github.com/tailwindlabs/tailwindcss/pull/9294)) - Fix ordering of parallel variants ([#9282](https://github.com/tailwindlabs/tailwindcss/pull/9282)) +- Handle variants in utility selectors using `:where()` and `:has()` ([#9309](https://github.com/tailwindlabs/tailwindcss/pull/9309)) ## [3.1.8] - 2022-08-05 From b497f6ce540e451cb3ed212453cb5f1ab051e602 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 12 Sep 2022 10:29:53 -0400 Subject: [PATCH 3/3] wip --- tests/variants.test.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/variants.test.js b/tests/variants.test.js index 5e1f9818ea20..45584fc3614a 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -949,8 +949,8 @@ test('class inside pseudo-class function :has', () => { let config = { content: [ { raw: html`
` }, - // { raw: html`
` }, - // { raw: html`
` }, + { raw: html`
` }, + { raw: html`
` }, ], corePlugins: { preflight: false }, } @@ -994,6 +994,12 @@ test('class inside pseudo-class function :has', () => { :matches(.hover\:foo:hover, .bar, .baz) { color: orange; } + :matches(.foo, .hover\:bar:hover, .baz) { + color: orange; + } + :matches(.foo, .bar, .hover\:baz:hover) { + color: orange; + } :is(.hover\:foo:hover) { color: yellow; } @@ -1007,6 +1013,12 @@ test('class inside pseudo-class function :has', () => { :matches(.sm\:foo, .bar, .baz) { color: orange; } + :matches(.foo, .sm\:bar, .baz) { + color: orange; + } + :matches(.foo, .bar, .sm\:baz) { + color: orange; + } :is(.sm\:foo) { color: yellow; }