diff --git a/CHANGELOG.md b/CHANGELOG.md index 34d5c1b9ab93..93accd6a8682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Canonicalization: don't crash when plugin utilities throw for unsupported values ([#20052](https://github.com/tailwindlabs/tailwindcss/pull/20052)) - Allow `@apply` to be used with CSS mixins ([#19427](https://github.com/tailwindlabs/tailwindcss/pull/19427)) - Ensure `not-*` correctly negates `@container` queries, including `style(…)` queries ([#20059](https://github.com/tailwindlabs/tailwindcss/pull/20059)) +- Ensure shadows with math functions parsed correctly ([#20067](https://github.com/tailwindlabs/tailwindcss/pull/20067)) ## [4.3.0] - 2026-05-08 diff --git a/packages/tailwindcss/src/utils/replace-shadow-colors.test.ts b/packages/tailwindcss/src/utils/replace-shadow-colors.test.ts index 6303e5a8d20a..ad23a3aecf1e 100644 --- a/packages/tailwindcss/src/utils/replace-shadow-colors.test.ts +++ b/packages/tailwindcss/src/utils/replace-shadow-colors.test.ts @@ -25,6 +25,26 @@ describe('without replacer', () => { expect(parsed).toMatchInlineSnapshot(`"0 0 0 var(--tw-shadow-color, var(--my-color))"`) }) + it('should handle var color with exotic zero offsets (1)', () => { + let parsed = replaceShadowColors('-0 0e9 0.00 var(--my-color)', replacer) + expect(parsed).toMatchInlineSnapshot(`"-0 0e9 0.00 var(--tw-shadow-color, var(--my-color))"`) + }) + + it('should handle var color with exotic zero offsets (2)', () => { + let parsed = replaceShadowColors('-0.00 0e-20 0.04px var(--my-color)', replacer) + expect(parsed).toMatchInlineSnapshot(`"-0.00 0e-20 0.04px var(--tw-shadow-color, var(--my-color))"`) + }) + + it('should handle zeros in colors (1)', () => { + let parsed = replaceShadowColors('1px 2px #000', replacer) + expect(parsed).toMatchInlineSnapshot(`"1px 2px var(--tw-shadow-color, #000)"`) + }) + + it('should handle zeros in colors (2)', () => { + let parsed = replaceShadowColors('1px 2px rgb(0 0 0)', replacer) + expect(parsed).toMatchInlineSnapshot(`"1px 2px var(--tw-shadow-color, rgb(0 0 0))"`) + }) + it('should handle two values with currentcolor', () => { let parsed = replaceShadowColors('1px 2px', replacer) expect(parsed).toMatchInlineSnapshot(`"1px 2px var(--tw-shadow-color, currentcolor)"`) @@ -49,6 +69,11 @@ describe('without replacer', () => { `"var(--my-shadow), 1px 1px var(--tw-shadow-color, var(--my-color)), 0 0 1px var(--tw-shadow-color, var(--my-color))"`, ) }) + + it('should handle calc()', () => { + let parsed = replaceShadowColors('0 0 calc(1 * var(--spacing)) black', replacer) + expect(parsed).toMatchInlineSnapshot(`"0 0 calc(1 * var(--spacing)) var(--tw-shadow-color, black)"`) + }) }) describe('with replacer', () => { diff --git a/packages/tailwindcss/src/utils/replace-shadow-colors.ts b/packages/tailwindcss/src/utils/replace-shadow-colors.ts index e51f9c2a0536..4cb10d096754 100644 --- a/packages/tailwindcss/src/utils/replace-shadow-colors.ts +++ b/packages/tailwindcss/src/utils/replace-shadow-colors.ts @@ -1,7 +1,8 @@ +import { isLength } from './infer-data-type' import { segment } from './segment' const KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset']) -const LENGTH = /^-?(\d+|\.\d+)(.*?)$/g +const IS_ZERO = /^-?0*\.?0+(?:[eE][+-]?\d+)?$/ export function replaceShadowColors(input: string, replacement: (color: string) => string) { let shadows = segment(input, ',').map((shadow) => { @@ -14,15 +15,12 @@ export function replaceShadowColors(input: string, replacement: (color: string) for (let part of parts) { if (KEYWORDS.has(part)) { continue - } else if (LENGTH.test(part)) { + } else if (isLength(part) || IS_ZERO.test(part)) { if (offsetX === null) { offsetX = part } else if (offsetY === null) { offsetY = part } - - // Reset index, since the regex is stateful. - LENGTH.lastIndex = 0 } else if (color === null) { color = part }