import { expect, test, type Page } from '@playwright/test'
import { Scanner } from '@tailwindcss/oxide'
import fs from 'node:fs'
import path from 'node:path'
import { optimize } from '../../@tailwindcss-node/src/optimize'
import { compile } from '../src'
import { segment } from '../src/utils/segment'
const html = String.raw
const css = String.raw
test('touch action', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="touch-pan-x touch-pan-y hover:touch-pinch-zoom">Hello world</div>`,
)
expect(await getPropertyValue('#x', 'touch-action')).toEqual('pan-x pan-y')
await page.locator('#x').hover()
expect([
'manipulation',
'pan-x pan-y pinch-zoom',
]).toContain(await getPropertyValue('#x', 'touch-action'))
})
for (let [classes, expected] of [
[
'bg-linear-to-r from-red',
'linear-gradient(to right in oklab, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
],
[
'bg-linear-to-r via-red',
'linear-gradient(to right in oklab, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgba(0, 0, 0, 0) 100%)',
],
[
'bg-linear-to-r to-red',
'linear-gradient(to right in oklab, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 100%)',
],
[
'bg-linear-to-r from-red to-blue',
'linear-gradient(to right in oklab, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
],
[
'bg-linear-45 from-red to-blue',
'linear-gradient(45deg in oklab, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
],
[
'-bg-linear-45 from-red to-blue',
[
'linear-gradient(-45deg in oklab, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
'linear-gradient(calc(-45deg) in oklab, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
],
],
[
'bg-linear-to-r via-red to-blue',
'linear-gradient(to right in oklab, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgb(0, 0, 255) 100%)',
],
[
'bg-linear-to-r from-red via-green to-blue',
'linear-gradient(to right in oklab, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
],
[
'bg-linear-[to_right,var(--color-red),var(--color-green),var(--color-blue)]',
'linear-gradient(to right, rgb(255, 0, 0), rgb(0, 255, 0), rgb(0, 0, 255))',
],
]) {
test(`background gradient, "${classes}"`, async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="${classes}">Hello world</div>`,
)
if (Array.isArray(expected)) {
expect(expected).toContain(await getPropertyValue('#x', 'background-image'))
} else {
expect(await getPropertyValue('#x', 'background-image')).toEqual(expected)
}
})
}
test('background gradient, going from 2 to 3', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html` <div id="x" class="bg-gradient-to-r from-red hover:via-green to-blue">Hello world</div> `,
)
expect(await getPropertyValue('#x', 'background-image')).toEqual(
'linear-gradient(to right in oklab, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
)
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'background-image')).toEqual(
'linear-gradient(to right in oklab, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
)
})
test('background gradient, going from 3 to 2', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="bg-gradient-to-r from-red via-green hover:via-none to-blue">
Hello world
</div>
`,
)
expect(await getPropertyValue('#x', 'background-image')).toEqual(
'linear-gradient(to right in oklab, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
)
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'background-image')).toEqual(
'linear-gradient(to right in oklab, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
)
})
for (let [classes, expected] of [
['bg-conic from-red', 'conic-gradient(in oklab, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
[
'bg-conic-45 from-red',
'conic-gradient(from 45deg in oklab, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
],
[
'bg-conic-[from_45deg] from-red',
'conic-gradient(from 45deg, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
],
[
'bg-conic-[from_45deg,var(--color-red),transparent]',
'conic-gradient(from 45deg, rgb(255, 0, 0), rgba(0, 0, 0, 0))',
],
]) {
test(`conic gradient, "${classes}"`, async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="${classes}">Hello world</div>`,
)
expect(await getPropertyValue('#x', 'background-image')).toEqual(expected)
})
}
for (let [classes, expected] of [
['bg-radial from-red', 'radial-gradient(in oklab, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
[
'bg-radial-[at_0%_0%] from-red',
'radial-gradient(at 0% 0%, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
],
[
'bg-radial-[at_0%_0%,var(--color-red),transparent]',
'radial-gradient(at 0% 0%, rgb(255, 0, 0), rgba(0, 0, 0, 0))',
],
[
'bg-radial-[at_center] from-red to-green',
'radial-gradient(rgb(255, 0, 0) 0%, rgb(0, 255, 0) 100%)',
],
]) {
test(`radial gradient, "${classes}"`, async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="${classes}">Hello world</div>`,
)
expect(await getPropertyValue('#x', 'background-image')).toEqual(expected)
})
}
for (let [classes, expected] of [
[
'mask-x-from-20%',
[
'linear-gradient(to left, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(to right, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-y-from-20%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(to top, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-t-from-20%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(to top, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-r-from-20%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(to right, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-b-from-20%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-l-from-20%',
[
'linear-gradient(to left, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-x-to-80%',
[
'linear-gradient(to left, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(to right, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-y-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(to top, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-t-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(to top, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-r-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(to right, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-b-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-l-to-80%',
[
'linear-gradient(to left, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-x-from-20% mask-x-to-80%',
[
'linear-gradient(to left, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(to right, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-y-from-20% mask-y-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(to top, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-t-from-20% mask-t-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(to top, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-r-from-20% mask-r-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(to right, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-b-from-20% mask-b-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-l-from-20% mask-l-to-80%',
[
'linear-gradient(to left, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-t-from-10% mask-t-to-20% mask-r-from-30% mask-r-to-40% mask-b-from-50% mask-b-to-60% mask-l-from-70% mask-l-to-80%',
[
'linear-gradient(to left, rgb(0, 0, 0) 70%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(to right, rgb(0, 0, 0) 30%, rgba(0, 0, 0, 0) 40%)',
'linear-gradient(rgb(0, 0, 0) 50%, rgba(0, 0, 0, 0) 60%)',
'linear-gradient(to top, rgb(0, 0, 0) 10%, rgba(0, 0, 0, 0) 20%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-x-from-10% mask-x-to-20% mask-y-from-30% mask-y-to-40%',
[
'linear-gradient(to left, rgb(0, 0, 0) 10%, rgba(0, 0, 0, 0) 20%)',
'linear-gradient(to right, rgb(0, 0, 0) 10%, rgba(0, 0, 0, 0) 20%)',
'linear-gradient(rgb(0, 0, 0) 30%, rgba(0, 0, 0, 0) 40%)',
'linear-gradient(to top, rgb(0, 0, 0) 30%, rgba(0, 0, 0, 0) 40%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-x-from-20% mask-x-to-80% mask-l-from-30% mask-r-to-70%',
[
'linear-gradient(to left, rgb(0, 0, 0) 30%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(to right, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 70%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-y-from-20% mask-y-to-80% mask-t-from-30% mask-b-to-70%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 70%)',
'linear-gradient(to top, rgb(0, 0, 0) 30%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
]) {
test(`mask edges, "${classes}"`, async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="${classes}">Hello world</div>`,
)
if (Array.isArray(expected)) {
expect(expected).toContain(await getPropertyValue('#x', 'mask-image'))
} else {
expect(await getPropertyValue('#x', 'mask-image')).toEqual(expected)
}
})
}
for (let [classes, expected] of [
[
'mask-linear-45 mask-linear-from-20%',
[
[
'linear-gradient(45deg, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(calc(45deg), rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
],
[
'mask-linear-45 mask-linear-to-80%',
[
[
'linear-gradient(45deg, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(calc(45deg), rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
],
[
'mask-linear-45 mask-linear-from-20% mask-linear-to-80%',
[
[
'linear-gradient(45deg, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(calc(45deg), rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
],
]) {
test(`mask linear, "${classes}"`, async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="${classes}">Hello world</div>`,
)
if (Array.isArray(expected)) {
expect(expected).toContain(await getPropertyValue('#x', 'mask-image'))
} else {
expect(await getPropertyValue('#x', 'mask-image')).toEqual(expected)
}
})
}
for (let [classes, expected] of [
[
'mask-radial-from-20%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-radial-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-radial-from-20% mask-radial-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-circle mask-radial-from-20% mask-radial-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(circle, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-ellipse mask-radial-from-20% mask-radial-to-80%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-radial-at-top-left mask-radial-from-20% mask-radial-to-80%',
[
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(at 0% 0%, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(at left top, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
],
[
'mask-circle mask-radial-at-top-left mask-radial-from-20% mask-radial-to-80%',
[
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(circle at 0% 0%, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(circle at left top, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
],
[
'mask-radial-[100%_100%] mask-radial-from-20%',
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(100% 100%, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
[
'mask-radial-[25%_150%] mask-radial-at-top mask-ellipse mask-radial-from-20% mask-radial-to-80%',
[
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(25% 150% at 50% 0%, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(25% 150% at center top, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
],
],
]) {
test(`mask radial, "${classes}"`, async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="${classes}">Hello world</div>`,
)
if (Array.isArray(expected)) {
expect(expected).toContain(await getPropertyValue('#x', 'mask-image'))
} else {
expect(await getPropertyValue('#x', 'mask-image')).toEqual(expected)
}
})
}
for (let [classes, expected] of [
[
'mask-conic-45 mask-conic-from-20%',
[
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from 45deg, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from calc(45deg), rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
],
],
[
'mask-conic-45 mask-conic-to-80%',
[
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from 45deg, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from calc(45deg), rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 80%)',
].join(', '),
],
],
[
'mask-conic-45 mask-conic-from-20% mask-conic-to-80%',
[
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from 45deg, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from calc(45deg), rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 80%)',
].join(', '),
],
],
]) {
test(`mask conic, "${classes}"`, async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="${classes}">Hello world</div>`,
)
if (Array.isArray(expected)) {
expect(expected).toContain(await getPropertyValue('#x', 'mask-image'))
} else {
expect(await getPropertyValue('#x', 'mask-image')).toEqual(expected)
}
})
}
for (let [classes, expected] of [
[
'mask-conic-45 mask-conic-from-20% mask-linear-30 mask-linear-to-70% mask-circle mask-radial-from-40% mask-radial-to-80%',
[
[
'linear-gradient(30deg, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 70%)',
'radial-gradient(circle, rgb(0, 0, 0) 40%, rgba(0, 0, 0, 0) 80%)',
'conic-gradient(from 45deg, rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
[
'linear-gradient(calc(30deg), rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0) 70%)',
'radial-gradient(circle, rgb(0, 0, 0) 40%, rgba(0, 0, 0, 0) 80%)',
'conic-gradient(from calc(45deg), rgb(0, 0, 0) 20%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
],
],
]) {
test(`mask multiple, "${classes}"`, async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="${classes}">Hello world</div>`,
)
if (Array.isArray(expected)) {
expect(expected).toContain(await getPropertyValue('#x', 'mask-image'))
} else {
expect(await getPropertyValue('#x', 'mask-image')).toEqual(expected)
}
})
}
test('mask edges can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html` <div id="x" class="mask-x-from-25% hover:mask-x-to-75%">Hello world</div> `,
)
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(to left, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(to right, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(to left, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 75%)',
'linear-gradient(to right, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 75%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
})
test('mask edge colors can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="mask-x-from-25% hover:mask-x-from-transparent hover:mask-x-to-black">
Hello world
</div>
`,
)
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(to left, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(to right, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(to left, rgba(0, 0, 0, 0) 25%, rgb(0, 0, 0) 100%)',
'linear-gradient(to right, rgba(0, 0, 0, 0) 25%, rgb(0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
})
test('linear mask direction can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="mask-linear-15 mask-linear-from-25% hover:mask-linear-45">Hello world</div>
`,
)
expect([
[
'linear-gradient(15deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(calc(15deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
await page.locator('#x').hover()
expect([
[
'linear-gradient(45deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(calc(45deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
})
test('linear mask position can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="mask-linear-15 mask-linear-from-25% hover:mask-linear-to-75%">
Hello world
</div>
`,
)
expect([
[
'linear-gradient(15deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(calc(15deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
await page.locator('#x').hover()
expect([
[
'linear-gradient(15deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 75%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(calc(15deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 75%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
})
test('linear mask colors can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div
id="x"
class="mask-linear-15 mask-linear-from-25% hover:mask-linear-from-transparent hover:mask-linear-to-black"
>
Hello world
</div>
`,
)
expect([
[
'linear-gradient(15deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(calc(15deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
await page.locator('#x').hover()
expect([
[
'linear-gradient(15deg, rgba(0, 0, 0, 0) 25%, rgb(0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
[
'linear-gradient(calc(15deg), rgba(0, 0, 0, 0) 25%, rgb(0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
})
test('radial mask size can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="mask-radial-[50%_50%] mask-radial-from-25% hover:mask-radial-[125%_25%]">
Hello world
</div>
`,
)
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(50% 50%, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(125% 25%, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
})
test('radial mask position can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="mask-radial-[50%_50%] mask-radial-from-25% hover:mask-radial-to-75%">
Hello world
</div>
`,
)
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(50% 50%, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(50% 50%, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 75%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
})
test('radial mask color can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div
id="x"
class="mask-radial-[50%_50%] mask-radial-from-25% hover:mask-radial-from-transparent hover:mask-radial-to-black"
>
Hello world
</div>
`,
)
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(50% 50%, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'mask-image')).toEqual(
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'radial-gradient(50% 50%, rgba(0, 0, 0, 0) 25%, rgb(0, 0, 0) 100%)',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
].join(', '),
)
})
test('conic mask direction can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="mask-conic-15 mask-conic-from-25% hover:mask-conic-45">Hello world</div>
`,
)
expect([
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from 15deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from calc(15deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
await page.locator('#x').hover()
expect([
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from 45deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from calc(45deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
})
test('conic mask position can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="mask-conic-15 mask-conic-from-25% hover:mask-conic-to-75%">
Hello world
</div>
`,
)
expect([
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from 15deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from calc(15deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
await page.locator('#x').hover()
expect([
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from 15deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 75%)',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from calc(15deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 75%)',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
})
test('conic mask color can be changed on hover', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div
id="x"
class="mask-conic-15 mask-conic-from-25% hover:mask-conic-from-transparent hover:mask-conic-to-black"
>
Hello world
</div>
`,
)
expect([
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from 15deg, rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from calc(15deg), rgb(0, 0, 0) 25%, rgba(0, 0, 0, 0) 100%)',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
await page.locator('#x').hover()
expect([
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from 15deg, rgba(0, 0, 0, 0) 25%, rgb(0, 0, 0) 100%)',
].join(', '),
[
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255))',
'conic-gradient(from calc(15deg), rgba(0, 0, 0, 0) 25%, rgb(0, 0, 0) 100%)',
].join(', '),
]).toContain(await getPropertyValue('#x', 'mask-image'))
})
test("::backdrop can receive a border with just the 'border' utility", async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<dialog id="x" class="backdrop:border backdrop:border-black">Hello world</dialog>`,
)
await page.evaluate(() => {
;(document.getElementById('x') as HTMLDialogElement)!.showModal()
})
expect(await getPropertyValue(['#x', '::backdrop'], 'border')).toEqual('1px solid rgb(0, 0, 0)')
})
test("::first-letter can receive a border with just the 'border' utility", async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="first-letter:border first-letter:border-black">Hello world</div>`,
)
expect(await getPropertyValue(['#x', '::first-letter'], 'border')).toEqual(
'1px solid rgb(0, 0, 0)',
)
})
test.skip("::file-selector-button can receive a border with just the 'border' utility", async ({
page,
}) => {
let { getPropertyValue } = await render(
page,
html`<input id="x" type="file" class="file:border file:border-black" />`,
)
expect(await getPropertyValue(['#x', '::file-selector-button'], 'border')).toEqual(
'1px solid rgb(0, 0, 0)',
)
})
test('composing shadow, inset shadow, ring, and inset ring', async ({ page }) => {
let { getPropertyList } = await render(
page,
html`<div
id="x"
class="shadow-sm shadow-[#f00] inset-shadow-sm inset-shadow-[#0f0] ring ring-[#fff] inset-ring inset-ring-[#00f]"
></div>`,
)
expect(await getPropertyList('#x', 'box-shadow')).toEqual([
expect.stringMatching(/oklab\(0.866\d+ -0.233\d+ 0.179\d+\) 0px 2px 4px 0px inset/),
'rgb(0, 0, 255) 0px 0px 0px 1px inset',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgb(255, 255, 255) 0px 0px 0px 1px',
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 3px 0px/),
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 2px -1px/),
])
})
test('shadow colors', async ({ page }) => {
let { getPropertyList } = await render(
page,
html`
<div id="a" class="shadow-sm shadow-red"></div>
<div id="b" class="shadow-xl shadow-red"></div>
<div id="c" class="shadow-[0px_2px_4px] shadow-red"></div>
<div id="d" class="shadow-sm shadow-red hover:shadow-xl">Hello world</div>
<div id="e" class="shadow-sm shadow-red hover:shadow-xl hover:shadow-initial">
Hello world
</div>
<div id="f" class="shadow-xs/75">Hello world</div>
<div id="g" class="shadow-xs/75 shadow-red/75">Hello world</div>
`,
)
expect(await getPropertyList('#a', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 3px 0px/),
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 2px -1px/),
])
expect(await getPropertyList('#b', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 20px 25px -5px/),
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 8px 10px -6px/),
])
expect(await getPropertyList('#c', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 2px 4px 0px/),
])
expect(await getPropertyList('#d', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 3px 0px/),
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 2px -1px/),
])
expect(await getPropertyList('#f', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'oklab(0 0 0 / 0.75) 0px 1px 2px 0px',
])
expect(await getPropertyList('#g', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+ \/ 0.56\d+\) 0px 1px 2px 0px/),
])
await page.locator('#d').hover()
expect(await getPropertyList('#d', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 20px 25px -5px/),
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 8px 10px -6px/),
])
expect(await getPropertyList('#e', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 3px 0px/),
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 2px -1px/),
])
await page.locator('#e').hover()
expect(await getPropertyList('#e', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0.1) 0px 20px 25px -5px',
'rgba(0, 0, 0, 0.1) 0px 8px 10px -6px',
])
})
test('inset shadow colors', async ({ page }) => {
let { getPropertyList } = await render(
page,
html`
<div id="a" class="inset-shadow-xs inset-shadow-red"></div>
<div id="b" class="inset-shadow-sm inset-shadow-red"></div>
<div id="c" class="inset-shadow-[0px_3px_6px] inset-shadow-red"></div>
<div id="d" class="inset-shadow-xs inset-shadow-red hover:inset-shadow-sm">Hello world</div>
<div
id="e"
class="inset-shadow-xs inset-shadow-red hover:inset-shadow-sm hover:inset-shadow-initial"
>
Hello world
</div>
<div id="f" class="inset-shadow-xs/75">Hello world</div>
<div id="g" class="inset-shadow-xs/75 inset-shadow-red/75">Hello world</div>
`,
)
expect(await getPropertyList('#a', 'box-shadow')).toEqual([
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 1px 0px inset/),
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
])
expect(await getPropertyList('#b', 'box-shadow')).toEqual([
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 2px 4px 0px inset/),
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
])
expect(await getPropertyList('#c', 'box-shadow')).toEqual([
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 3px 6px 0px inset/),
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
])
expect(await getPropertyList('#d', 'box-shadow')).toEqual([
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 1px 0px inset/),
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
])
expect(await getPropertyList('#f', 'box-shadow')).toEqual([
'oklab(0 0 0 / 0.75) 0px 1px 1px 0px inset',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
])
expect(await getPropertyList('#g', 'box-shadow')).toEqual([
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+ \/ 0.56\d+\) 0px 1px 1px 0px inset/),
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
])
await page.locator('#d').hover()
expect(await getPropertyList('#d', 'box-shadow')).toEqual([
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 2px 4px 0px inset/),
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
])
expect(await getPropertyList('#e', 'box-shadow')).toEqual([
expect.stringMatching(/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 1px 0px inset/),
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
])
await page.locator('#e').hover()
expect(await getPropertyList('#e', 'box-shadow')).toEqual([
'rgba(0, 0, 0, 0.05) 0px 2px 4px 0px inset',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
'rgba(0, 0, 0, 0) 0px 0px 0px 0px',
])
})
test('text shadow colors', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="a" class="text-shadow-xs text-shadow-red"></div>
<div id="b" class="text-shadow-lg text-shadow-red"></div>
<div id="c" class="text-shadow-[0px_2px_4px] text-shadow-red"></div>
<div id="d" class="text-shadow-xs text-shadow-red hover:text-shadow-lg">Hello world</div>
<div
id="e"
class="text-shadow-xs text-shadow-red hover:text-shadow-lg hover:text-shadow-initial"
>
Hello world
</div>
<div id="f" class="text-shadow-xs/75">Hello world</div>
<div id="g" class="text-shadow-xs/75 text-shadow-red/75">Hello world</div>
`,
)
expect(await getPropertyValue('#a', 'text-shadow')).toMatch(
/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 1px/,
)
expect(await getPropertyValue('#b', 'text-shadow')).toMatch(
/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 2px, oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 3px 2px, oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 4px 8px/,
)
expect(await getPropertyValue('#c', 'text-shadow')).toMatch(
/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 2px 4px/,
)
expect(await getPropertyValue('#d', 'text-shadow')).toMatch(
/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 1px/,
)
expect(await getPropertyValue('#f', 'text-shadow')).toEqual('oklab(0 0 0 / 0.75) 0px 1px 1px')
expect(await getPropertyValue('#g', 'text-shadow')).toMatch(
/oklab\(0.627\d+ 0.224\d+ 0.125\d+ \/ 0.56\d+\) 0px 1px 1px/,
)
await page.locator('#d').hover()
expect(await getPropertyValue('#d', 'text-shadow')).toMatch(
/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 2px, oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 3px 2px, oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 4px 8px/,
)
expect(await getPropertyValue('#e', 'text-shadow')).toMatch(
/oklab\(0.627\d+ 0.224\d+ 0.125\d+\) 0px 1px 1px/,
)
await page.locator('#e').hover()
expect(await getPropertyValue('#e', 'text-shadow')).toEqual(
'rgba(0, 0, 0, 0.1) 0px 1px 2px, rgba(0, 0, 0, 0.1) 0px 3px 2px, rgba(0, 0, 0, 0.1) 0px 4px 8px',
)
})
test('filter', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div
id="a"
class="blur-md brightness-50 contrast-50 drop-shadow-md grayscale hue-rotate-180 invert saturate-50 sepia"
>
<div id="b" class="contrast-100"></div>
</div>
`,
)
expect(await getPropertyValue('#a', 'filter')).toEqual(
[
'blur(12px)',
'brightness(0.5)',
'contrast(0.5)',
'grayscale(1)',
'hue-rotate(180deg)',
'invert(1)',
'saturate(0.5)',
'sepia(1)',
'drop-shadow(rgba(0, 0, 0, 0.12) 0px 3px 3px)',
].join(' '),
)
expect(await getPropertyValue('#b', 'filter')).toEqual('contrast(1)')
})
test('drop shadow colors', async ({ page }) => {
let { getPropertyList } = await render(
page,
html`
<div id="a" class="drop-shadow-md"></div>
<div id="b" class="drop-shadow-md drop-shadow-red"></div>
<div id="c" class="drop-shadow-md/50"></div>
<div id="d" class="drop-shadow-md/50 drop-shadow-red"></div>
<div id="e" class="drop-shadow-md/50 drop-shadow-red/50"></div>
`,
)
expect(await getPropertyList('#a', 'filter')).toEqual([
'drop-shadow(rgba(0, 0, 0, 0.12) 0px 3px 3px)',
])
expect(await getPropertyList('#b', 'filter')).toEqual([
expect.stringMatching(/drop-shadow\(oklab\(0\.627\d+ 0\.224\d+ 0\.125\d+\) 0px 3px 3px\)/),
])
expect(await getPropertyList('#c', 'filter')).toEqual([
'drop-shadow(oklab(0 0 0 / 0.5) 0px 3px 3px)',
])
expect(await getPropertyList('#d', 'filter')).toEqual([
expect.stringMatching(
/drop-shadow\(oklab\(0\.627\d+ 0\.224\d+ 0\.125\d+ \/ 0\.5\) 0px 3px 3px\)/,
),
])
expect(await getPropertyList('#e', 'filter')).toEqual([
expect.stringMatching(
/drop-shadow\(oklab\(0\.627\d+ 0\.224\d+ 0\.125\d+ \/ 0\.25\) 0px 3px 3px\)/,
),
])
})
test('multiple drop shadow filters with `@theme inline`', async ({ page }) => {
let { getPropertyList } = await render(
page,
html`
<div id="a" class="drop-shadow-inlined"></div>
<div id="b" class="drop-shadow-inlined drop-shadow-red"></div>
<div style="--drop-shadow-var: 0 20px 20px rgb(0 0 0 / 0.75)">
<div id="c" class="drop-shadow-var"></div>
</div>
<div style="--drop-shadow-var: 0 10px 10px rgb(0 0 0 / 0.75), 0 20px 20px rgb(0 0 0 / 0.75)">
<div id="d" class="drop-shadow-var"></div>
</div>
`,
css`
@theme {
--drop-shadow-var: 0 1px 1px rgb(0 0 0 / 0.5), 0 10px 10px rgb(0 0 0 / 0.25);
}
@theme inline {
--drop-shadow-inlined: 0 1px 1px rgb(0 0 0 / 0.5), 0 10px 10px rgb(0 0 0 / 0.25);
}
`,
)
expect(await getPropertyList('#a', 'filter')).toEqual([
'drop-shadow(rgba(0, 0, 0, 0.5) 0px 1px 1px) drop-shadow(rgba(0, 0, 0, 0.25) 0px 10px 10px)',
])
expect(await getPropertyList('#b', 'filter')).toEqual([
expect.stringMatching(
/drop-shadow\(oklab\(0\.627\d+ 0\.224\d+ 0\.125\d+\) 0px 1px 1px\) drop-shadow\(oklab\(0\.627\d+ 0\.224\d+ 0\.125\d+\) 0px 10px 10px\)/,
),
])
expect(await getPropertyList('#c', 'filter')).toEqual([
'drop-shadow(rgba(0, 0, 0, 0.75) 0px 20px 20px)',
])
expect(await getPropertyList('#d', 'filter')).toEqual(['none'])
})
test('outline style is optional', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="outline-2 outline-white"></div>`,
)
expect(await getPropertyValue('#x', 'outline')).toEqual('rgb(255, 255, 255) solid 2px')
})
test('outline style is preserved when changing outline width', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="outline-2 outline-dotted outline-white hover:outline-4">
Hello world
</div>`,
)
expect(await getPropertyValue('#x', 'outline')).toEqual('rgb(255, 255, 255) dotted 2px')
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'outline')).toEqual('rgb(255, 255, 255) dotted 4px')
})
test('borders can be added without a border-style utility', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="text-black border-2"></div>`,
)
expect(await getPropertyValue('#x', 'border')).toEqual('2px solid rgb(0, 0, 0)')
})
test('borders can be added to a single side without a border-style utility', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="text-black border-r-2 border-dashed hover:border-r-4">
Hello world
</div>`,
)
expect(await getPropertyValue('#x', 'border-right')).toEqual('2px dashed rgb(0, 0, 0)')
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'border-right')).toEqual('4px dashed rgb(0, 0, 0)')
})
test('dividers can be added without setting border-style', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="a" class="divide-y-2 divide-dashed hover:divide-y-4">
<div id="b">First</div>
<div>Second</div>
</div>`,
)
expect(await getPropertyValue('#b', 'border-bottom')).toEqual('2px dashed rgb(0, 0, 0)')
await page.locator('#a').hover()
expect(await getPropertyValue('#b', 'border-bottom')).toEqual('4px dashed rgb(0, 0, 0)')
})
test('scale can be a number or percentage', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="scale-[50%] hover:scale-[1.5]">Hello world</div>`,
)
expect(await getPropertyValue('#x', 'scale')).toEqual('0.5')
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'scale')).toEqual('1.5')
})
test('content-none persists when conditionally styling a pseudo-element', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`<div id="x" class="after:content-none after:hover:underline">Hello world</div>`,
)
expect(await getPropertyValue(['#x', '::after'], 'content')).toEqual('none')
await page.locator('#x').hover()
expect(await getPropertyValue(['#x', '::after'], 'content')).toEqual('none')
})
test('explicit leading utilities are respected when overriding font-size', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="text-sm hover:text-xl">Hello world</div>
<div id="y" class="text-sm leading-tight hover:text-xl">Hello world</div>
<div id="z" class="text-sm leading-[10px] hover:text-xl">Hello world</div>
`,
css`
@theme {
--text-sm: 14px;
--text-sm--line-height: 16px;
--text-xl: 20px;
--text-xl--line-height: 24px;
--leading-tight: 8px;
}
`,
)
expect(await getPropertyValue('#x', 'line-height')).toEqual('16px')
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'line-height')).toEqual('24px')
expect(await getPropertyValue('#y', 'line-height')).toEqual('8px')
await page.locator('#y').hover()
expect(await getPropertyValue('#y', 'line-height')).toEqual('8px')
expect(await getPropertyValue('#z', 'line-height')).toEqual('10px')
await page.locator('#z').hover()
expect(await getPropertyValue('#z', 'line-height')).toEqual('10px')
})
test('explicit leading utilities are overridden by line-height modifiers', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="text-sm hover:text-xl/[100px]">Hello world</div>
<div id="y" class="text-sm leading-tight hover:text-xl/[100px]">Hello world</div>
<div id="z" class="text-sm leading-[10px] hover:text-xl/[100px]">Hello world</div>
`,
css`
@theme {
--text-sm: 14px;
--text-sm--line-height: 16px;
--text-xl: 20px;
--text-xl--line-height: 24px;
--leading-tight: 8px;
}
`,
)
expect(await getPropertyValue('#x', 'line-height')).toEqual('16px')
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'line-height')).toEqual('100px')
expect(await getPropertyValue('#y', 'line-height')).toEqual('8px')
await page.locator('#y').hover()
expect(await getPropertyValue('#y', 'line-height')).toEqual('100px')
expect(await getPropertyValue('#z', 'line-height')).toEqual('10px')
await page.locator('#z').hover()
expect(await getPropertyValue('#z', 'line-height')).toEqual('100px')
})
test('explicit tracking utilities are respected when overriding font-size', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="text-sm hover:text-xl">Hello world</div>
<div id="y" class="text-sm tracking-tight hover:text-xl">Hello world</div>
<div id="z" class="text-sm tracking-[3px] hover:text-xl">Hello world</div>
`,
css`
@theme {
--text-sm--letter-spacing: 5px;
--text-xl--letter-spacing: 10px;
--tracking-tight: 1px;
}
`,
)
expect(await getPropertyValue('#x', 'letter-spacing')).toEqual('5px')
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'letter-spacing')).toEqual('10px')
expect(await getPropertyValue('#y', 'letter-spacing')).toEqual('1px')
await page.locator('#y').hover()
expect(await getPropertyValue('#y', 'letter-spacing')).toEqual('1px')
expect(await getPropertyValue('#z', 'letter-spacing')).toEqual('3px')
await page.locator('#z').hover()
expect(await getPropertyValue('#z', 'letter-spacing')).toEqual('3px')
})
test('explicit font-weight utilities are respected when overriding font-size', async ({ page }) => {
let { getPropertyValue } = await render(
page,
html`
<div id="x" class="text-sm hover:text-xl">Hello world</div>
<div id="y" class="text-sm font-bold hover:text-xl">Hello world</div>
<div id="z" class="text-sm font-[900] hover:text-xl">Hello world</div>
`,
css`
@theme {
--text-sm--font-weight: 100;
--text-xl--font-weight: 200;
}
`,
)
expect(await getPropertyValue('#x', 'font-weight')).toEqual('100')
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'font-weight')).toEqual('200')
expect(await getPropertyValue('#y', 'font-weight')).toEqual('700')
await page.locator('#y').hover()
expect(await getPropertyValue('#y', 'font-weight')).toEqual('700')
expect(await getPropertyValue('#z', 'font-weight')).toEqual('900')
await page.locator('#z').hover()
expect(await getPropertyValue('#z', 'font-weight')).toEqual('900')
})
test('explicit duration and ease utilities are respected when overriding transition-property', async ({
page,
}) => {
let { getPropertyValue } = await render(
page,
html`
<div
id="x"
class="ease-[linear] duration-500 transition-[opacity] hover:transition-[background-color]"
>
Hello world
</div>
`,
)
expect(await getPropertyValue('#x', 'transition-timing-function')).toEqual('linear')
expect(await getPropertyValue('#x', 'transition-duration')).toEqual('0.5s')
await page.locator('#x').hover()
expect(await getPropertyValue('#x', 'transition-timing-function')).toEqual('linear')
expect(await getPropertyValue('#x', 'transition-duration')).toEqual('0.5s')
})
test('shadow DOM has access to variables', async ({ page }) => {
await render(
page,
html`
<script type="text/javascript">
class Component extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
}
connectedCallback() {
this.shadowRoot.innerHTML =
'<style>' +
document.querySelector('style').textContent +
'</style>' +
'<div class="flex gap-2" id="x">' +
' <div>one</div>' +
' <div>two</div>' +
'</div>'
}
}
customElements.define('my-component', Component)
</script>
<my-component id="shadow"></my-component>
`,
)
let gap = await page.evaluate(() => {
let shadowRoot = document.querySelector('#shadow')!.shadowRoot!
let x = shadowRoot.querySelector('#x')!
return window.getComputedStyle(x).getPropertyValue('gap')
})
expect(gap).toBe('8px')
})
const preflight = fs.readFileSync(path.resolve(__dirname, '..', 'preflight.css'), 'utf-8')
const defaultTheme = fs.readFileSync(path.resolve(__dirname, '..', 'theme.css'), 'utf-8')
async function render(page: Page, content: string, extraCss: string = '') {
let { build } = await compile(css`
@layer theme, base, components, utilities;
@layer theme {
${defaultTheme}
@theme {
--color-red: rgb(255, 0, 0);
--color-green: rgb(0, 255, 0);
--color-blue: rgb(0, 0, 255);
--color-black: black;
--color-transparent: transparent;
}
}
@layer base {
${preflight}
}
@layer utilities {
@tailwind utilities;
}
${extraCss}
`)
content = `<div id="mouse-park" class="size-12"></div>${content}`
let scanner = new Scanner({})
let candidates = scanner.scanFiles([{ content, extension: 'html' }])
let { code: styles } = optimize(build(candidates))
content = `<style type="text/css">${styles}</style>${content}`
await page.setContent(content)
await page.locator('#mouse-park').hover()
return {
getPropertyValue(selector: string | [string, string], property: string) {
return getPropertyValue(
page,
Array.isArray(selector) ? selector : [selector, undefined],
property,
)
},
async getPropertyList(selector: string | [string, string], property: string) {
let value = await getPropertyValue(
page,
Array.isArray(selector) ? selector : [selector, undefined],
property,
)
return segment(value, ',').map((item) => item.trim())
},
}
}
async function getPropertyValue(
page: Page,
selector: [string, string | undefined],
property: string,
) {
return page.evaluate(
([[selector, pseudo], property]) => {
return window
.getComputedStyle(document.querySelector(selector)!, pseudo)
.getPropertyValue(property)
},
[selector, property] as const,
)
}