diff --git a/src/lib/validators.ts b/src/lib/validators.ts index 93757b75..0789db12 100644 --- a/src/lib/validators.ts +++ b/src/lib/validators.ts @@ -4,6 +4,7 @@ const stringLengths = new Set(['px', 'full', 'screen']) const tshirtUnitRegex = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/ const lengthUnitRegex = /\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/ +const colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch))\(.+\)$/ // Shadow always begins with x and y offset separated by underscore const shadowRegex = /^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/ const imageRegex = @@ -84,7 +85,10 @@ function getIsArbitraryValue( } function isLengthOnly(value: string) { - return lengthUnitRegex.test(value) + // `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths. + // For example, `hsl(0 0% 0%)` would be classified as a length without this check. + // I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough. + return lengthUnitRegex.test(value) && !colorFunctionRegex.test(value) } function isNever() { diff --git a/tests/colors.test.ts b/tests/colors.test.ts index 6a259adc..1d653f14 100644 --- a/tests/colors.test.ts +++ b/tests/colors.test.ts @@ -3,4 +3,7 @@ import { twMerge } from '../src' test('handles color conflicts properly', () => { expect(twMerge('bg-grey-5 bg-hotpink')).toBe('bg-hotpink') expect(twMerge('hover:bg-grey-5 hover:bg-hotpink')).toBe('hover:bg-hotpink') + expect(twMerge('stroke-[hsl(350_80%_0%)] stroke-[10px]')).toBe( + 'stroke-[hsl(350_80%_0%)] stroke-[10px]', + ) })