From 6e1f53348d581bdb3c03b437186ae0129651b406 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Fri, 11 Apr 2025 16:38:46 +0200 Subject: [PATCH] Workaround Chrome rendering bug for `skew-*` utilities (#17627) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #17621 Chrome as a pretty ugly rendering glitch when using a `skew-*` utility in Tailwind 4: https://play.tailwindcss.com/HuiZtbrHOc The was not an issue in v3 since transforms were set up in a different way. Without the `var(…)` syntax, the difference boils down to this: ```css .skew-v3 { transform: rotate(0) skewX(-20deg); } .skew-v4 { transform: rotateX(0) rotateY(0) rotateZ(0) skewX(-20deg); } ``` It appears that using any of the single-dimension rotate functions will cause the Chrome rendering to glitch. After doing some digging, we found [that initially these `@property`s were defined as type `` and later changed to `*`](https://github.com/tailwindlabs/tailwindcss/issues/15144). With a type of `*`, it makes sense that the initial value of these variables can default to `initial` without any compromises, allowing us to default to something like this now: ```css .skew-new { transform: skewX(-20deg); } ``` Tested this change in the latest version of Chrome (135) and it does make the rendering glitch in the initial issue disappear. By using the `var(--tw-rotate-x,)` syntax we also ensure this works on older versions of Safari (tested on Safari 15.5 and 16.4). Note, however, that there are still glitches happening when you combine rotate and skew in the latest version of Chrome or when you transition the `skew(…)` variable. This also happens in plain CSS with no variables though, so there isn't something we can do about this: https://play.tailwindcss.com/g3FXPEJHpn ## Test plan - Tested on latest Chrome, Firefox, and Safari as well as Safari 15.5 and 16.4. Screenshot 2025-04-09 at 18 01 51 --- CHANGELOG.md | 1 + packages/tailwindcss/src/utilities.test.ts | 221 +++++++++------------ packages/tailwindcss/src/utilities.ts | 20 +- 3 files changed, 104 insertions(+), 138 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bc26c2fa..e026e3bef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure `color-mix(…)` polyfills do not cause used CSS variables to be removed ([#17555](https://github.com/tailwindlabs/tailwindcss/pull/17555)) - Ensure the `color-mix(…)` polyfill creates fallbacks for theme variables that reference other theme variables ([#17562](https://github.com/tailwindlabs/tailwindcss/pull/17562)) - Fix brace expansion in `@source inline('z-{10..0}')` with range going down ([#17591](https://github.com/tailwindlabs/tailwindcss/pull/17591)) +- Work around a Chrome rendering bug when using the `skew-*` utilities ([#17627](https://github.com/tailwindlabs/tailwindcss/pull/17627)) - Ensure container query variant names can contain hyphens ([#17628](https://github.com/tailwindlabs/tailwindcss/pull/17628)) - Ensure `shadow-inherit`, `inset-shadow-inherit`, `drop-shadow-inherit`, and `text-shadow-inherit` inherits the shadow color ([#17647](https://github.com/tailwindlabs/tailwindcss/pull/17647)) - Ensure compatibility with array tuples used in `fontSize` JS theme keys ([#17630](https://github.com/tailwindlabs/tailwindcss/pull/17630)) diff --git a/packages/tailwindcss/src/utilities.test.ts b/packages/tailwindcss/src/utilities.test.ts index a8c6152d2..549f556e3 100644 --- a/packages/tailwindcss/src/utilities.test.ts +++ b/packages/tailwindcss/src/utilities.test.ts @@ -4472,58 +4472,53 @@ test('rotate-x', async () => { "@layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { - --tw-rotate-x: rotateX(0); - --tw-rotate-y: rotateY(0); - --tw-rotate-z: rotateZ(0); - --tw-skew-x: skewX(0); - --tw-skew-y: skewY(0); + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; } } } .-rotate-x-45 { --tw-rotate-x: rotateX(calc(45deg * -1)); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .rotate-x-45 { --tw-rotate-x: rotateX(45deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .rotate-x-\\[123deg\\] { --tw-rotate-x: rotateX(123deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } @property --tw-rotate-x { syntax: "*"; - inherits: false; - initial-value: rotateX(0); + inherits: false } @property --tw-rotate-y { syntax: "*"; - inherits: false; - initial-value: rotateY(0); + inherits: false } @property --tw-rotate-z { syntax: "*"; - inherits: false; - initial-value: rotateZ(0); + inherits: false } @property --tw-skew-x { syntax: "*"; - inherits: false; - initial-value: skewX(0); + inherits: false } @property --tw-skew-y { syntax: "*"; - inherits: false; - initial-value: skewY(0); + inherits: false }" `) expect( @@ -4545,63 +4540,58 @@ test('rotate-y', async () => { "@layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { - --tw-rotate-x: rotateX(0); - --tw-rotate-y: rotateY(0); - --tw-rotate-z: rotateZ(0); - --tw-skew-x: skewX(0); - --tw-skew-y: skewY(0); + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; } } } .-rotate-y-45 { --tw-rotate-y: rotateY(calc(45deg * -1)); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .-rotate-y-\\[123deg\\] { --tw-rotate-y: rotateY(calc(123deg * -1)); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .rotate-y-45 { --tw-rotate-y: rotateY(45deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .rotate-y-\\[123deg\\] { --tw-rotate-y: rotateY(123deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } @property --tw-rotate-x { syntax: "*"; - inherits: false; - initial-value: rotateX(0); + inherits: false } @property --tw-rotate-y { syntax: "*"; - inherits: false; - initial-value: rotateY(0); + inherits: false } @property --tw-rotate-z { syntax: "*"; - inherits: false; - initial-value: rotateZ(0); + inherits: false } @property --tw-skew-x { syntax: "*"; - inherits: false; - initial-value: skewX(0); + inherits: false } @property --tw-skew-y { syntax: "*"; - inherits: false; - initial-value: skewY(0); + inherits: false }" `) expect( @@ -4623,63 +4613,58 @@ test('rotate-z', async () => { "@layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { - --tw-rotate-x: rotateX(0); - --tw-rotate-y: rotateY(0); - --tw-rotate-z: rotateZ(0); - --tw-skew-x: skewX(0); - --tw-skew-y: skewY(0); + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; } } } .-rotate-z-45 { --tw-rotate-z: rotateZ(calc(45deg * -1)); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .-rotate-z-\\[123deg\\] { --tw-rotate-z: rotateZ(calc(123deg * -1)); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .rotate-z-45 { --tw-rotate-z: rotateZ(45deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .rotate-z-\\[123deg\\] { --tw-rotate-z: rotateZ(123deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } @property --tw-rotate-x { syntax: "*"; - inherits: false; - initial-value: rotateX(0); + inherits: false } @property --tw-rotate-y { syntax: "*"; - inherits: false; - initial-value: rotateY(0); + inherits: false } @property --tw-rotate-z { syntax: "*"; - inherits: false; - initial-value: rotateZ(0); + inherits: false } @property --tw-skew-x { syntax: "*"; - inherits: false; - initial-value: skewX(0); + inherits: false } @property --tw-skew-y { syntax: "*"; - inherits: false; - initial-value: skewY(0); + inherits: false }" `) expect( @@ -4700,11 +4685,11 @@ test('skew', async () => { "@layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { - --tw-rotate-x: rotateX(0); - --tw-rotate-y: rotateY(0); - --tw-rotate-z: rotateZ(0); - --tw-skew-x: skewX(0); - --tw-skew-y: skewY(0); + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; } } } @@ -4712,49 +4697,44 @@ test('skew', async () => { .-skew-6 { --tw-skew-x: skewX(calc(6deg * -1)); --tw-skew-y: skewY(calc(6deg * -1)); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .skew-6 { --tw-skew-x: skewX(6deg); --tw-skew-y: skewY(6deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .skew-\\[123deg\\] { --tw-skew-x: skewX(123deg); --tw-skew-y: skewY(123deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } @property --tw-rotate-x { syntax: "*"; - inherits: false; - initial-value: rotateX(0); + inherits: false } @property --tw-rotate-y { syntax: "*"; - inherits: false; - initial-value: rotateY(0); + inherits: false } @property --tw-rotate-z { syntax: "*"; - inherits: false; - initial-value: rotateZ(0); + inherits: false } @property --tw-skew-x { syntax: "*"; - inherits: false; - initial-value: skewX(0); + inherits: false } @property --tw-skew-y { syntax: "*"; - inherits: false; - initial-value: skewY(0); + inherits: false }" `) expect( @@ -4774,58 +4754,53 @@ test('skew-x', async () => { "@layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { - --tw-rotate-x: rotateX(0); - --tw-rotate-y: rotateY(0); - --tw-rotate-z: rotateZ(0); - --tw-skew-x: skewX(0); - --tw-skew-y: skewY(0); + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; } } } .-skew-x-6 { --tw-skew-x: skewX(calc(6deg * -1)); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .skew-x-6 { --tw-skew-x: skewX(6deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .skew-x-\\[123deg\\] { --tw-skew-x: skewX(123deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } @property --tw-rotate-x { syntax: "*"; - inherits: false; - initial-value: rotateX(0); + inherits: false } @property --tw-rotate-y { syntax: "*"; - inherits: false; - initial-value: rotateY(0); + inherits: false } @property --tw-rotate-z { syntax: "*"; - inherits: false; - initial-value: rotateZ(0); + inherits: false } @property --tw-skew-x { syntax: "*"; - inherits: false; - initial-value: skewX(0); + inherits: false } @property --tw-skew-y { syntax: "*"; - inherits: false; - initial-value: skewY(0); + inherits: false }" `) expect( @@ -4845,58 +4820,53 @@ test('skew-y', async () => { "@layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { - --tw-rotate-x: rotateX(0); - --tw-rotate-y: rotateY(0); - --tw-rotate-z: rotateZ(0); - --tw-skew-x: skewX(0); - --tw-skew-y: skewY(0); + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; } } } .-skew-y-6 { --tw-skew-y: skewY(calc(6deg * -1)); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .skew-y-6 { --tw-skew-y: skewY(6deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .skew-y-\\[123deg\\] { --tw-skew-y: skewY(123deg); - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } @property --tw-rotate-x { syntax: "*"; - inherits: false; - initial-value: rotateX(0); + inherits: false } @property --tw-rotate-y { syntax: "*"; - inherits: false; - initial-value: rotateY(0); + inherits: false } @property --tw-rotate-z { syntax: "*"; - inherits: false; - initial-value: rotateZ(0); + inherits: false } @property --tw-skew-x { syntax: "*"; - inherits: false; - initial-value: skewX(0); + inherits: false } @property --tw-skew-y { syntax: "*"; - inherits: false; - initial-value: skewY(0); + inherits: false }" `) expect( @@ -5244,17 +5214,17 @@ test('transform', async () => { "@layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { - --tw-rotate-x: rotateX(0); - --tw-rotate-y: rotateY(0); - --tw-rotate-z: rotateZ(0); - --tw-skew-x: skewX(0); - --tw-skew-y: skewY(0); + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; } } } .transform { - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .transform-\\[scaleZ\\(2\\)_rotateY\\(45deg\\)\\] { @@ -5262,11 +5232,11 @@ test('transform', async () => { } .transform-cpu { - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .transform-gpu { - transform: translateZ(0) var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: translateZ(0) var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, ); } .transform-none { @@ -5275,32 +5245,27 @@ test('transform', async () => { @property --tw-rotate-x { syntax: "*"; - inherits: false; - initial-value: rotateX(0); + inherits: false } @property --tw-rotate-y { syntax: "*"; - inherits: false; - initial-value: rotateY(0); + inherits: false } @property --tw-rotate-z { syntax: "*"; - inherits: false; - initial-value: rotateZ(0); + inherits: false } @property --tw-skew-x { syntax: "*"; - inherits: false; - initial-value: skewX(0); + inherits: false } @property --tw-skew-y { syntax: "*"; - inherits: false; - initial-value: skewY(0); + inherits: false }" `) expect( diff --git a/packages/tailwindcss/src/utilities.ts b/packages/tailwindcss/src/utilities.ts index 3bd91af0c..2f121d340 100644 --- a/packages/tailwindcss/src/utilities.ts +++ b/packages/tailwindcss/src/utilities.ts @@ -1397,20 +1397,20 @@ export function createUtilities(theme: Theme) { { let transformValue = [ - 'var(--tw-rotate-x)', - 'var(--tw-rotate-y)', - 'var(--tw-rotate-z)', - 'var(--tw-skew-x)', - 'var(--tw-skew-y)', + 'var(--tw-rotate-x,)', + 'var(--tw-rotate-y,)', + 'var(--tw-rotate-z,)', + 'var(--tw-skew-x,)', + 'var(--tw-skew-y,)', ].join(' ') let transformProperties = () => atRoot([ - property('--tw-rotate-x', 'rotateX(0)'), - property('--tw-rotate-y', 'rotateY(0)'), - property('--tw-rotate-z', 'rotateZ(0)'), - property('--tw-skew-x', 'skewX(0)'), - property('--tw-skew-y', 'skewY(0)'), + property('--tw-rotate-x'), + property('--tw-rotate-y'), + property('--tw-rotate-z'), + property('--tw-skew-x'), + property('--tw-skew-y'), ]) for (let axis of ['x', 'y', 'z']) {