diff --git a/CHANGELOG.md b/CHANGELOG.md index 9daa0015903c..65a43f03aa1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Ensure max specificity of `0,0,1` for button and input Preflight rules ([#12735](https://github.com/tailwindlabs/tailwindcss/pull/12735)) +- Split `:has` rules when using `experimental.optimizeUniversalDefaults` ([#12736](https://github.com/tailwindlabs/tailwindcss/pull/12736)) ## [3.4.1] - 2014-01-05 diff --git a/src/lib/resolveDefaultsAtRules.js b/src/lib/resolveDefaultsAtRules.js index 389ea4bacac7..621cdaf6f765 100644 --- a/src/lib/resolveDefaultsAtRules.js +++ b/src/lib/resolveDefaultsAtRules.js @@ -104,8 +104,12 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) { // we consider them separately because merging the declarations into // a single rule will cause browsers that do not understand the // vendor prefix to throw out the whole rule + // Additionally if a selector contains `:has` we also consider + // it separately because FF only recently gained support for it let selectorGroupName = - selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__' + selector.includes(':-') || selector.includes('::-') || selector.includes(':has') + ? selector + : '__DEFAULT__' let selectors = selectorGroups.get(selectorGroupName) ?? new Set() selectorGroups.set(selectorGroupName, selectors) diff --git a/tests/resolve-defaults-at-rules.test.js b/tests/resolve-defaults-at-rules.test.js index 5c7e0738e7ba..e8964c4c6f7e 100644 --- a/tests/resolve-defaults-at-rules.test.js +++ b/tests/resolve-defaults-at-rules.test.js @@ -772,3 +772,58 @@ crosscheck(() => { }) }) }) + +test('optimize universal defaults groups :has separately', () => { + let config = { + experimental: { optimizeUniversalDefaults: true }, + content: [ + { raw: html`
` }, + { raw: html`
` }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .ring-1 { + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #3b82f680; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + } + .has-\[\:checked\]\:ring-1:has(:checked) { + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #3b82f680; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + } + .ring-1 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + .has-\[\:checked\]\:ring-1:has(:checked) { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +})