From 0b6f63a0a0ad1910a1a166ba6f43f3fca75a77e7 Mon Sep 17 00:00:00 2001 From: Nick Fujita Date: Mon, 21 Oct 2024 17:36:40 +0900 Subject: [PATCH 1/2] 'update important selector to also apply styles to wrapper component ' --- src/util/applyImportantSelector.js | 2 +- tests/apply.test.js | 2 ++ tests/custom-plugins.test.js | 3 ++ tests/experimental.test.js | 3 ++ tests/important-selector.test.js | 17 +++++++++++ tests/util/apply-important-selector.test.js | 34 ++++++++++----------- 6 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/util/applyImportantSelector.js b/src/util/applyImportantSelector.js index 07302f94d5f6..ff48eac60df1 100644 --- a/src/util/applyImportantSelector.js +++ b/src/util/applyImportantSelector.js @@ -22,5 +22,5 @@ export function applyImportantSelector(selector, important) { movePseudos(sel) }) - return `${important} ${sel.toString()}` + return `${important}${sel.toString()}, ${important} ${sel.toString()}` } diff --git a/tests/apply.test.js b/tests/apply.test.js index 6576241bfdeb..05b4a223919c 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -1754,9 +1754,11 @@ it('apply + layer utilities + selector variants (like group) + important selecto let result = await run(input, config) expect(result.css).toMatchFormattedCss(css` + #myselector:is(.custom-utility), #myselector :is(.custom-utility) { font-weight: 400; } + #myselector:is(.group:hover .custom-utility), #myselector :is(.group:hover .custom-utility) { text-decoration-line: underline; } diff --git a/tests/custom-plugins.test.js b/tests/custom-plugins.test.js index d1ae598e5efb..9eebccb580aa 100644 --- a/tests/custom-plugins.test.js +++ b/tests/custom-plugins.test.js @@ -843,6 +843,7 @@ test('when important is a selector it is used to scope utilities instead of addi return run('@tailwind utilities', config).then((result) => { expect(result.css).toMatchFormattedCss(css` + #app.tw-custom-rotate-90, #app .tw-custom-rotate-90 { transform: rotate(90deg); } @@ -867,7 +868,9 @@ test('when important is a selector it scopes all selectors in a rule, even thoug return run('@tailwind utilities', config).then((result) => { expect(result.css).toMatchFormattedCss(css` + #app.custom-rotate-90, #app .custom-rotate-90, + #app.custom-rotate-1\/4, #app .custom-rotate-1\/4 { transform: rotate(90deg); } diff --git a/tests/experimental.test.js b/tests/experimental.test.js index b3fa49185bae..1fdf0884a558 100644 --- a/tests/experimental.test.js +++ b/tests/experimental.test.js @@ -175,14 +175,17 @@ test('experimental universal selector improvements (#app important)', () => { --tw-shadow: 0 0 #0000; --tw-shadow-colored: 0 0 #0000; } + #app:is(.resize), #app :is(.resize) { resize: both; } + #app:is(.divide-y > :not([hidden]) ~ :not([hidden])), #app :is(.divide-y > :not([hidden]) ~ :not([hidden])) { --tw-divide-y-reverse: 0; border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); } + #app:is(.shadow), #app :is(.shadow) { --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), diff --git a/tests/important-selector.test.js b/tests/important-selector.test.js index bc41ecfceb4d..0445f9d88842 100644 --- a/tests/important-selector.test.js +++ b/tests/important-selector.test.js @@ -104,6 +104,7 @@ test('important selector', () => { max-width: 1536px; } } + #app.btn, #app .btn { button: yes; } @@ -124,41 +125,56 @@ test('important selector', () => { transform: rotate(360deg); } } + #app:is(.animate-spin), #app :is(.animate-spin) { animation: 1s linear infinite spin; } + #app:is(.font-bold), #app :is(.font-bold) { font-weight: 700; } .custom-util { button: no; } + #app.\[color\:red\], #app .\[color\:red\] { color: red; } + #app:is(.group:hover .group-hover\:focus-within\:text-left:focus-within), #app :is(.group:hover .group-hover\:focus-within\:text-left:focus-within) { text-align: left; } @media (prefers-reduced-motion: no-preference) { + #app:is(.motion-safe\:hover\:text-center:hover), #app :is(.motion-safe\:hover\:text-center:hover) { text-align: center; } } @media (min-width: 768px) { + #app:is(.md\:hover\:text-right:hover), #app :is(.md\:hover\:text-right:hover) { text-align: right; } } + #app:is(.rtl\:active\:text-center:active:where([dir='rtl'], [dir='rtl'] *)), #app :is(.rtl\:active\:text-center:active:where([dir='rtl'], [dir='rtl'] *)) { text-align: center; } + #app:is(.dark\:before\:underline:where(.dark, .dark *)):before, #app :is(.dark\:before\:underline:where(.dark, .dark *)):before { content: var(--tw-content); text-decoration-line: underline; } + #app:is(.dark\:focus\:text-left:focus:where(.dark, .dark *)), #app :is(.dark\:focus\:text-left:focus:where(.dark, .dark *)) { text-align: left; } + #app:is( + .hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100:where( + .dark, + .dark * + ):where([dir='rtl'], [dir='rtl'] *) + )::file-selector-button:hover, #app :is( .hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100:where( @@ -193,6 +209,7 @@ test('pseudo-elements are appended after the `:is()`', () => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` ${defaults} + #app.dark\:before\:bg-black:where(.dark, .dark *)::before, #app .dark\:before\:bg-black:where(.dark, .dark *)::before { content: var(--tw-content); --tw-bg-opacity: 1; diff --git a/tests/util/apply-important-selector.test.js b/tests/util/apply-important-selector.test.js index cd971b92c732..78222f9c111f 100644 --- a/tests/util/apply-important-selector.test.js +++ b/tests/util/apply-important-selector.test.js @@ -2,23 +2,23 @@ import { applyImportantSelector } from '../../src/util/applyImportantSelector' it.each` before | after - ${'.foo'} | ${'#app .foo'} - ${'.foo .bar'} | ${'#app :is(.foo .bar)'} - ${'.foo:hover'} | ${'#app .foo:hover'} - ${'.foo .bar:hover'} | ${'#app :is(.foo .bar:hover)'} - ${'.foo::before'} | ${'#app .foo::before'} - ${'.foo::file-selector-button'} | ${'#app .foo::file-selector-button'} - ${'.foo::-webkit-progress-bar'} | ${'#app .foo::-webkit-progress-bar'} - ${'.foo:hover::before'} | ${'#app .foo:hover::before'} - ${':is(:where(.dark) :is(:where([dir="rtl"]) .foo::before))'} | ${'#app :is(:where(.dark) :is(:where([dir="rtl"]) .foo))::before'} - ${':is(:where(.dark) .foo) .bar'} | ${'#app :is(:is(:where(.dark) .foo) .bar)'} - ${':is(.foo) :is(.bar)'} | ${'#app :is(:is(.foo) :is(.bar))'} - ${':is(.foo)::before'} | ${'#app :is(.foo)::before'} - ${'.foo:before'} | ${'#app .foo:before'} - ${'.foo::some-uknown-pseudo'} | ${'#app .foo::some-uknown-pseudo'} - ${'.foo::some-uknown-pseudo:hover'} | ${'#app .foo::some-uknown-pseudo:hover'} - ${'.foo:focus::some-uknown-pseudo:hover'} | ${'#app .foo:focus::some-uknown-pseudo:hover'} - ${'.foo:hover::some-uknown-pseudo:focus'} | ${'#app .foo:hover::some-uknown-pseudo:focus'} + ${'.foo'} | ${'#app.foo, #app .foo'} + ${'.foo .bar'} | ${'#app:is(.foo .bar), #app :is(.foo .bar)'} + ${'.foo:hover'} | ${'#app.foo:hover, #app .foo:hover'} + ${'.foo .bar:hover'} | ${'#app:is(.foo .bar:hover), #app :is(.foo .bar:hover)'} + ${'.foo::before'} | ${'#app.foo::before, #app .foo::before'} + ${'.foo::file-selector-button'} | ${'#app.foo::file-selector-button, #app .foo::file-selector-button'} + ${'.foo::-webkit-progress-bar'} | ${'#app.foo::-webkit-progress-bar, #app .foo::-webkit-progress-bar'} + ${'.foo:hover::before'} | ${'#app.foo:hover::before, #app .foo:hover::before'} + ${':is(:where(.dark) :is(:where([dir="rtl"]) .foo::before))'} | ${'#app:is(:where(.dark) :is(:where([dir="rtl"]) .foo))::before, #app :is(:where(.dark) :is(:where([dir="rtl"]) .foo))::before'} + ${':is(:where(.dark) .foo) .bar'} | ${'#app:is(:is(:where(.dark) .foo) .bar), #app :is(:is(:where(.dark) .foo) .bar)'} + ${':is(.foo) :is(.bar)'} | ${'#app:is(:is(.foo) :is(.bar)), #app :is(:is(.foo) :is(.bar))'} + ${':is(.foo)::before'} | ${'#app:is(.foo)::before, #app :is(.foo)::before'} + ${'.foo:before'} | ${'#app.foo:before, #app .foo:before'} + ${'.foo::some-uknown-pseudo'} | ${'#app.foo::some-uknown-pseudo, #app .foo::some-uknown-pseudo'} + ${'.foo::some-uknown-pseudo:hover'} | ${'#app.foo::some-uknown-pseudo:hover, #app .foo::some-uknown-pseudo:hover'} + ${'.foo:focus::some-uknown-pseudo:hover'} | ${'#app.foo:focus::some-uknown-pseudo:hover, #app .foo:focus::some-uknown-pseudo:hover'} + ${'.foo:hover::some-uknown-pseudo:focus'} | ${'#app.foo:hover::some-uknown-pseudo:focus, #app .foo:hover::some-uknown-pseudo:focus'} `('should generate "$after" from "$before"', ({ before, after }) => { expect(applyImportantSelector(before, '#app')).toEqual(after) }) From 774b0635ecd0e53f9daa21a6d57ff9904f796848 Mon Sep 17 00:00:00 2001 From: Nick Fujita Date: Tue, 22 Oct 2024 13:45:57 +0900 Subject: [PATCH 2/2] 'fix reapplication of important selector on selectors generated from @apply statements' --- src/lib/expandApplyAtRules.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js index 906f9fb2d1e7..5bf16db1a421 100644 --- a/src/lib/expandApplyAtRules.js +++ b/src/lib/expandApplyAtRules.js @@ -565,10 +565,12 @@ function processApply(root, context, localCache) { // We do *not* want to do this for user CSS that happens to be structured the same let isGenerated = parent.raws.tailwind !== undefined - let parentSelector = - isGenerated && importantSelector && parent.selector.indexOf(importantSelector) === 0 - ? parent.selector.slice(importantSelector.length) - : parent.selector + let parentSelector = parent.selector + if (isGenerated && importantSelector && parent.selector.includes(importantSelector)) { + const parts = parent.selector.split(',') + const filteredParts = parts.filter((_, i) => i % 2 === 0).join(',') + parentSelector = filteredParts.replace(importantSelector, '') + } // If the selector becomes empty after replacing the important selector // This means that it's the same as the parent selector and we don't want to replace it