diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d3cca93636c..32ea237f62d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove default `[hidden]` style in preflight ([#8248](https://github.com/tailwindlabs/tailwindcss/pull/8248)) - Only check selectors containing base apply candidates for circular dependencies ([#8222](https://github.com/tailwindlabs/tailwindcss/pull/8222)) - Rewrite default class extractor ([#8204](https://github.com/tailwindlabs/tailwindcss/pull/8204)) +- Move `important` selector to the front when `@apply`-ing selector-modifying variants in custom utilities ([#8313](https://github.com/tailwindlabs/tailwindcss/pull/8313)) ### Changed diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js index 1bef657fc6cf..f5e84a318147 100644 --- a/src/lib/expandApplyAtRules.js +++ b/src/lib/expandApplyAtRules.js @@ -471,7 +471,27 @@ function processApply(root, context, localCache) { return } - rule.selector = replaceSelector(parent.selector, rule.selector, applyCandidate) + // Strip the important selector from the parent selector if at the beginning + let importantSelector = + typeof context.tailwindConfig.important === 'string' + ? context.tailwindConfig.important + : null + + // We only want to move the "important" selector if this is a Tailwind-generated utility + // 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 + + rule.selector = replaceSelector(parentSelector, rule.selector, applyCandidate) + + // And then re-add it if it was removed + if (importantSelector && parentSelector !== parent.selector) { + rule.selector = `${importantSelector} ${rule.selector}` + } rule.walkDecls((d) => { d.important = meta.important || important diff --git a/tests/apply.test.js b/tests/apply.test.js index 7033300f01a6..6040aad22c38 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -1467,3 +1467,84 @@ it('should apply using the updated user CSS when the source has changed', async } `) }) + +it('apply + layer utilities + selector variants (like group) + important selector', async () => { + let config = { + important: '#myselector', + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + @layer utilities { + .custom-utility { + @apply font-normal group-hover:underline; + } + } + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + #myselector .custom-utility { + font-weight: 400; + } + + #myselector .group:hover .custom-utility { + text-decoration-line: underline; + } + `) +}) + +it('apply + user CSS + selector variants (like group) + important selector (1)', async () => { + let config = { + important: '#myselector', + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + .custom-utility { + @apply font-normal group-hover:underline; + } + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .custom-utility { + font-weight: 400; + } + + .group:hover .custom-utility { + text-decoration-line: underline; + } + `) +}) + +it('apply + user CSS + selector variants (like group) + important selector (2)', async () => { + let config = { + important: '#myselector', + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + #myselector .custom-utility { + @apply font-normal group-hover:underline; + } + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + #myselector .custom-utility { + font-weight: 400; + } + + .group:hover #myselector .custom-utility { + text-decoration-line: underline; + } + `) +})