Skip to content
Closed
Changes from all commits
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
44 changes: 28 additions & 16 deletions packages/tailwindcss/src/canonicalize-candidates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,13 @@ const spacing = new DefaultMap<DesignSystem, DefaultMap<string, number | null> |
})

function arbitraryUtilities(candidate: Candidate, options: SignatureOptions): Candidate {
// Add guard: Skip conversion if candidate uses CSS variables or complex expressions (var(), calc(), spaces, commas, slashes)
if (candidate.kind === 'functional' && candidate.value?.kind === 'arbitrary') {
const raw = String(candidate.value.value ?? '')
if (/var\(--[^)]+\)|calc\(|\s|,|\//.test(raw)) {
return candidate
}
}
// We are only interested in arbitrary properties and arbitrary values
if (
// Arbitrary property
Expand Down Expand Up @@ -673,6 +680,12 @@ function arbitraryUtilities(candidate: Candidate, options: SignatureOptions): Ca
let value =
candidate.kind === 'arbitrary' ? candidate.value : (candidate.value?.value ?? null)
if (value === null) return

// Always cast value to string for consistency
const valueStr = String(value)

// Detect complex values (var(), calc(), spaces, commas, slashes)
const isComplex = /var\(--[^)]+\)|calc\(|\s|,|\//.test(valueStr)

let spacingMultiplier = spacing.get(designSystem)?.get(value) ?? null
let rootPrefix = ''
Expand All @@ -689,19 +702,20 @@ function arbitraryUtilities(candidate: Candidate, options: SignatureOptions): Ca
)) {
if (rootPrefix) root = `${rootPrefix}${root}`

// Try as bare value
for (let replacementCandidate of parseCandidate(designSystem, `${root}-${value}`)) {
yield replacementCandidate
}

// Try as bare value with modifier
if (candidate.modifier) {
for (let replacementCandidate of parseCandidate(
designSystem,
`${root}-${value}${candidate.modifier}`,
)) {
// Skip bare-value attempts for complex values to prevent gap-(--gap) corruption
if (!isComplex) {
for (let replacementCandidate of parseCandidate(designSystem, `${root}-${valueStr}`)) {
yield replacementCandidate
}
// Always use printModifier() for modifiers
if (candidate.modifier) {
for (let replacementCandidate of parseCandidate(
designSystem,
`${root}-${valueStr}${printModifier(candidate.modifier)}`,
)) {
yield replacementCandidate
}
}
}

// Try bare value based on the `--spacing` value. E.g.:
Expand All @@ -726,16 +740,14 @@ function arbitraryUtilities(candidate: Candidate, options: SignatureOptions): Ca
}
}

// Try as arbitrary value
for (let replacementCandidate of parseCandidate(designSystem, `${root}-[${value}]`)) {
// Bracketed arbitrary values are always safe — keep them
for (let replacementCandidate of parseCandidate(designSystem, `${root}-[${valueStr}]`)) {
yield replacementCandidate
}

// Try as arbitrary value with modifier
if (candidate.modifier) {
for (let replacementCandidate of parseCandidate(
designSystem,
`${root}-[${value}]${printModifier(candidate.modifier)}`,
`${root}-[${valueStr}]${printModifier(candidate.modifier)}`,
)) {
yield replacementCandidate
}
Expand Down