diff --git a/CHANGELOG.md b/CHANGELOG.md index 338a78eb9..c2cbe5a7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Only normalize top-level `and`, `or`, and `not` keywords in `supports-[…]` variants (e.g. `selector(a: not (.foo))` → `selector(a:not(.foo))`) ([#20420](https://github.com/tailwindlabs/tailwindcss/pull/20420)) - Don't warn about Angular's `::ng-deep` and `:host-context()` when optimizing CSS ([#20434](https://github.com/tailwindlabs/tailwindcss/pull/20434)) - Don't generate CSS for candidates containing an empty additional modifier (e.g. `bg-red-500/50/` and `group-hover/foo//bar:flex`) ([#20466](https://github.com/tailwindlabs/tailwindcss/pull/20466)) +- Sort `min-*`, `max-*`, and container query variants with decimal values numerically (e.g. `min-[40.25rem]` before `min-[40.5rem]`) ([#20512](https://github.com/tailwindlabs/tailwindcss/pull/20512)) ## [4.3.3] - 2026-07-16 diff --git a/packages/tailwindcss/src/utils/compare-breakpoints.ts b/packages/tailwindcss/src/utils/compare-breakpoints.ts index 08e97998e..4eb562764 100644 --- a/packages/tailwindcss/src/utils/compare-breakpoints.ts +++ b/packages/tailwindcss/src/utils/compare-breakpoints.ts @@ -25,7 +25,7 @@ export function compareBreakpoints(a: string, z: string, direction: 'asc' | 'des // Compare by bucket name (aBucket === zBucket ? 0 : aBucket < zBucket ? -1 : 1) || // If bucket names are the same, compare by value - (direction === 'asc' ? parseInt(a) - parseInt(z) : parseInt(z) - parseInt(a)) + (direction === 'asc' ? parseFloat(a) - parseFloat(z) : parseFloat(z) - parseFloat(a)) // If the groups are the same, and the contents are not numbers, the // `order` will result in `NaN`. In this case, we want to make sorting diff --git a/packages/tailwindcss/src/variants.test.ts b/packages/tailwindcss/src/variants.test.ts index 454f4c1bf..2304326fc 100644 --- a/packages/tailwindcss/src/variants.test.ts +++ b/packages/tailwindcss/src/variants.test.ts @@ -1424,6 +1424,80 @@ test('sorting `min` and `max` should sort by unit, then by value, then alphabeti `) }) +test('sorting `min` and `max` should compare decimal values numerically', async () => { + expect( + await run([ + 'min-[40.5rem]:flex', + 'min-[40.25rem]:flex', + 'max-[40.25rem]:flex', + 'max-[40.5rem]:flex', + ]), + ).toMatchInlineSnapshot(` + " + @media not all and (min-width: 40.5rem) { + .max-\\[40\\.5rem\\]\\:flex { + display: flex; + } + } + + @media not all and (min-width: 40.25rem) { + .max-\\[40\\.25rem\\]\\:flex { + display: flex; + } + } + + @media (min-width: 40.25rem) { + .min-\\[40\\.25rem\\]\\:flex { + display: flex; + } + } + + @media (min-width: 40.5rem) { + .min-\\[40\\.5rem\\]\\:flex { + display: flex; + } + } + " + `) +}) + +test('sorting `@min` and `@max` should compare decimal values numerically', async () => { + expect( + await run([ + '@min-[40.5rem]:flex', + '@min-[40.25rem]:flex', + '@max-[40.25rem]:flex', + '@max-[40.5rem]:flex', + ]), + ).toMatchInlineSnapshot(` + " + @container not (min-width: 40.5rem) { + .\\@max-\\[40\\.5rem\\]\\:flex { + display: flex; + } + } + + @container not (min-width: 40.25rem) { + .\\@max-\\[40\\.25rem\\]\\:flex { + display: flex; + } + } + + @container (min-width: 40.25rem) { + .\\@min-\\[40\\.25rem\\]\\:flex { + display: flex; + } + } + + @container (min-width: 40.5rem) { + .\\@min-\\[40\\.5rem\\]\\:flex { + display: flex; + } + } + " + `) +}) + test('supports', async () => { expect( await run([