Skip to content

fix(parse): match color function names and keywords case-insensitively - #275

Open
maximilliangrand wants to merge 2 commits into
Evercoder:mainfrom
maximilliangrand:fix/case-insensitive-parsing
Open

maximilliangrand wants to merge 2 commits into
Evercoder:mainfrom
maximilliangrand:fix/case-insensitive-parsing

Conversation

@maximilliangrand

@maximilliangrand maximilliangrand commented Aug 16, 2026 •

Copy link
Copy Markdown

CSS color function names and keywords are case-insensitive, but forms such as RGB(255 0 0), TRANSPARENT, COLOR(display-p3 ...), and uppercase hue units were rejected.

Normalize the trimmed input once in tokenize(), make the legacy RGB/HSL patterns case-insensitive, normalize angle units inside hueToDeg(), and accept case-insensitive transparent while guarding absent inputs. These incorporate all three maintainer suggestions. The color-profile lookup retains its own-property guard, so names such as CONSTRUCTOR cannot invoke inherited properties.

Validation of this follow-up (c94320a):

  • 32 focused parser tests pass on Node 24.21.0, including three new cases for absent transparent input, mixed-case keywords with exponential notation, and modern/legacy angle units. The absent-input regression throws on the previous PR head.
  • Build passed; ESM and CommonJS exports were checked directly for uppercase parsing and absent transparent input.
  • Whole-source/test lint and changed-file formatting pass.
  • Full suite: 238 passed and 13 floating-point exact-comparison failures on macOS arm64. The previous PR head (434b9d7) has the same 13 failures with the same Node runtime (235 passed before these three tests were added). No numerical conversion code changed. The full suite is not being reported as green.

AI assistance was used for the review follow-up and validation.

CSS function names and keywords are ASCII case-insensitive, but culori only
accepted them in lowercase: `RGB(255 0 0)`, `HSL(...)`, `OKLCH(...)`,
`COLOR(display-p3 ...)`, uppercase hue units (`120DEG`), and `TRANSPARENT`
all returned undefined, while `rgb(...)`/`red`/`RED`/`#FFF` worked.

- Normalize identifiers (function names, keywords, color-space names, hue
  units) to lowercase as they are consumed by the tokenizer.
- Add the `i` flag to the legacy comma-syntax rgb()/hsl() regexes and lowercase
  the captured hue unit before mapping it.
- Match `transparent` case-insensitively.
- Guard the color() profile lookup with an own-property check so identifiers
  inherited from Object.prototype (`constructor`, `__proto__`, …) resolve to
  no profile and return undefined instead of throwing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@danburzo danburzo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for this PR. I’ve made a few comments, but otherwise looks good!

Comment thread src/hsl/parseHslLegacy.js
if (match[3] !== undefined) {
res.h = +match[3];
} else if (match[1] !== undefined && match[2] !== undefined) {
res.h = hueToDeg(match[1], match[2]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just as a stylistic preference, could you move toLowerCase() to the hueToDeg() function directly?

switch (unit?.toLowerCase()) { … }

@@ -1,5 +1,5 @@
const parseTransparent = c =>
c === 'transparent'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It would be useful to guard for undefined colors with c?.toLowerCase().

Comment thread src/parse.js
while (_i < chars.length && IdentCodePoint.test(chars[_i])) {
v += chars[_i++];
}
return v;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As noted in #276, I think instead of all the changes here we just make the input lowercase in the tokenize() function.

@maximilliangrand

Copy link
Copy Markdown
Author

Thanks for the review. Addressed in c94320a. All three suggestions are now applied: tokenize() lowercases the input once after trimming, hueToDeg() handles case normalization itself, and parseTransparent() guards absent inputs.

I added coverage for the direct parseTransparent() call with no argument (which threw before this follow-up), mixed-case none with exponent notation, and uppercase angle units in both modern and legacy syntax. The focused parser suites pass all 32 tests on Node 24.21.0.

The full suite has 13 existing floating-point exact-comparison failures on this macOS arm64 machine. I verified the same 13 failures on the unchanged published head with the same Node runtime: 235 passing before, 238 after the three added tests. No numerical conversion code changed. Source/test lint and formatting checks pass.

The build also passes; I checked uppercase parsing and absent-input handling through both the built ESM and CommonJS exports.

This branch has not been deployed

No deployments
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