Skip to content

Commit c6b5607

Browse files
committed
refactor: prev pr
1 parent 8fda1a5 commit c6b5607

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

packages/core/__tests__/conditions.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,36 @@ describe('Conditions', () => {
292292
}
293293
`)
294294
})
295+
296+
test('theme conditions sort correctly with pseudo-elements', () => {
297+
const css = new Conditions({
298+
conditions: {
299+
before: '&::before',
300+
hover: ['@media (hover: hover) and (pointer: fine)', '&:is(:hover, [data-hover])'],
301+
},
302+
themes: {
303+
primary: { tokens: { colors: { brand: { value: 'blue' } } } },
304+
},
305+
})
306+
307+
// Theme (parent-nesting) + pseudo-element: theme first, pseudo-element last
308+
const sorted = css.sort(['_before', '_themePrimary'])
309+
expect(sorted.map((c) => c.raw)).toMatchInlineSnapshot(`
310+
[
311+
"[data-panda-theme=primary] &",
312+
"&::before",
313+
]
314+
`)
315+
316+
// Theme + mixed hover + pseudo-element: at-rule first, selectors in source order, pseudo-element last
317+
const sortedAll = css.sort(['_before', '_hover', '_themePrimary'])
318+
expect(sortedAll.map((c) => c.raw)).toMatchInlineSnapshot(`
319+
[
320+
"@media (hover: hover) and (pointer: fine)",
321+
"&:is(:hover, [data-hover])",
322+
"[data-panda-theme=primary] &",
323+
"&::before",
324+
]
325+
`)
326+
})
295327
})

packages/core/src/conditions.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ import { compareAtRuleOrMixed } from './sort-style-rules'
1616
const isAtRule = (cond: ConditionDetails): boolean => cond.type === 'at-rule'
1717

1818
/**
19-
* Checks if a condition is a pseudo-element selector (::before, ::after, etc.)
19+
* Matches pseudo-element selectors (::before, ::after, ::placeholder, etc.)
2020
* Pseudo-elements must appear at the end of CSS selector chains per the CSS spec.
2121
*/
22-
const isPseudoElement = (cond: ConditionDetails): boolean => typeof cond.raw === 'string' && cond.raw.includes('::')
22+
const pseudoElementRegex = /::[\w-]/
23+
const isPseudoElement = (cond: ConditionDetails): boolean =>
24+
typeof cond.raw === 'string' && pseudoElementRegex.test(cond.raw)
2325

2426
/**
2527
* Flattens a condition, extracting parts from mixed conditions.
@@ -201,14 +203,10 @@ export class Conditions {
201203
if (aIsAtRule && !bIsAtRule) return -1
202204
if (!aIsAtRule && bIsAtRule) return 1
203205

204-
// Among non-at-rules: pseudo-elements (::before, ::after, etc.) must come last
205-
// CSS requires pseudo-elements at the end of selector chains
206-
if (!aIsAtRule && !bIsAtRule) {
207-
const aIsPseudoElement = isPseudoElement(a.cond)
208-
const bIsPseudoElement = isPseudoElement(b.cond)
209-
if (aIsPseudoElement && !bIsPseudoElement) return 1
210-
if (!aIsPseudoElement && bIsPseudoElement) return -1
211-
}
206+
// Pseudo-elements (::before, ::after, etc.) must come last per CSS spec
207+
const aIsPseudo = isPseudoElement(a.cond)
208+
const bIsPseudo = isPseudoElement(b.cond)
209+
if (aIsPseudo !== bIsPseudo) return aIsPseudo ? 1 : -1
212210

213211
// Within same category, preserve original source order
214212
return a.originalIndex - b.originalIndex

0 commit comments

Comments
 (0)