Skip to content

Commit 47dbb4a

Browse files
adamwathanRobinMalfaitaggreggator
authored andcommitted
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 7642e28 commit 47dbb4a

File tree

5 files changed

+49
-1
lines changed

5 files changed

+49
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
- Add `forced-colors` variant ([#11694](https://github.com/tailwindlabs/tailwindcss/pull/11694))
2323
- Add `appearance-auto` utility ([#12404](https://github.com/tailwindlabs/tailwindcss/pull/12404))
2424
- Add logical property values for `float` and `clear` utilities ([#12480](https://github.com/tailwindlabs/tailwindcss/pull/12480))
25+
- Add `*` variant for targeting direct children ([#12551](https://github.com/tailwindlabs/tailwindcss/pull/12551))
2526

2627
### Changed
2728

oxide/crates/core/src/parser.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ impl<'a> Extractor<'a> {
278278
}
279279

280280
// Allowed first characters.
281-
b'@' | b'!' | b'-' | b'<' | b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' => {
281+
b'@' | b'!' | b'-' | b'<' | b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'*' => {
282282
// TODO: A bunch of characters that we currently support but maybe we only want it behind
283283
// a flag. E.g.: '<sm'
284284
// | '<' | '>' | '$' | '^' | '_'
@@ -329,6 +329,7 @@ impl<'a> Extractor<'a> {
329329
| b'!'
330330
| b'@'
331331
| b'%'
332+
| b'*'
332333
if prev != b']' =>
333334
{
334335
/* TODO: The `b'@'` is necessary for custom separators like _@, maybe we can handle this in a better way... */
@@ -508,6 +509,15 @@ mod test {
508509
assert_eq!(candidates, vec!["hover:underline"]);
509510
}
510511

512+
#[test]
513+
fn it_can_parse_start_variants() {
514+
let candidates = run("*:underline", false);
515+
assert_eq!(candidates, vec!["*:underline"]);
516+
517+
let candidates = run("hover:*:underline", false);
518+
assert_eq!(candidates, vec!["hover:*:underline"]);
519+
}
520+
511521
#[test]
512522
fn it_can_parse_simple_candidates_with_stacked_variants() {
513523
let candidates = run("focus:hover:underline", false);

src/corePlugins.js

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

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

src/lib/setupContextUtils.js

+1
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ function resolvePlugins(context, root) {
756756
// TODO: This is a workaround for backwards compatibility, since custom variants
757757
// were historically sorted before screen/stackable variants.
758758
let beforeVariants = [
759+
variantPlugins['childVariant'],
759760
variantPlugins['pseudoElementVariants'],
760761
variantPlugins['pseudoClassVariants'],
761762
variantPlugins['hasVariants'],

tests/variants.test.js

+33
Original file line numberDiff line numberDiff line change
@@ -1201,4 +1201,37 @@ crosscheck(({ stable, oxide }) => {
12011201
}
12021202
`)
12031203
})
1204+
1205+
test('* is matched by the parser as the children variant', async () => {
1206+
let config = {
1207+
content: [
1208+
{
1209+
raw: html`
1210+
<div class="*:italic" />
1211+
<div class="*:hover:italic" />
1212+
<div class="hover:*:italic" />
1213+
<div class="data-[slot=label]:*:hover:italic" />
1214+
<div class="[&_p]:*:hover:italic" />
1215+
`,
1216+
},
1217+
],
1218+
corePlugins: { preflight: false },
1219+
}
1220+
1221+
let input = css`
1222+
@tailwind utilities;
1223+
`
1224+
1225+
let result = await run(input, config)
1226+
1227+
expect(result.css).toMatchFormattedCss(css`
1228+
.\*\:italic > *,
1229+
.\*\:hover\:italic:hover > *,
1230+
.hover\:\*\:italic > :hover,
1231+
.data-\[slot\=label\]\:\*\:hover\:italic:hover > [data-slot='label'],
1232+
.\[\&_p\]\:\*\:hover\:italic:hover > * p {
1233+
font-style: italic;
1234+
}
1235+
`)
1236+
})
12041237
})

0 commit comments

Comments
 (0)