From a116f56b05abf9effd3a276d1d89b42840e8e216 Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Tue, 30 May 2023 11:37:36 -0400 Subject: [PATCH 1/3] Add `has-*` variants for `:has(...)` pseudo-class --- src/corePlugins.js | 20 +++++ src/lib/setupContextUtils.js | 1 + tests/arbitrary-variants.test.js | 129 +++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) diff --git a/src/corePlugins.js b/src/corePlugins.js index fc373a77f38f..15b4e8d0354d 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -386,6 +386,26 @@ export let variantPlugins = { ) }, + hasVariants: ({ matchVariant }) => { + matchVariant('has', (value) => `&:has(${normalize(value)})`, { values: {} }) + matchVariant( + 'group-has', + (value, { modifier }) => + modifier + ? `:merge(.group\\/${modifier}):has(${normalize(value)}) &` + : `:merge(.group):has(${normalize(value)}) &`, + { values: {} } + ) + matchVariant( + 'peer-has', + (value, { modifier }) => + modifier + ? `:merge(.peer\\/${modifier}):has(${normalize(value)}) ~ &` + : `:merge(.peer):has(${normalize(value)}) ~ &`, + { values: {} } + ) + }, + ariaVariants: ({ matchVariant, theme }) => { matchVariant('aria', (value) => `&[aria-${normalize(value)}]`, { values: theme('aria') ?? {} }) matchVariant( diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index e8026a50e0b8..0d6bc2509ded 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -744,6 +744,7 @@ function resolvePlugins(context, root) { let beforeVariants = [ variantPlugins['pseudoElementVariants'], variantPlugins['pseudoClassVariants'], + variantPlugins['hasVariants'], variantPlugins['ariaVariants'], variantPlugins['dataVariants'], ] diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js index 1e37a5047ef3..dc023b6cdd0e 100644 --- a/tests/arbitrary-variants.test.js +++ b/tests/arbitrary-variants.test.js @@ -772,6 +772,135 @@ it('should support supports', () => { }) }) +test('has-* variants with arbitrary values', () => { + let config = { + theme: {}, + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .has-\[figcaption\]:has(figcaption) { + text-decoration: underline; + } + .has-\[\.foo\:hover\]\:block:has(.foo:hover) { + display: block; + } + .has-\[\[data-active\]\]\:inline:has([data-active]) { + display: inline; + } + .has-\[\.foo\]\:flex:has(.foo) { + display: flex; + } + .has-\[\>_\.potato\]\:table:has(> .potato) { + display: table; + } + .has-\[\+_h2\]\:grid:has(+ h2) { + display: grid; + } + .has-\[\>_h1_\+_h2\]\:contents:has(> h1 + h2) { + display: contents; + } + .has-\[h2\]\:has-\[\.banana\]\:hidden:has(.banana):has(h2) { + display: none; + } + .has-\[figcaption\]\:underline:has(figcaption) { + text-decoration-line: underline; + } + `) + }) +}) + +test('group-has-* variants with arbitrary values', () => { + let config = { + theme: {}, + content: [ + { + raw: html` +
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .group:has(> h1 + .foo) .group-has-\[\>_h1_\+_\.foo\]\:block { + display: block; + } + .group\/two:has(> h1 + .foo) .group-has-\[\>_h1_\+_\.foo\]\/two\:flex { + display: flex; + } + `) + }) +}) + +test('peer-has-* variants with arbitrary values', () => { + let config = { + theme: {}, + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .peer:has(> h1 + .foo) ~ .peer-has-\[\>_h1_\+_\.foo\]\:block { + display: block; + } + .peer\/two:has(> h1 + .foo) ~ .peer-has-\[\>_h1_\+_\.foo\]\/two\:flex { + display: flex; + } + `) + }) +}) + it('should be possible to use modifiers and arbitrary groups', () => { let config = { content: [ From b84608f2066c61861af9bbebb1efbb8607cf17b5 Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Tue, 30 May 2023 11:55:54 -0400 Subject: [PATCH 2/3] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bf9d5b783b1..cc45a59f34fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix parsing of `theme()` inside `calc()` when there are no spaces around operators ([#11157](https://github.com/tailwindlabs/tailwindcss/pull/11157)) - Ensure `repeating-conic-gradient` is detected as an image ([#11180](https://github.com/tailwindlabs/tailwindcss/pull/11180)) - Remove `autoprefixer` dependency ([#11315](https://github.com/tailwindlabs/tailwindcss/pull/11315)) +- Add `has-*` variants for `:has(...)` pseudo-class ([#11318](https://github.com/tailwindlabs/tailwindcss/pull/11318)) ### Added From 4b83235a03ebbd5677e4c5174cbb459c116fe22f Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Tue, 30 May 2023 12:08:51 -0400 Subject: [PATCH 3/3] Fix mistake in test --- tests/arbitrary-variants.test.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js index dc023b6cdd0e..c49376139a7c 100644 --- a/tests/arbitrary-variants.test.js +++ b/tests/arbitrary-variants.test.js @@ -779,7 +779,7 @@ test('has-* variants with arbitrary values', () => { { raw: html`
-
+
@@ -800,12 +800,12 @@ test('has-* variants with arbitrary values', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` - .has-\[figcaption\]:has(figcaption) { - text-decoration: underline; - } .has-\[\.foo\:hover\]\:block:has(.foo:hover) { display: block; } + .has-\[figcaption\]\:inline-block:has(figcaption) { + display: inline-block; + } .has-\[\[data-active\]\]\:inline:has([data-active]) { display: inline; } @@ -824,9 +824,6 @@ test('has-* variants with arbitrary values', () => { .has-\[h2\]\:has-\[\.banana\]\:hidden:has(.banana):has(h2) { display: none; } - .has-\[figcaption\]\:underline:has(figcaption) { - text-decoration-line: underline; - } `) }) })