Ensure individual logical property utilities are sorted later than left/right pair utilities (#14777)

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:

<img width="1041" alt="image"
src="https://github.com/user-attachments/assets/fb330536-2131-4de8-a584-62edf380148f">

…and now with the change in this PR, `ps-2` also overrides `px-6` as
you'd expect:

<img width="1043" alt="image"
src="https://github.com/user-attachments/assets/c6799416-9c80-4fd5-bce4-ea1e4da53968">

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
This commit is contained in:
Adam Wathan 2024-10-24 10:58:41 -04:00 committed by GitHub
parent 3f2ff03fc0
commit d643d79f4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 137 additions and 13 deletions

View File

@ -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

View File

@ -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(

View File

@ -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',

View File

@ -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',
],
]

View File

@ -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', {