Skip to content

Commit b49dc7c

Browse files
Move important selector to the front when @apply-ing selector-modifying variants in custom utilities (tailwindlabs#8313)
* Fix generated utilities using `@apply` with important selectors * Update changelog
1 parent be51739 commit b49dc7c

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- Remove default `[hidden]` style in preflight ([#8248](https://github.com/tailwindlabs/tailwindcss/pull/8248))
2121
- Only check selectors containing base apply candidates for circular dependencies ([#8222](https://github.com/tailwindlabs/tailwindcss/pull/8222))
2222
- Rewrite default class extractor ([#8204](https://github.com/tailwindlabs/tailwindcss/pull/8204))
23+
- Move `important` selector to the front when `@apply`-ing selector-modifying variants in custom utilities ([#8313](https://github.com/tailwindlabs/tailwindcss/pull/8313))
2324

2425
### Changed
2526

src/lib/expandApplyAtRules.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,27 @@ function processApply(root, context, localCache) {
471471
return
472472
}
473473

474-
rule.selector = replaceSelector(parent.selector, rule.selector, applyCandidate)
474+
// Strip the important selector from the parent selector if at the beginning
475+
let importantSelector =
476+
typeof context.tailwindConfig.important === 'string'
477+
? context.tailwindConfig.important
478+
: null
479+
480+
// We only want to move the "important" selector if this is a Tailwind-generated utility
481+
// We do *not* want to do this for user CSS that happens to be structured the same
482+
let isGenerated = parent.raws.tailwind !== undefined
483+
484+
let parentSelector =
485+
isGenerated && importantSelector && parent.selector.indexOf(importantSelector) === 0
486+
? parent.selector.slice(importantSelector.length)
487+
: parent.selector
488+
489+
rule.selector = replaceSelector(parentSelector, rule.selector, applyCandidate)
490+
491+
// And then re-add it if it was removed
492+
if (importantSelector && parentSelector !== parent.selector) {
493+
rule.selector = `${importantSelector} ${rule.selector}`
494+
}
475495

476496
rule.walkDecls((d) => {
477497
d.important = meta.important || important

tests/apply.test.js

+81
Original file line numberDiff line numberDiff line change
@@ -1467,3 +1467,84 @@ it('should apply using the updated user CSS when the source has changed', async
14671467
}
14681468
`)
14691469
})
1470+
1471+
it('apply + layer utilities + selector variants (like group) + important selector', async () => {
1472+
let config = {
1473+
important: '#myselector',
1474+
content: [{ raw: html`<div class="custom-utility"></div>` }],
1475+
plugins: [],
1476+
}
1477+
1478+
let input = css`
1479+
@tailwind utilities;
1480+
@layer utilities {
1481+
.custom-utility {
1482+
@apply font-normal group-hover:underline;
1483+
}
1484+
}
1485+
`
1486+
1487+
let result = await run(input, config)
1488+
1489+
expect(result.css).toMatchFormattedCss(css`
1490+
#myselector .custom-utility {
1491+
font-weight: 400;
1492+
}
1493+
1494+
#myselector .group:hover .custom-utility {
1495+
text-decoration-line: underline;
1496+
}
1497+
`)
1498+
})
1499+
1500+
it('apply + user CSS + selector variants (like group) + important selector (1)', async () => {
1501+
let config = {
1502+
important: '#myselector',
1503+
content: [{ raw: html`<div class="custom-utility"></div>` }],
1504+
plugins: [],
1505+
}
1506+
1507+
let input = css`
1508+
.custom-utility {
1509+
@apply font-normal group-hover:underline;
1510+
}
1511+
`
1512+
1513+
let result = await run(input, config)
1514+
1515+
expect(result.css).toMatchFormattedCss(css`
1516+
.custom-utility {
1517+
font-weight: 400;
1518+
}
1519+
1520+
.group:hover .custom-utility {
1521+
text-decoration-line: underline;
1522+
}
1523+
`)
1524+
})
1525+
1526+
it('apply + user CSS + selector variants (like group) + important selector (2)', async () => {
1527+
let config = {
1528+
important: '#myselector',
1529+
content: [{ raw: html`<div class="custom-utility"></div>` }],
1530+
plugins: [],
1531+
}
1532+
1533+
let input = css`
1534+
#myselector .custom-utility {
1535+
@apply font-normal group-hover:underline;
1536+
}
1537+
`
1538+
1539+
let result = await run(input, config)
1540+
1541+
expect(result.css).toMatchFormattedCss(css`
1542+
#myselector .custom-utility {
1543+
font-weight: 400;
1544+
}
1545+
1546+
.group:hover #myselector .custom-utility {
1547+
text-decoration-line: underline;
1548+
}
1549+
`)
1550+
})

0 commit comments

Comments
 (0)