diff --git a/packages/tailwindcss/src/canonicalize-candidates.test.ts b/packages/tailwindcss/src/canonicalize-candidates.test.ts index 811de77f5dc0..0ee92a7d15b2 100644 --- a/packages/tailwindcss/src/canonicalize-candidates.test.ts +++ b/packages/tailwindcss/src/canonicalize-candidates.test.ts @@ -254,6 +254,10 @@ describe.each([['default'], ['with-variant'], ['important'], ['prefix']])('%s', ['[color:var(--color-red-500)]/[25%]', 'text-red-500/25'], ['[color:var(--color-red-500)]/[100%]', 'text-red-500'], ['[color:var(--color-red-500)]/100', 'text-red-500'], + ['[color:var(--color-red-500)]/[10%]', 'text-red-500/10'], + ['[color:var(--color-red-500)]/[10.0%]', 'text-red-500/10'], + ['[color:var(--color-red-500)]/[.1]', 'text-red-500/10'], + ['[color:var(--color-red-500)]/[.10]', 'text-red-500/10'], // No need for `/50` because that's already encoded in the `--color-primary` // value ['[color:oklch(62.3%_0.214_259.815)]/50', 'text-primary'], diff --git a/packages/tailwindcss/src/canonicalize-candidates.ts b/packages/tailwindcss/src/canonicalize-candidates.ts index ec07b9511f14..8f3312194bbf 100644 --- a/packages/tailwindcss/src/canonicalize-candidates.ts +++ b/packages/tailwindcss/src/canonicalize-candidates.ts @@ -1469,7 +1469,11 @@ function optimizeModifier(designSystem: DesignSystem, candidate: Candidate): Can { let newModifier: NamedUtilityValue = { kind: 'named', - value: modifier.value.endsWith('%') ? modifier.value.slice(0, -1) : modifier.value, + value: modifier.value.endsWith('%') + ? modifier.value.includes('.') + ? `${Number(modifier.value.slice(0, -1))}` + : modifier.value.slice(0, -1) + : modifier.value, fraction: null, } diff --git a/packages/tailwindcss/src/signatures.ts b/packages/tailwindcss/src/signatures.ts index 89a5edde424c..80d8f2948206 100644 --- a/packages/tailwindcss/src/signatures.ts +++ b/packages/tailwindcss/src/signatures.ts @@ -9,6 +9,8 @@ import { dimensions } from './utils/dimensions' import { isValidSpacingMultiplier } from './utils/infer-data-type' import * as ValueParser from './value-parser' +const FLOATING_POINT_PERCENTAGE = /\d*\.\d+(?:[eE][+-]?\d+)?%/g + // Given a utility, compute a signature that represents the utility. The // signature will be a normalised form of the generated CSS for the utility, or // a unique symbol if the utility is not valid. The class in the selector will @@ -59,6 +61,17 @@ export const computeUtilitySignature = new DefaultMap< if (node.value === undefined || node.property === '--tw-sort') { replaceWith([]) } + + // Normalize percentages by removing unnecessary dots and zeros. + // + // E.g.: `50.0%` → `50%` + else if (node.value.includes('%')) { + FLOATING_POINT_PERCENTAGE.lastIndex = 0 + node.value = node.value.replaceAll( + FLOATING_POINT_PERCENTAGE, + (match) => `${Number(match.slice(0, -1))}%`, + ) + } } // Replace special nodes with its children