Skip to content

Commit cb9c64a

Browse files
adamwathanRobinMalfaitaggreggator
authored
Add * variant for targeting direct children (#12551)
* add `*` as child variant * add `*` as allowed variant character * update test to reflect Lightning CSS output * add `childVariant` test * Update changelog --------- Co-authored-by: Adam Wathan <[email protected]> Co-authored-by: Robin Malfait <[email protected]> Co-authored-by: Gregor Kaczmarczyk <[email protected]>
1 parent c5449a0 commit cb9c64a

File tree

7 files changed

+61
-2
lines changed

7 files changed

+61
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2626
- Add `forced-colors` variant ([#11694](https://github.com/tailwindlabs/tailwindcss/pull/11694))
2727
- Add `appearance-auto` utility ([#12404](https://github.com/tailwindlabs/tailwindcss/pull/12404))
2828
- Add logical property values for `float` and `clear` utilities ([#12480](https://github.com/tailwindlabs/tailwindcss/pull/12480))
29+
- Add `*` variant for targeting direct children ([#12551](https://github.com/tailwindlabs/tailwindcss/pull/12551))
2930
- [Oxide] New Rust template parsing engine ([#10252](https://github.com/tailwindlabs/tailwindcss/pull/10252))
3031
- [Oxide] Support `@import "tailwindcss"` using top-level `index.css` file ([#11205](https://github.com/tailwindlabs/tailwindcss/pull/11205), ([#11260](https://github.com/tailwindlabs/tailwindcss/pull/11260)))
3132
- [Oxide] Use `lightningcss` for nesting and vendor prefixes in PostCSS plugin ([#10399](https://github.com/tailwindlabs/tailwindcss/pull/10399))

oxide/crates/core/src/parser.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ impl<'a> Extractor<'a> {
412412
}
413413

414414
// Allowed first characters.
415-
b'@' | b'!' | b'-' | b'<' | b'>' | b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' => {
415+
b'@' | b'!' | b'-' | b'<' | b'>' | b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'*' => {
416416
// TODO: A bunch of characters that we currently support but maybe we only want it behind
417417
// a flag. E.g.: '<sm'
418418
// | '$' | '^' | '_'
@@ -473,7 +473,7 @@ impl<'a> Extractor<'a> {
473473
b'%' => return ParseAction::Skip,
474474

475475
// < and > can only be part of a variant and only be the first or last character
476-
b'<' | b'>' => {
476+
b'<' | b'>' | b'*' => {
477477
// Can only be the first or last character
478478
// E.g.:
479479
// - <sm:underline
@@ -797,6 +797,15 @@ mod test {
797797
assert_eq!(candidates, vec!["hover:underline"]);
798798
}
799799

800+
#[test]
801+
fn it_can_parse_start_variants() {
802+
let candidates = run("*:underline", false);
803+
assert_eq!(candidates, vec!["*:underline"]);
804+
805+
let candidates = run("hover:*:underline", false);
806+
assert_eq!(candidates, vec!["hover:*:underline"]);
807+
}
808+
800809
#[test]
801810
fn it_can_parse_simple_candidates_with_stacked_variants() {
802811
let candidates = run("focus:hover:underline", false);

src/corePlugins.js

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import { normalize } from './util/dataTypes'
2424
import { INTERNAL_FEATURES } from './lib/setupContextUtils'
2525

2626
export let variantPlugins = {
27+
childVariant: ({ addVariant }) => {
28+
addVariant('*', '& > *')
29+
},
2730
pseudoElementVariants: ({ addVariant }) => {
2831
addVariant('first-letter', '&::first-letter')
2932
addVariant('first-line', '&::first-line')

src/lib/setupContextUtils.js

+1
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ function resolvePlugins(context, root) {
746746
// TODO: This is a workaround for backwards compatibility, since custom variants
747747
// were historically sorted before screen/stackable variants.
748748
let beforeVariants = [
749+
variantPlugins['childVariant'],
749750
variantPlugins['pseudoElementVariants'],
750751
variantPlugins['pseudoClassVariants'],
751752
variantPlugins['hasVariants'],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`should test the 'childVariant' plugin 1`] = `
4+
"
5+
.\\*\\:flex > * {
6+
display: flex;
7+
}
8+
"
9+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { quickVariantPluginTest } from '../../util/run'
2+
3+
quickVariantPluginTest('childVariant').toMatchSnapshot()

tests/variants.test.js

+33
Original file line numberDiff line numberDiff line change
@@ -1167,3 +1167,36 @@ test('stacking dark and rtl variants with pseudo elements', async () => {
11671167
}
11681168
`)
11691169
})
1170+
1171+
test('* is matched by the parser as the children variant', async () => {
1172+
let config = {
1173+
content: [
1174+
{
1175+
raw: html`
1176+
<div class="*:italic" />
1177+
<div class="*:hover:italic" />
1178+
<div class="hover:*:italic" />
1179+
<div class="data-[slot=label]:*:hover:italic" />
1180+
<div class="[&_p]:*:hover:italic" />
1181+
`,
1182+
},
1183+
],
1184+
corePlugins: { preflight: false },
1185+
}
1186+
1187+
let input = css`
1188+
@tailwind utilities;
1189+
`
1190+
1191+
let result = await run(input, config)
1192+
1193+
expect(result.css).toMatchFormattedCss(css`
1194+
.\*\:italic > *,
1195+
.\*\:hover\:italic:hover > *,
1196+
.hover\:\*\:italic > :hover,
1197+
.data-\[slot\=label\]\:\*\:hover\:italic:hover > [data-slot='label'],
1198+
.\[\&_p\]\:\*\:hover\:italic:hover > * p {
1199+
font-style: italic;
1200+
}
1201+
`)
1202+
})

0 commit comments

Comments
 (0)