Skip to content

Decode single-digit hexadecimal CSS escapes - #20591

Merged
RobinMalfait merged 1 commit into
tailwindlabs:mainfrom
jiwoo1222:fix/single-digit-css-escapes
Oct 19, 2026
Merged

RobinMalfait merged 1 commit into
tailwindlabs:mainfrom
jiwoo1222:fix/single-digit-css-escapes

Conversation

@jiwoo1222

@jiwoo1222 jiwoo1222 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

unescape treats every two-character match as a simple character escape, including a backslash followed by a single hexadecimal digit. For example, \a becomes the letter a instead of U+000A, and \0 becomes 0 instead of U+FFFD.

Distinguish hexadecimal escapes from simple escapes using separate capture groups. This preserves the existing handling of one-to-six hex digits, optional trailing whitespace, and invalid code points.

This also fixes theme variable normalization: @theme { --color-\0: red; } currently registers --color-0, while the CSS reference var(--color-\0) refers to a different identifier. Add a compilation regression test alongside the helper tests.

Reference: CSS Syntax — consume an escaped code point.

Test plan

  • With the original implementation, the nine new single-digit helper cases and the new compilation case fail.
  • pnpm exec vitest run --project tailwindcss packages/tailwindcss/src/utils/escape.test.ts — 19 passed.
  • pnpm exec vitest run --project tailwindcss packages/tailwindcss/src/index.test.ts -t 'single-digit hexadecimal' — 1 passed.
  • Prettier check on all three changed files — passed.
  • pnpm exec vitest run --project tailwindcss — 5,158 passed, 3 failed, 1 skipped. The same three snapshot failures were verified on unmodified upstream f7f47f8: CSS mixins, rounded utilities, and theme string indexing.
  • Full Rust/build verification has not been run; the partial workspace typecheck limitation noted during setup remains.

@jiwoo1222
jiwoo1222 marked this pull request as ready for review October 19, 2026 01:24
@jiwoo1222
jiwoo1222 requested a review from a team as a code owner October 19, 2026 01:24
@coderabbitai

coderabbitai Bot commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 803152b6-6a56-4de7-8069-c199b8095075
📥 Commits

Reviewing files that changed from the base of the PR and between f7f47f8 and e34b397.

📒 Files selected for processing: 3
  • packages/tailwindcss/src/index.test.ts
  • packages/tailwindcss/src/utils/escape.test.ts
  • packages/tailwindcss/src/utils/escape.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

Walkthrough

unescape now parses hexadecimal digits separately from escaped characters. New tests cover single-digit hexadecimal escapes and other escape cases. A compilation regression test checks that a theme variable with a single-digit hexadecimal escape is emitted with the normalized --color-\uFFFD name.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to e34b3

This change corrects single-digit hexadecimal escapes in CSS names, with no additional escape-handling regression established. No concrete merge-blocking risk remains.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: decoding single-digit hexadecimal CSS escapes.
Description check Passed The description explains the unescape fix, the theme variable normalization regression test, and the verification results. It is directly related to the changeset.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Reviews (1) · Last reviewed commit: "Decode single-digit hexadecimal CSS esca..." · Reviewed by Greptile

@RobinMalfait

Copy link
Copy Markdown
Contributor

Hey! Thanks for the PR.

Before we look at this further: is there a real problem behind it that you run into? For example a project where a theme variable, an @utility name or a plugin config key uses an escape like \a or \0, and the output is wrong because of it.

If so, can you share the code or the setup (a small reproduction is perfect)? That helps us judge whether the change is worth it, and makes sure that we test the case that matters to you.

Sent by an AI assistant on behalf of @RobinMalfait.

@jiwoo1222

Copy link
Copy Markdown
Contributor Author

Thanks for taking a look! I don't have a real project where I've run into this. I found it while reading the escape decoder, so this is a synthetic reproduction rather than a production bug report.

The compilation test in the PR uses this input:

@theme {
  --color-\0: red;
}
@tailwind utilities;

and builds the candidate bg-(--color-\0) with the core compile API:

const compiler = await compile(String.raw`
  @theme { --color-\0: red; }
  @tailwind utilities;
`)

const output = compiler.build([String.raw`bg-(--color-\0)`])

With the original decoder at f7f47f8, the output is:

:root, :host {
  --color-0: red;
}
.bg-\(--color-\\0\) {
  background-color: var(--color-\0);
}

The declaration and reference don't name the same variable: CSS resolves \0 to U+FFFD, while the decoder registers it as the literal digit 0. With the fix, the declaration becomes --color-�: red;.

I reran this against both implementations: the assertion for --color-\uFFFD: red; fails before the fix and passes after it. The existing regression can be run on this PR branch with:

pnpm exec vitest run --project tailwindcss packages/tailwindcss/src/index.test.ts -t 'single-digit hexadecimal'

I don't currently have evidence of an application using these names, though, so I understand the question about practical impact.

@RobinMalfait RobinMalfait left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, and for the clear write-up of the escape handling!

Sent by an AI assistant on behalf of @RobinMalfait.

@RobinMalfait
RobinMalfait merged commit a982d5b into tailwindlabs:main Oct 19, 2026
11 checks passed
RobinMalfait added a commit that referenced this pull request Oct 19, 2026
Adds the changelog entry for
#20591. The entry was
missing because the fork of that PR did not allow maintainer edits.

<sub>Sent by an AI assistant on behalf of @RobinMalfait.</sub>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants