Fix canonicalization bug where we end up with a high precision number (#20221)

This PR fixes a bug where we suggest a canonicalization with a high
precision number.

E.g.:

`w-[calc(100%/3.5)]` → `w-[28.571428571428573%]`

While this is technically correct, it's also not as user friendly. This
PR solves this issue by checking whether the result has a precision of
`.xx` at most. If that's not the case, then we keep the original
expression.

Fixes:
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1591

## Test plan

1. Added an a regression test for this situation
2. Existing tests pass
This commit is contained in:
Robin Malfait 2026-06-12 00:23:11 +02:00 • committed by GitHub
parent 97a5b3abfb
commit 12833aa4b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 32 additions and 1 deletions

View file

@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure transitions between `inset-shadow-none` and other inset shadows work correctly ([#20208](https://github.com/tailwindlabs/tailwindcss/pull/20208))
- Ensure explicitly referenced `@source` directories are scanned even when ignored by git ([#20214](https://github.com/tailwindlabs/tailwindcss/pull/20214))
- Ensure `@source` globs ending in `**/*` preserve dynamic path segments to avoid scanning too many files ([#20217](https://github.com/tailwindlabs/tailwindcss/pull/20217))
- Canonicalization: don't fold `calc(…)` divisions when the result would require high precision (e.g. `w-[calc(100%/3.5)]` → `w-[calc(100%/3.5)]`, not `w-[28.571428571428573%]`) ([#20221](https://github.com/tailwindlabs/tailwindcss/pull/20221))
### Changed

View file

@ -1244,6 +1244,17 @@ describe.each([['default'], ['with-variant'], ['important'], ['prefix']])('%s',
await expectCanonicalization(input, candidate, expected)
})
// https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1591
test.each([
['w-[calc(100%/3.5)]', 'w-[calc(100%/3.5)]'], // Not w-[28.571428571428573%]
])(testName, { timeout }, async (candidate, expected) => {
let input = css`
@import 'tailwindcss';
`
await expectCanonicalization(input, candidate, expected)
})
})
describe('theme to var', () => {

View file

@ -127,3 +127,11 @@ it.each([
it('should not constant fold when dividing by `0`', () => {
expect(constantFoldDeclaration('calc(123rem / 0)')).toBe('calc(123rem / 0)')
})
it('should not constant fold when a computation has a high-precision result', () => {
expect(constantFoldDeclaration('calc(100% / 3.5)')).toBe('calc(100% / 3.5)')
})
it('should constant fold division results with floating-point error after scaling', () => {
expect(constantFoldDeclaration('calc(29% / 100)')).toBe('0.29%')
})

View file

@ -286,8 +286,19 @@ export function constantFoldDeclarationAst(
((lhs[1] === null && rhs[1] === null) || // Unitless / Unitless, e.g.: `8 / 2`
(lhs[1] !== null && rhs[1] === null)) // Unit / Unitless, e.g.: `1rem / 2`
) {
let computed = lhs[0] / rhs[0]
// Only fold with .xx precision, anything beyond that might be a bit too much.
//
// E.g. 100% / 3.5 = 28.571428571428573%, which is correct but not
// as user friendly. Especially when going from
// `w-[calc(100%/3.5)]` → `w-[28.571428571428573%]`
if (Math.round(computed * 100) / 100 !== computed) {
break
}
folded = true
return WalkAction.ReplaceSkip(ValueParser.word(`${lhs[0] / rhs[0]}${lhs[1] ?? ''}`))
return WalkAction.ReplaceSkip(ValueParser.word(`${computed}${lhs[1] ?? ''}`))
}
break
}