Use variables with fallbacks for utility classes (#13177)

* Use variables with fallbacks for utility classes

* Update playwright test

* rename `resolveBare` to `resolveValue`

* make private methods really private

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This commit is contained in:
Adam Wathan 2024-03-21 13:25:43 -04:00 • committed by GitHub
parent a3cedf253a
commit b5df5e9202
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 323 additions and 305 deletions

View file

@ -585,12 +585,12 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
@layer utilities {
.text-2xl {
font-size: 1.5rem;
line-height: 2rem;
font-size: var(--font-size-2xl, 1.5rem);
line-height: var(--font-size-2xl--line-height, 2rem);
}
.text-black\\/50 {
color: #00000080;
color: color-mix(in srgb, var(--color-black, #000) 50%, transparent);
}
.underline {

View file

@ -104,7 +104,7 @@ test('@apply can be used without emitting the theme in the CSS file', async () =
expect(result.css.trim()).toMatchInlineSnapshot(`
".foo {
color: #ef4444;
color: var(--color-red-500, #ef4444);
}"
`)
})

View file

@ -404,11 +404,11 @@ exports[`compiling CSS > \`@tailwind utilities\` is replaced by utilities using
}
.w-4 {
width: 1rem;
width: var(--spacing-4, 1rem);
}
.bg-red-500 {
background-color: #ef4444;
background-color: var(--color-red-500, #ef4444);
}
.shadow {

View file

@ -95,11 +95,11 @@ exports[`border-* 1`] = `
}
.border-red-500 {
border-color: #ef4444;
border-color: var(--color-red-500, #ef4444);
}
.border-red-500\\/50 {
border-color: #ef444480;
border-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
}
.border-transparent {
@ -208,11 +208,11 @@ exports[`border-b-* 1`] = `
}
.border-b-red-500 {
border-bottom-color: #ef4444;
border-bottom-color: var(--color-red-500, #ef4444);
}
.border-b-red-500\\/50 {
border-bottom-color: #ef444480;
border-bottom-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
}
.border-b-transparent {
@ -321,11 +321,11 @@ exports[`border-e-* 1`] = `
}
.border-e-red-500 {
border-inline-end-color: #ef4444;
border-inline-end-color: var(--color-red-500, #ef4444);
}
.border-e-red-500\\/50 {
border-inline-end-color: #ef444480;
border-inline-end-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
}
.border-e-transparent {
@ -434,11 +434,11 @@ exports[`border-l-* 1`] = `
}
.border-l-red-500 {
border-left-color: #ef4444;
border-left-color: var(--color-red-500, #ef4444);
}
.border-l-red-500\\/50 {
border-left-color: #ef444480;
border-left-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
}
.border-l-transparent {
@ -547,11 +547,11 @@ exports[`border-r-* 1`] = `
}
.border-r-red-500 {
border-right-color: #ef4444;
border-right-color: var(--color-red-500, #ef4444);
}
.border-r-red-500\\/50 {
border-right-color: #ef444480;
border-right-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
}
.border-r-transparent {
@ -660,11 +660,11 @@ exports[`border-s-* 1`] = `
}
.border-s-red-500 {
border-inline-start-color: #ef4444;
border-inline-start-color: var(--color-red-500, #ef4444);
}
.border-s-red-500\\/50 {
border-inline-start-color: #ef444480;
border-inline-start-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
}
.border-s-transparent {
@ -773,11 +773,11 @@ exports[`border-t-* 1`] = `
}
.border-t-red-500 {
border-top-color: #ef4444;
border-top-color: var(--color-red-500, #ef4444);
}
.border-t-red-500\\/50 {
border-top-color: #ef444480;
border-top-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
}
.border-t-transparent {
@ -915,13 +915,13 @@ exports[`border-x-* 1`] = `
}
.border-x-red-500 {
border-left-color: #ef4444;
border-right-color: #ef4444;
border-left-color: var(--color-red-500, #ef4444);
border-right-color: var(--color-red-500, #ef4444);
}
.border-x-red-500\\/50 {
border-left-color: #ef444480;
border-right-color: #ef444480;
border-left-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
border-right-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
}
.border-x-transparent {
@ -1060,13 +1060,13 @@ exports[`border-y-* 1`] = `
}
.border-y-red-500 {
border-top-color: #ef4444;
border-bottom-color: #ef4444;
border-top-color: var(--color-red-500, #ef4444);
border-bottom-color: var(--color-red-500, #ef4444);
}
.border-y-red-500\\/50 {
border-top-color: #ef444480;
border-bottom-color: #ef444480;
border-top-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
border-bottom-color: color-mix(in srgb, var(--color-red-500, #ef4444) 50%, transparent);
}
.border-y-transparent {

View file

@ -44,7 +44,7 @@ describe('compiling CSS', () => {
@media (prefers-color-scheme: dark) {
.dark\\:bg-black {
background-color: #000;
background-color: var(--color-black, #000);
}
}
}"
@ -186,28 +186,28 @@ describe('@apply', () => {
.foo {
--tw-translate-x: 100%;
translate: var(--tw-translate-x) var(--tw-translate-y);
background-color: #ef4444;
animation: var(--animate-spin, spin 1s linear infinite);
background-color: var(--color-red-500, #ef4444);
text-decoration-line: underline;
animation: 1s linear infinite spin;
}
.foo:hover {
background-color: #3b82f6;
background-color: var(--color-blue-500, #3b82f6);
}
@media (width >= 768px) {
.foo {
background-color: #22c55e;
background-color: var(--color-green-500, #22c55e);
}
}
.foo:hover:focus {
background-color: #fecaca;
background-color: var(--color-red-200, #fecaca);
}
@media (width >= 768px) {
.foo:hover:focus {
background-color: #bbf7d0;
background-color: var(--color-green-200, #bbf7d0);
}
}
@ -418,7 +418,7 @@ describe('important', () => {
}
.animate-spin\\! {
animation: 1s linear infinite spin !important;
animation: var(--animate-spin, spin 1s linear infinite) !important;
}
@keyframes spin {
@ -464,16 +464,16 @@ describe('sorting', () => {
}
.p-1 {
padding: .25rem;
padding: var(--spacing-1, .25rem);
}
.px-1 {
padding-left: .25rem;
padding-right: .25rem;
padding-left: var(--spacing-1, .25rem);
padding-right: var(--spacing-1, .25rem);
}
.pl-1 {
padding-left: .25rem;
padding-left: var(--spacing-1, .25rem);
}"
`)
})
@ -522,17 +522,17 @@ describe('sorting', () => {
}
.mx-0 {
margin-left: 0;
margin-right: 0;
margin-left: var(--spacing-0, 0px);
margin-right: var(--spacing-0, 0px);
}
.gap-4 {
gap: 1rem;
gap: var(--spacing-4, 1rem);
}
:where(.space-x-2 > :not([hidden]) ~ :not([hidden])) {
margin-inline-start: calc(.5rem * calc(1 - var(--tw-space-x-reverse)));
margin-inline-end: calc(.5rem * var(--tw-space-x-reverse));
margin-inline-start: calc(var(--spacing-2, .5rem) * calc(1 - var(--tw-space-x-reverse)));
margin-inline-end: calc(var(--spacing-2, .5rem) * var(--tw-space-x-reverse));
}
@property --tw-space-x-reverse {
@ -713,7 +713,7 @@ describe('Parsing themes values from CSS', () => {
}
.accent-red-500 {
accent-color: red;
accent-color: var(--color-red-500, red);
}"
`)
})
@ -736,7 +736,7 @@ describe('Parsing themes values from CSS', () => {
}
.accent-red-500 {
accent-color: #f10;
accent-color: var(--color-red-500, #f10);
}"
`)
})
@ -762,11 +762,11 @@ describe('Parsing themes values from CSS', () => {
}
.accent-blue-500 {
accent-color: #00f;
accent-color: var(--color-blue-500, #00f);
}
.accent-red-500 {
accent-color: red;
accent-color: var(--color-red-500, red);
}"
`)
})
@ -791,11 +791,11 @@ describe('Parsing themes values from CSS', () => {
}
.w-1\\/2 {
width: 75%;
width: var(--width-1\\/2, 75%);
}
.w-75\\% {
width: 50%;
width: var(--width-75\\%, 50%);
}"
`)
})
@ -828,11 +828,11 @@ describe('Parsing themes values from CSS', () => {
}
.text-lg {
font-size: 20px;
font-size: var(--font-size-lg, 20px);
}
.accent-red {
accent-color: red;
accent-color: var(--color-red, red);
}
@keyframes foo {
@ -871,11 +871,11 @@ describe('Parsing themes values from CSS', () => {
}
.text-sm {
font-size: 13px;
font-size: var(--font-size-sm, 13px);
}
.accent-green {
accent-color: #0f0;
accent-color: var(--color-green, #0f0);
}"
`)
})
@ -906,7 +906,7 @@ describe('Parsing themes values from CSS', () => {
}
.accent-green {
accent-color: #0f0;
accent-color: var(--color-green, #0f0);
}"
`)
})
@ -944,7 +944,7 @@ describe('Parsing themes values from CSS', () => {
}
.animate-foobar {
animation: 1s infinite foobar;
animation: var(--animate-foobar, foobar 1s infinite);
}
@keyframes foobar {
@ -975,11 +975,11 @@ describe('Parsing themes values from CSS', () => {
}
.bg-potato {
background-color: #ac855b;
background-color: var(--color-potato, #ac855b);
}
.bg-tomato {
background-color: #e10c04;
background-color: var(--color-tomato, #e10c04);
}"
`)
})
@ -1000,7 +1000,7 @@ describe('Parsing themes values from CSS', () => {
),
).toMatchInlineSnapshot(`
".bg-potato {
background-color: #c794aa;
background-color: var(--color-potato, #c794aa);
}"
`)
})
@ -1025,7 +1025,7 @@ describe('Parsing themes values from CSS', () => {
}
.bg-potato {
background-color: #c794aa;
background-color: var(--color-potato, #c794aa);
}"
`)
})
@ -1055,15 +1055,15 @@ describe('Parsing themes values from CSS', () => {
}
.bg-avocado {
background-color: #c0cc6d;
background-color: var(--color-avocado, #c0cc6d);
}
.bg-potato {
background-color: #ac855b;
background-color: var(--color-potato, #ac855b);
}
.bg-tomato {
background-color: #e10c04;
background-color: var(--color-tomato, #e10c04);
}"
`)
})

View file

@ -11,7 +11,7 @@ export class Theme {
if (key === '--*') {
this.values.clear()
} else {
this.clearNamespace(key.slice(0, -2))
this.#clearNamespace(key.slice(0, -2))
}
}
@ -57,7 +57,7 @@ export class Theme {
return this.values.entries()
}
clearNamespace(namespace: string) {
#clearNamespace(namespace: string) {
for (let key of this.values.keys()) {
if (key.startsWith(namespace)) {
this.values.delete(key)
@ -65,7 +65,7 @@ export class Theme {
}
}
resolveKey(candidateValue: string, themeKeys: ThemeKey[]): string | null {
#resolveKey(candidateValue: string, themeKeys: ThemeKey[]): string | null {
for (let key of themeKeys) {
let themeKey = escape(`${key}-${candidateValue.replaceAll('.', '_')}`)
@ -77,8 +77,24 @@ export class Theme {
return null
}
#var(themeKey: string) {
if (!this.values.has(themeKey)) {
return null
}
return `var(${themeKey}, ${this.values.get(themeKey)?.value})`
}
resolve(candidateValue: string, themeKeys: ThemeKey[]): string | null {
let themeKey = this.resolveKey(candidateValue, themeKeys)
let themeKey = this.#resolveKey(candidateValue, themeKeys)
if (!themeKey) return null
return this.#var(themeKey)
}
resolveValue(candidateValue: string, themeKeys: ThemeKey[]): string | null {
let themeKey = this.#resolveKey(candidateValue, themeKeys)
if (!themeKey) return null
@ -90,19 +106,19 @@ export class Theme {
themeKeys: ThemeKey[],
nestedKeys: `--${string}`[] = [],
): [string, Record<string, string>] | null {
let themeKey = this.resolveKey(candidateValue, themeKeys)
let themeKey = this.#resolveKey(candidateValue, themeKeys)
if (!themeKey) return null
let extra = {} as Record<string, string>
for (let name of nestedKeys) {
let nestedValue = this.values.get(`${themeKey}${name}`)
let nestedValue = this.#var(`${themeKey}${name}`)
if (nestedValue) {
extra[name] = nestedValue.value
extra[name] = nestedValue
}
}
return [this.values.get(themeKey)!.value, extra]
return [this.#var(themeKey)!, extra]
}
namespace(namespace: string) {

File diff suppressed because it is too large Load diff

View file

@ -512,7 +512,7 @@ export function createVariants(theme: Theme): Variants {
if (variant.value.kind === 'arbitrary') {
value = variant.value.value
} else if (variant.value.kind === 'named') {
value = theme.resolve(variant.value.value, ['--breakpoint'])
value = theme.resolveValue(variant.value.value, ['--breakpoint'])
}
if (!value) return null
@ -597,7 +597,7 @@ export function createVariants(theme: Theme): Variants {
if (variant.value.kind === 'arbitrary') {
value = variant.value.value
} else if (variant.value.kind === 'named') {
value = theme.resolve(variant.value.value, ['--width'])
value = theme.resolveValue(variant.value.value, ['--width'])
}
if (!value) return null

View file

@ -145,7 +145,7 @@ test('composing shadow, inset shadow, ring, and inset ring', async ({ page }) =>
page,
html`<div
id="x"
class="shadow shadow-[#f00]/50 inset-shadow inset-shadow-[#0f0]/50 ring ring-white/50 inset-ring inset-ring-[#00f]/50"
class="shadow shadow-[#f00]/50 inset-shadow inset-shadow-[#0f0]/50 ring ring-[#fff]/50 inset-ring inset-ring-[#00f]/50"
></div>`,
)