Skip to content

Commit 4275e74

Browse files
RobinMalfaitclaude
andcommitted
Add browser tests for group-* and peer-* variants
Verify matching behavior, specificity, stacked and compound variants, and default namespace handling in Chromium, Firefox, and WebKit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent b0fcc99 commit 4275e74

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

‎packages/tailwindcss/tests/ui.spec.ts‎

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2387,6 +2387,157 @@ test('shadow DOM has access to variables', async ({ page }) => {
23872387
}
23882388
}
23892389

2390+
for (let kind of ['group', 'peer']) {
2391+
test(`${kind}-* variants`, async ({ page }) => {
2392+
let targets = html`
2393+
<div id="target" class="${kind}-focus:z-10"></div>
2394+
<div id="applied" class="applied"></div>
2395+
`
2396+
2397+
let { getPropertyValue } = await render(
2398+
page,
2399+
kind === 'group'
2400+
? html`<div id="trigger" tabindex="0" class="group owner">${targets}</div>`
2401+
: html`<div class="owner">
2402+
<div id="trigger" tabindex="0" class="peer"></div>
2403+
${targets}
2404+
</div>`,
2405+
css`
2406+
.owner > .applied {
2407+
@apply ${kind}-focus:z-20;
2408+
}
2409+
`,
2410+
)
2411+
2412+
expect(await getPropertyValue('#target', 'z-index')).toEqual('auto')
2413+
expect(await getPropertyValue('#applied', 'z-index')).toEqual('auto')
2414+
2415+
await page.locator('#trigger').focus()
2416+
2417+
expect(await getPropertyValue('#target', 'z-index')).toEqual('10')
2418+
expect(await getPropertyValue('#applied', 'z-index')).toEqual('20')
2419+
2420+
await page.locator('#trigger').blur()
2421+
2422+
expect(await getPropertyValue('#target', 'z-index')).toEqual('auto')
2423+
expect(await getPropertyValue('#applied', 'z-index')).toEqual('auto')
2424+
})
2425+
}
2426+
2427+
test('group-* variants do not change specificity', async ({ page }) => {
2428+
let { getPropertyValue } = await render(
2429+
page,
2430+
html`
2431+
<div id="trigger" tabindex="0" class="group">
2432+
<div id="target" class="target group-focus:z-10"></div>
2433+
</div>
2434+
`,
2435+
css`
2436+
@layer utilities {
2437+
.target {
2438+
z-index: 20;
2439+
}
2440+
.target.equal {
2441+
z-index: 30;
2442+
}
2443+
}
2444+
`,
2445+
)
2446+
2447+
await page.locator('#trigger').focus()
2448+
2449+
expect(await getPropertyValue('#target', 'z-index')).toEqual('10')
2450+
2451+
await page.locator('#target').evaluate((element) => element.classList.add('equal'))
2452+
2453+
expect(await getPropertyValue('#target', 'z-index')).toEqual('30')
2454+
})
2455+
2456+
test('stacked group-* variants match regardless of the order of the groups', async ({ page }) => {
2457+
for (let [outer, inner] of [
2458+
['a', 'b'],
2459+
['b', 'a'],
2460+
]) {
2461+
let { getPropertyValue } = await render(
2462+
page,
2463+
html`
2464+
<div class="group/${outer}" data-active>
2465+
<div id="inner" class="group/${inner}" data-active>
2466+
<div id="target" class="group-data-active/a:group-data-active/b:z-10"></div>
2467+
</div>
2468+
</div>
2469+
`,
2470+
)
2471+
2472+
expect(await getPropertyValue('#target', 'z-index')).toEqual('10')
2473+
2474+
await page.locator('#inner').evaluate((element) => element.removeAttribute('data-active'))
2475+
2476+
expect(await getPropertyValue('#target', 'z-index')).toEqual('auto')
2477+
}
2478+
})
2479+
2480+
test('compound group-* and peer-* variants', async ({ page }) => {
2481+
let { getPropertyValue } = await render(
2482+
page,
2483+
html`
2484+
<div id="outer" tabindex="0" class="group">
2485+
<div id="peer" tabindex="0" class="peer"></div>
2486+
<div id="middle" class="group peer-group-focus:z-20">
2487+
<div id="target" class="group-peer-focus:z-10"></div>
2488+
</div>
2489+
</div>
2490+
`,
2491+
)
2492+
2493+
await page.locator('#peer').focus()
2494+
2495+
expect(await getPropertyValue('#target', 'z-index')).toEqual('10')
2496+
expect(await getPropertyValue('#middle', 'z-index')).toEqual('auto')
2497+
2498+
await page.locator('#outer').focus()
2499+
2500+
expect(await getPropertyValue('#target', 'z-index')).toEqual('auto')
2501+
expect(await getPropertyValue('#middle', 'z-index')).toEqual('20')
2502+
})
2503+
2504+
for (let optimized of [false, true]) {
2505+
test(`compound group-* variants respect the default namespace${optimized ? ' (optimized)' : ''}`, async ({
2506+
page,
2507+
}) => {
2508+
// `@namespace` must precede all other rules, so we can't use `render(…)`
2509+
let { build } = await compile(css`
2510+
@namespace url(http://www.w3.org/1999/xhtml);
2511+
@tailwind utilities;
2512+
`)
2513+
let styles = build(['group-group-data-active:z-10'])
2514+
if (optimized) styles = optimize(styles).code
2515+
2516+
await page.setContent(html`
2517+
<style>
2518+
${styles}
2519+
</style>
2520+
<div class="group" data-active>
2521+
<svg class="group">
2522+
<foreignObject>
2523+
<div id="intermediate">
2524+
<div id="target" class="group-group-data-active:z-10"></div>
2525+
</div>
2526+
</foreignObject>
2527+
</svg>
2528+
</div>
2529+
`)
2530+
2531+
// The `svg` element is not in the default namespace, so it can't be the
2532+
// inner group
2533+
expect(await getPropertyValue(page, ['#target', undefined], 'z-index')).toEqual('auto')
2534+
2535+
await page.locator('#intermediate').evaluate((element) => element.classList.add('group'))
2536+
2537+
expect(await getPropertyValue(page, ['#target', undefined], 'z-index')).toEqual('10')
2538+
})
2539+
}
2540+
23902541
// ---
23912542

23922543
const preflight = fs.readFileSync(path.resolve(__dirname, '..', 'preflight.css'), 'utf-8')

0 commit comments

Comments
 (0)