From d03edefd19b5500d3d83af37c3e80c9bfff4b2d9 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 20 May 2026 15:54:07 +0200 Subject: [PATCH] Fix parsing bug in SelectorParser (#20090) While working on another feature, I noticed that a selector such as `.foo::before` was parsed as: ```ts [ { kind: 'compound', nodes: [ { kind: 'selector', value: '.foo' }, { kind: 'selector', value: ':' }, { kind: 'selector', value: '::before' }, ], }, ] ``` Instead of: ```ts [ { kind: 'compound', nodes: [ { kind: 'selector', value: '.foo' }, { kind: 'selector', value: '::before' }, ], }, ] ``` So far this hasn't been a real issue in practice, but it is in a follow-up PR that I'm working on. To keep things separated, I wanted to fix this behavior in a dedicated PR instead. ## Test plan 1. Added a test case for this situation 2. Other tests still pass --- packages/tailwindcss/src/selector-parser.test.ts | 12 ++++++++++++ packages/tailwindcss/src/selector-parser.ts | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/packages/tailwindcss/src/selector-parser.test.ts b/packages/tailwindcss/src/selector-parser.test.ts index e0aacdbd1..e66abd90b 100644 --- a/packages/tailwindcss/src/selector-parser.test.ts +++ b/packages/tailwindcss/src/selector-parser.test.ts @@ -21,6 +21,18 @@ describe('parse', () => { ]) }) + it('should parse a pseudo-element selector with double ::', () => { + expect(parse('.foo::before')).toEqual([ + { + kind: 'compound', + nodes: [ + { kind: 'selector', value: '.foo' }, + { kind: 'selector', value: '::before' }, + ], + }, + ]) + }) + it('should parse a selector list', () => { expect(parse('.foo,.bar')).toEqual([ { diff --git a/packages/tailwindcss/src/selector-parser.ts b/packages/tailwindcss/src/selector-parser.ts index c55b58bb6..fcc78b2e9 100644 --- a/packages/tailwindcss/src/selector-parser.ts +++ b/packages/tailwindcss/src/selector-parser.ts @@ -387,6 +387,11 @@ export function parse(input: string) { case FULL_STOP: case COLON: case NUMBER_SIGN: { + if (currentChar === COLON && buffer === ':') { + buffer += input[i] + break + } + // Handle everything before the combinator as a selector and // start a new selector if (buffer.length > 0) {