From d643d79f4b01fe83dfbdddf3fb8c00c4f0cecefa Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Thu, 24 Oct 2024 10:58:41 -0400 Subject: [PATCH] Ensure individual logical property utilities are sorted later than left/right pair utilities (#14777) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #14772. This PR fixes an issue where utilities like `ps-2` were sorted earlier in the generated CSS than `px-3`, causing `ps-2` to not override `px-3` as expected. This happened because `px-3` uses `padding-left` and `padding-right`, and `ps-2` uses `padding-inline-start`, and in `property-order.ts` we sort those properties as follows: ```js ... 'padding', 'padding-inline', 'padding-block', 'padding-inline-start', 'padding-inline-end', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', ... ``` Since `padding-left` and `padding-right` both appear later than `padding-inline-start`, the `px-3` utility is sorted later in the CSS since all of its properties are later in the sort order than all of properties in `ps-2`. To fix this, I'm using our internal `--tw-sort` property to tell Tailwind to sort the `px-3` utility as if it used `padding-inline` to ensure that it's sorted earlier in the CSS. This PR applies this same fix for the `padding` utilities, `scroll-margin` utilities, and `scroll-padding` utilities. No changes have been made to the `margin` utilities because we were already handling this correctly there. Here you can see that `pl-2` overrides `px-6` as you'd expect: image …and now with the change in this PR, `ps-2` also overrides `px-6` as you'd expect: image --------- Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com> --- CHANGELOG.md | 4 +- packages/tailwindcss/src/index.test.ts | 94 ++++++++++++++++++++++ packages/tailwindcss/src/property-order.ts | 4 + packages/tailwindcss/src/sort.test.ts | 12 +-- packages/tailwindcss/src/utilities.ts | 36 +++++++-- 5 files changed, 137 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 872da98b6..19dc2a86c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Ensure individual logical property utilities are sorted later than left/right pair utilities ([#14777](https://github.com/tailwindlabs/tailwindcss/pull/14777)) ## [4.0.0-alpha.29] - 2024-10-23 diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index 5c1f6286c..ccae67e19 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -674,6 +674,100 @@ describe('sorting', () => { `) }) + it('should sort individual logical properties later than left/right pairs', async () => { + expect( + await compileCss( + css` + @theme { + --spacing-1: 1px; + --spacing-2: 2px; + --spacing-3: 3px; + } + @tailwind utilities; + `, + [ + // scroll-margin + 'scroll-ms-1', + 'scroll-me-2', + 'scroll-mx-3', + + // scroll-padding + 'scroll-ps-1', + 'scroll-pe-2', + 'scroll-px-3', + + // margin + 'ms-1', + 'me-2', + 'mx-3', + + // padding + 'ps-1', + 'pe-2', + 'px-3', + ].sort(() => Math.random() - 0.5), + ), + ).toMatchInlineSnapshot(` + ":root { + --spacing-1: 1px; + --spacing-2: 2px; + --spacing-3: 3px; + } + + .mx-3 { + margin-left: var(--spacing-3, 3px); + margin-right: var(--spacing-3, 3px); + } + + .ms-1 { + margin-inline-start: var(--spacing-1, 1px); + } + + .me-2 { + margin-inline-end: var(--spacing-2, 2px); + } + + .scroll-mx-3 { + scroll-margin-left: var(--spacing-3, 3px); + scroll-margin-right: var(--spacing-3, 3px); + } + + .scroll-ms-1 { + scroll-margin-inline-start: var(--spacing-1, 1px); + } + + .scroll-me-2 { + scroll-margin-inline-end: var(--spacing-2, 2px); + } + + .scroll-px-3 { + scroll-padding-left: var(--spacing-3, 3px); + scroll-padding-right: var(--spacing-3, 3px); + } + + .scroll-ps-1 { + scroll-padding-inline-start: var(--spacing-1, 1px); + } + + .scroll-pe-2 { + scroll-padding-inline-end: var(--spacing-2, 2px); + } + + .px-3 { + padding-left: var(--spacing-3, 3px); + padding-right: var(--spacing-3, 3px); + } + + .ps-1 { + padding-inline-start: var(--spacing-1, 1px); + } + + .pe-2 { + padding-inline-end: var(--spacing-2, 2px); + }" + `) + }) + it('should move variants to the end while sorting', async () => { expect( await run( diff --git a/packages/tailwindcss/src/property-order.ts b/packages/tailwindcss/src/property-order.ts index d448ec05b..0305f173c 100644 --- a/packages/tailwindcss/src/property-order.ts +++ b/packages/tailwindcss/src/property-order.ts @@ -99,6 +99,8 @@ export default [ 'scroll-snap-align', 'scroll-snap-stop', 'scroll-margin', + 'scroll-margin-inline', + 'scroll-margin-block', 'scroll-margin-inline-start', 'scroll-margin-inline-end', 'scroll-margin-top', @@ -107,6 +109,8 @@ export default [ 'scroll-margin-left', 'scroll-padding', + 'scroll-padding-inline', + 'scroll-padding-block', 'scroll-padding-inline-start', 'scroll-padding-inline-end', 'scroll-padding-top', diff --git a/packages/tailwindcss/src/sort.test.ts b/packages/tailwindcss/src/sort.test.ts index 992c1d141..6ced80929 100644 --- a/packages/tailwindcss/src/sort.test.ts +++ b/packages/tailwindcss/src/sort.test.ts @@ -16,14 +16,14 @@ function loadDesign() { const table = [ // Utilities - ['px-3 p-1 py-3', 'p-1 py-3 px-3'], + ['py-3 p-1 px-3', 'p-1 px-3 py-3'], // Utilities with variants - ['px-3 focus:hover:p-3 hover:p-1 py-3', 'py-3 px-3 hover:p-1 focus:hover:p-3'], + ['px-3 focus:hover:p-3 hover:p-1 py-3', 'px-3 py-3 hover:p-1 focus:hover:p-3'], // Utilities with important - ['px-3 py-4! p-1', 'p-1 py-4! px-3'], - ['py-4! px-3 p-1', 'p-1 py-4! px-3'], + ['px-3 py-4! p-1', 'p-1 px-3 py-4!'], + ['py-4! px-3 p-1', 'p-1 px-3 py-4!'], // User CSS order is the same and moved to the front ['b p-1 a', 'b a p-1'], @@ -48,11 +48,11 @@ test('can sort classes deterministically across multiple class lists', async () let classes = [ [ 'a-class px-3 p-1 b-class py-3 bg-red-500 bg-blue-500', - 'a-class b-class bg-blue-500 bg-red-500 p-1 py-3 px-3', + 'a-class b-class bg-blue-500 bg-red-500 p-1 px-3 py-3', ], [ 'px-3 b-class p-1 py-3 bg-blue-500 a-class bg-red-500', - 'b-class a-class bg-blue-500 bg-red-500 p-1 py-3 px-3', + 'b-class a-class bg-blue-500 bg-red-500 p-1 px-3 py-3', ], ] diff --git a/packages/tailwindcss/src/utilities.ts b/packages/tailwindcss/src/utilities.ts index 5d46c7f32..b0ab8519f 100644 --- a/packages/tailwindcss/src/utilities.ts +++ b/packages/tailwindcss/src/utilities.ts @@ -1743,13 +1743,21 @@ export function createUtilities(theme: Theme) { functionalUtility('scroll-mx', { supportsNegative: true, themeKeys: ['--scroll-margin', '--spacing'], - handle: (value) => [decl('scroll-margin-left', value), decl('scroll-margin-right', value)], + handle: (value) => [ + decl('--tw-sort', 'scroll-margin-inline'), + decl('scroll-margin-left', value), + decl('scroll-margin-right', value), + ], }) functionalUtility('scroll-my', { supportsNegative: true, themeKeys: ['--scroll-margin', '--spacing'], - handle: (value) => [decl('scroll-margin-top', value), decl('scroll-margin-bottom', value)], + handle: (value) => [ + decl('--tw-sort', 'scroll-margin-block'), + decl('scroll-margin-top', value), + decl('scroll-margin-bottom', value), + ], }) functionalUtility('scroll-ms', { @@ -1798,13 +1806,21 @@ export function createUtilities(theme: Theme) { functionalUtility('scroll-px', { supportsNegative: true, themeKeys: ['--scroll-padding', '--spacing'], - handle: (value) => [decl('scroll-padding-left', value), decl('scroll-padding-right', value)], + handle: (value) => [ + decl('--tw-sort', 'scroll-padding-inline'), + decl('scroll-padding-left', value), + decl('scroll-padding-right', value), + ], }) functionalUtility('scroll-py', { supportsNegative: true, themeKeys: ['--scroll-padding', '--spacing'], - handle: (value) => [decl('scroll-padding-top', value), decl('scroll-padding-bottom', value)], + handle: (value) => [ + decl('--tw-sort', 'scroll-padding-block'), + decl('scroll-padding-top', value), + decl('scroll-padding-bottom', value), + ], }) functionalUtility('scroll-ps', { @@ -2960,12 +2976,20 @@ export function createUtilities(theme: Theme) { functionalUtility('px', { themeKeys: ['--padding', '--spacing'], - handle: (value) => [decl('padding-left', value), decl('padding-right', value)], + handle: (value) => [ + decl('--tw-sort', 'padding-inline'), + decl('padding-left', value), + decl('padding-right', value), + ], }) functionalUtility('py', { themeKeys: ['--padding', '--spacing'], - handle: (value) => [decl('padding-top', value), decl('padding-bottom', value)], + handle: (value) => [ + decl('--tw-sort', 'padding-block'), + decl('padding-top', value), + decl('padding-bottom', value), + ], }) functionalUtility('pt', {