Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Reuse the same sorting as the breakpoint variants
  • Loading branch information
philipp-spiess committed Nov 13, 2024
commit 6fdb27a5f6bcf22be733c148c606ff9bd7d1089e
66 changes: 66 additions & 0 deletions packages/tailwindcss/src/utilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3217,6 +3217,72 @@ describe('container', () => {
`)
})

test('sorts breakpoints based on unit and then in ascending aOrder', async () => {
expect(
await compileCss(
css`
@theme reference {
--breakpoint-lg: 64rem;
--breakpoint-xl: 80rem;
--breakpoint-3xl: 1600px;
--breakpoint-sm: 40em;
--breakpoint-2xl: 96rem;
--breakpoint-xs: 30px;
--breakpoint-md: 48em;
}
@tailwind utilities;
`,
['container'],
),
).toMatchInlineSnapshot(`
".container {
width: 100%;
}

@media (width >= 40em) {
.container {
max-width: 40em;
}
}

@media (width >= 48em) {
.container {
max-width: 48em;
}
}

@media (width >= 30px) {
.container {
max-width: 30px;
}
}

@media (width >= 1600px) {
.container {
max-width: 1600px;
}
}

@media (width >= 64rem) {
.container {
max-width: 64rem;
}
}

@media (width >= 80rem) {
.container {
max-width: 80rem;
}
}

@media (width >= 96rem) {
.container {
max-width: 96rem;
}
}"
`)
})

test('custom `@utility container` always follow the core utility ', async () => {
expect(
await compileCss(
Expand Down
3 changes: 2 additions & 1 deletion packages/tailwindcss/src/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { atRoot, atRule, decl, styleRule, type AstNode } from './ast'
import type { Candidate, CandidateModifier, NamedUtilityValue } from './candidate'
import type { Theme, ThemeKey } from './theme'
import { compareBreakpoints } from './utils/compare-breakpoints'
import { DefaultMap } from './utils/default-map'
import {
inferDataType,
Expand Down Expand Up @@ -899,7 +900,7 @@ export function createUtilities(theme: Theme) {

utilities.static('container', () => {
let breakpoints = [...theme.namespace('--breakpoint').values()]
breakpoints.sort((a, b) => parseInt(a) - parseInt(b))
breakpoints.sort((a, z) => compareBreakpoints(a, z, 'asc'))

let decls: AstNode[] = [decl('--tw-sort', '--tw-container-component'), decl('width', '100%')]
for (let breakpoint of breakpoints) {
Expand Down
46 changes: 46 additions & 0 deletions packages/tailwindcss/src/utils/compare-breakpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export function compareBreakpoints(a: string, z: string, direction: 'asc' | 'desc') {
// Assumption: when a `(` exists, we are dealing with a CSS function.
//
// E.g.: `calc(100% - 1rem)`
let aIsCssFunction = a.indexOf('(')
let zIsCssFunction = z.indexOf('(')

let aBucket =
aIsCssFunction === -1
? // No CSS function found, bucket by unit instead
a.replace(/[\d.]+/g, '')
: // CSS function found, bucket by function name
a.slice(0, aIsCssFunction)

let zBucket =
zIsCssFunction === -1
? // No CSS function found, bucket by unit
z.replace(/[\d.]+/g, '')
: // CSS function found, bucket by function name
z.slice(0, zIsCssFunction)

let order =
// 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))

// 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
// stable by falling back to a string comparison.
//
// This can happen when using CSS functions such as `calc`.
//
// E.g.:
//
// - `min-[calc(100%-1rem)]` and `min-[calc(100%-2rem)]`
// - `@[calc(100%-1rem)]` and `@[calc(100%-2rem)]`
//
// In this scenario, we want to alphabetically sort `calc(100%-1rem)` and
// `calc(100%-2rem)` to make it deterministic.
if (Number.isNaN(order)) {
return a < z ? -1 : 1
}

return order
}
58 changes: 7 additions & 51 deletions packages/tailwindcss/src/variants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from './ast'
import { type Variant } from './candidate'
import type { Theme } from './theme'
import { compareBreakpoints } from './utils/compare-breakpoints'
import { DefaultMap } from './utils/default-map'
import { isPositiveInteger } from './utils/infer-data-type'
import { segment } from './utils/segment'
Expand Down Expand Up @@ -869,7 +870,7 @@ export function createVariants(theme: Theme): Variants {
// Helper to compare variants by their resolved values, this is used by the
// responsive variants (`sm`, `md`, ...), `min-*`, `max-*` and container
// queries (`@`).
function compareBreakpoints(
function compareBreakpointVariants(
a: Variant,
z: Variant,
direction: 'asc' | 'desc',
Expand All @@ -884,52 +885,7 @@ export function createVariants(theme: Theme): Variants {

if (aValue === zValue) return 0

// Assumption: when a `(` exists, we are dealing with a CSS function.
//
// E.g.: `calc(100% - 1rem)`
let aIsCssFunction = aValue.indexOf('(')
let zIsCssFunction = zValue.indexOf('(')

let aBucket =
aIsCssFunction === -1
? // No CSS function found, bucket by unit instead
aValue.replace(/[\d.]+/g, '')
: // CSS function found, bucket by function name
aValue.slice(0, aIsCssFunction)

let zBucket =
zIsCssFunction === -1
? // No CSS function found, bucket by unit
zValue.replace(/[\d.]+/g, '')
: // CSS function found, bucket by function name
zValue.slice(0, zIsCssFunction)

let order =
// Compare by bucket name
(aBucket === zBucket ? 0 : aBucket < zBucket ? -1 : 1) ||
// If bucket names are the same, compare by value
(direction === 'asc'
? parseInt(aValue) - parseInt(zValue)
: parseInt(zValue) - parseInt(aValue))

// 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
// stable by falling back to a string comparison.
//
// This can happen when using CSS functions such as `calc`.
//
// E.g.:
//
// - `min-[calc(100%-1rem)]` and `min-[calc(100%-2rem)]`
// - `@[calc(100%-1rem)]` and `@[calc(100%-2rem)]`
//
// In this scenario, we want to alphabetically sort `calc(100%-1rem)` and
// `calc(100%-2rem)` to make it deterministic.
if (Number.isNaN(order)) {
return aValue < zValue ? -1 : 1
}

return order
return compareBreakpoints(aValue, zValue, direction)
}

// Breakpoints
Expand Down Expand Up @@ -978,7 +934,7 @@ export function createVariants(theme: Theme): Variants {
{ compounds: Compounds.AtRules },
)
},
(a, z) => compareBreakpoints(a, z, 'desc', resolvedBreakpoints),
(a, z) => compareBreakpointVariants(a, z, 'desc', resolvedBreakpoints),
)

variants.suggest(
Expand Down Expand Up @@ -1013,7 +969,7 @@ export function createVariants(theme: Theme): Variants {
{ compounds: Compounds.AtRules },
)
},
(a, z) => compareBreakpoints(a, z, 'asc', resolvedBreakpoints),
(a, z) => compareBreakpointVariants(a, z, 'asc', resolvedBreakpoints),
)

variants.suggest(
Expand Down Expand Up @@ -1072,7 +1028,7 @@ export function createVariants(theme: Theme): Variants {
{ compounds: Compounds.AtRules },
)
},
(a, z) => compareBreakpoints(a, z, 'desc', resolvedWidths),
(a, z) => compareBreakpointVariants(a, z, 'desc', resolvedWidths),
)

variants.suggest(
Expand Down Expand Up @@ -1119,7 +1075,7 @@ export function createVariants(theme: Theme): Variants {
{ compounds: Compounds.AtRules },
)
},
(a, z) => compareBreakpoints(a, z, 'asc', resolvedWidths),
(a, z) => compareBreakpointVariants(a, z, 'asc', resolvedWidths),
)

variants.suggest(
Expand Down