From 98c279d1fbfae6f96b0e5fdb13d0e4da012ac76d Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Mon, 11 Nov 2024 19:45:37 +0100 Subject: [PATCH] Don't create bare spacing utilities with invalid multiples (#14962) Closes #14960 When we moved to the `--spacing` multiples scale, we seemingly overlooked a bail that caused us to use non-numerical values as a spacing multiple. This caused the `-translate-x-full` and `-translate-y-full` utilities to treat `full` as a valid multiple in our spacing scale and created invalid CSS: ```css .-translate-x-full { --tw-translate-x: calc(var(--spacing) * -x-full); --tw-translate-y: calc(var(--spacing) * -x-full); translate: var(--tw-translate-x) var(--tw-translate-y); } ``` ## Test plan I reproduced the issue in our Vite playground and then created a failing test case. It requires a `--spacing` `@theme` variable to be defined so I've added this as a test case now in the unit tests. I also audited all places that are using `calc()` and wrapping some numbers. In doing so I found a few other broken cases: - `-translate-x-full` - `-translate-y-full` - `-space-x-full` - `-space-y-full` - `-inset-full` I validated that the fix indeed works and no longer creates broken CSS definitions for these cases: Screenshot 2024-11-11 at 19 33 51 --------- Co-authored-by: Adam Wathan --- CHANGELOG.md | 1 + packages/tailwindcss/src/utilities.test.ts | 152 +++++++++++++++++++++ packages/tailwindcss/src/utilities.ts | 2 + playgrounds/vite/src/app.tsx | 1 + 4 files changed, 156 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8c5580ab..dd171daf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Don't reset horizontal padding on date/time pseudo-elements ([#14959](https://github.com/tailwindlabs/tailwindcss/pull/14959)) +- Don't emit `calc()` with invalid values for bare values that aren't integers in spacing utilities ([#14962](https://github.com/tailwindlabs/tailwindcss/pull/14962)) ## [4.0.0-alpha.32] - 2024-11-11 diff --git a/packages/tailwindcss/src/utilities.test.ts b/packages/tailwindcss/src/utilities.test.ts index a9c4a6ae2..3335e4168 100644 --- a/packages/tailwindcss/src/utilities.test.ts +++ b/packages/tailwindcss/src/utilities.test.ts @@ -3861,6 +3861,82 @@ test('translate-x', async () => { '-translate-x-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --spacing: 0.25rem; + } + @tailwind utilities; + `, + ['translate-x-full', '-translate-x-full', 'translate-x-px', '-translate-x-[var(--value)]'], + ), + ).toMatchInlineSnapshot(` + ":root { + --spacing: .25rem; + } + + .-translate-x-\\[var\\(--value\\)\\] { + --tw-translate-x: calc(var(--value) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + .-translate-x-full { + --tw-translate-x: -100%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + .translate-x-full { + --tw-translate-x: 100%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + .translate-x-px { + --tw-translate-x: 1px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + @supports (-moz-orient: inline) { + @layer base { + *, :before, :after, ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-translate-z: 0; + } + } + } + + @property --tw-translate-x { + syntax: " | "; + inherits: false; + initial-value: 0; + } + + @property --tw-translate-y { + syntax: " | "; + inherits: false; + initial-value: 0; + } + + @property --tw-translate-z { + syntax: ""; + inherits: false; + initial-value: 0; + }" + `) + expect( + await run([ + 'perspective', + '-perspective', + 'perspective-potato', + 'perspective-123', + 'perspective-normal/foo', + 'perspective-dramatic/foo', + 'perspective-none/foo', + 'perspective-[456px]/foo', + ]), + ).toEqual('') }) test('translate-y', async () => { @@ -3933,6 +4009,82 @@ test('translate-y', async () => { '-translate-y-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --spacing: 0.25rem; + } + @tailwind utilities; + `, + ['translate-y-full', '-translate-y-full', 'translate-y-px', '-translate-y-[var(--value)]'], + ), + ).toMatchInlineSnapshot(` + ":root { + --spacing: .25rem; + } + + .-translate-y-\\[var\\(--value\\)\\] { + --tw-translate-y: calc(var(--value) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + .-translate-y-full { + --tw-translate-y: -100%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + .translate-y-full { + --tw-translate-y: 100%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + .translate-y-px { + --tw-translate-y: 1px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + @supports (-moz-orient: inline) { + @layer base { + *, :before, :after, ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-translate-z: 0; + } + } + } + + @property --tw-translate-x { + syntax: " | "; + inherits: false; + initial-value: 0; + } + + @property --tw-translate-y { + syntax: " | "; + inherits: false; + initial-value: 0; + } + + @property --tw-translate-z { + syntax: ""; + inherits: false; + initial-value: 0; + }" + `) + expect( + await run([ + 'perspective', + '-perspective', + 'perspective-potato', + 'perspective-123', + 'perspective-normal/foo', + 'perspective-dramatic/foo', + 'perspective-none/foo', + 'perspective-[456px]/foo', + ]), + ).toEqual('') }) test('translate-z', async () => { diff --git a/packages/tailwindcss/src/utilities.ts b/packages/tailwindcss/src/utilities.ts index 96ceea6b5..a20938550 100644 --- a/packages/tailwindcss/src/utilities.ts +++ b/packages/tailwindcss/src/utilities.ts @@ -387,6 +387,8 @@ export function createUtilities(theme: Theme) { handleNegativeBareValue: ({ value }) => { let multiplier = theme.resolve(null, ['--spacing']) if (!multiplier) return null + if (!isValidSpacingMultiplier(value)) return null + return `calc(${multiplier} * -${value})` }, handle, diff --git a/playgrounds/vite/src/app.tsx b/playgrounds/vite/src/app.tsx index 8ec502989..4abc17cb5 100644 --- a/playgrounds/vite/src/app.tsx +++ b/playgrounds/vite/src/app.tsx @@ -2,6 +2,7 @@ export function App() { return (

Hello World

+
) }