Skip to content

Commit 8fc1bb8

Browse files
committed
Don't treat \ as an escape inside CSS comments
Per the CSS Syntax spec, a comment ends at the first `*/`; escapes are not processed inside comments. The parser skipped the character after a `\`, so a comment like `/* C:\temp\*/` was never closed and swallowed (or corrupted) the CSS that followed it.
1 parent 41d9cae commit 8fc1bb8

3 files changed

Lines changed: 45 additions & 13 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3333
- Only normalize top-level `and`, `or`, and `not` keywords in `supports-[…]` variants (e.g. `selector(a: not (.foo))` → `selector(a:not(.foo))`) ([#20420](https://github.com/tailwindlabs/tailwindcss/pull/20420))
3434
- Don't warn about Angular's `::ng-deep` and `:host-context()` when optimizing CSS ([#20434](https://github.com/tailwindlabs/tailwindcss/pull/20434))
3535
- Don't generate CSS for candidates containing an empty additional modifier (e.g. `bg-red-500/50/` and `group-hover/foo//bar:flex`) ([#20466](https://github.com/tailwindlabs/tailwindcss/pull/20466))
36+
- Ensure CSS comments ending with `\*/` are closed correctly instead of swallowing the CSS that follows (e.g. `/* C:\temp\*/`)
3637

3738
## [4.3.3] - 2026-07-16
3839

‎packages/tailwindcss/src/css-parser.test.ts‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ describe.each(['Unix', 'Windows'])('Line endings: %s', (lineEndings) => {
3131
).toEqual([])
3232
})
3333

34+
it('should end a comment at `*/` even when it is preceded by a `\\`', () => {
35+
expect(
36+
parse(css`
37+
/* C:\temp\*/
38+
.foo {
39+
color: red;
40+
}
41+
`),
42+
).toEqual([
43+
{
44+
kind: 'rule',
45+
selector: '.foo',
46+
nodes: [{ kind: 'declaration', property: 'color', value: 'red', important: false }],
47+
},
48+
])
49+
})
50+
3451
it('should parse a comment inside of a selector and ignore it', () => {
3552
expect(
3653
parse(css`
@@ -448,6 +465,28 @@ describe.each(['Unix', 'Windows'])('Line endings: %s', (lineEndings) => {
448465
])
449466
})
450467

468+
it('should end a comment in a custom property at `*/` even when it is preceded by a `\\`', () => {
469+
expect(
470+
parse(css`
471+
--foo: /* C:\temp\*/ bar;
472+
--bar: /* baz */ qux;
473+
`),
474+
).toEqual([
475+
{
476+
kind: 'declaration',
477+
property: '--foo',
478+
value: '/* C:\\temp\\*/ bar',
479+
important: false,
480+
},
481+
{
482+
kind: 'declaration',
483+
property: '--bar',
484+
value: '/* baz */ qux',
485+
important: false,
486+
},
487+
])
488+
})
489+
451490
it('should parse empty custom properties', () => {
452491
expect(
453492
parse(css`

‎packages/tailwindcss/src/css-parser.ts‎

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,9 @@ export function parse(input: string, opts?: ParseOptions) {
134134
for (let j = i + 2; j < input.length; j++) {
135135
peekChar = input.charCodeAt(j)
136136

137-
// Current character is a `\` therefore the next character is escaped.
138-
if (peekChar === BACKSLASH) {
139-
j += 1
140-
}
141-
142-
// End of the comment
143-
else if (peekChar === ASTERISK && input.charCodeAt(j + 1) === SLASH) {
137+
// End of the comment. Escapes are not processed inside of comments,
138+
// so a `\` right before the closing `*/` does not escape it.
139+
if (peekChar === ASTERISK && input.charCodeAt(j + 1) === SLASH) {
144140
i = j + 1
145141
break
146142
}
@@ -224,13 +220,9 @@ export function parse(input: string, opts?: ParseOptions) {
224220
else if (peekChar === SLASH && input.charCodeAt(j + 1) === ASTERISK) {
225221
for (let k = j + 2; k < input.length; k++) {
226222
peekChar = input.charCodeAt(k)
227-
// Current character is a `\` therefore the next character is escaped.
228-
if (peekChar === BACKSLASH) {
229-
k += 1
230-
}
231223

232-
// End of the comment
233-
else if (peekChar === ASTERISK && input.charCodeAt(k + 1) === SLASH) {
224+
// End of the comment. Escapes are not processed inside of comments.
225+
if (peekChar === ASTERISK && input.charCodeAt(k + 1) === SLASH) {
234226
j = k + 1
235227
break
236228
}

0 commit comments

Comments
 (0)