less / less/less.js

rgb(from … r g b / <alpha>) throws under --math=always (Operation on an invalid type)

Open
#4,528 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
JavaScript
Stars
17k
Forks
3.4k
Avg merge
7h 42m
Merged PRs (30d)
26

Description

To reproduce:

CSS Color Level 5 relative-color syntax with an alpha slash. This is valid CSS that should be left for the browser.

Playground (4.9.1, --math=always):
https://lesscss.org/less-preview/#eyJjb2RlIjoiLmNhcmQge1xuICAvKiBDU1MgQ29sb3IgTGV2ZWwgNSByZWxhdGl2ZSBjb2xvciArIGFscGhhICovXG4gIGJhY2tncm91bmQ6IHJnYihmcm9tIHZhcigtLXN1cmZhY2UpIHIgZyBiIC8gMC45KTtcbn1cbiIsImFjdGl2ZVZlcnNpb24iOiI0LjkuMSIsIm1hdGgiOiJhbHdheXMiLCJzdHJpY3RVbml0cyI6ZmFsc2V9

Or locally:

.card {
  background: rgb(from var(--surface) r g b / 0.9);
}
npx lessc --math=always input.less

The origin does not have to be var(). A literal hex fails the same way:

.card {
  background: rgb(from #112233 r g b / 0.9);
}

Current behavior:

Less 4.4.0+ with --math=always throws:

Error evaluating function `rgb`: Operation on an invalid type

Less treats the CSS alpha slash as division. After #4346, b is a colorOperand, so b / 0.9 becomes a Less Operation and eval blows up.

Expected behavior:

Emit the declaration unchanged, same as 4.3.0 and same as 4.9.1 with --math=parens-division:

.card {
  background: rgb(from var(--surface) r g b / 0.9);
}

That matches the pass-through already expected by the tests added in #4346 / #4434 for rgb(from #0000FF calc(r + 1) g b).

Environment information:

  • less version: 4.9.1 (also reproduced on 4.4.0+. 4.3.0 is fine)
  • nodejs version: 24.12.0
  • operating system: macOS

Additional context:

rgb() as a Less color constructor is not new. The regression is that relative-color CSS started parsing as a Less rgb() call, then the / <alpha> slash is evaluated as math.

Input 4.3.0 --math=always 4.9.1 --math=always 4.9.1 --math=parens-division
rgb(from var(--x) r g b / 0.9) pass-through error pass-through
rgb(from #112233 r g b / 0.9) pass-through error pass-through
rgb(from var(--x) r g b) (no slash) pass-through pass-through pass-through
rgb(17, 34, 51) (Less constructor) #112233 #112233 #112233

Related:

  • #4343 / #4346 (v4.4.0): added colorOperand ([lchrgbs]) so oklch(from …) / rgb(from … calc(r + 1) …) would parse instead of Could not parse call arguments or missing ')'.
  • #4316 / #4434 (v4.6.6): tightened the operand regex so b matches before /.
  • #4479 (v4.8.1): same class of bug for math functions (sin(var(--x)) etc.). Those now return nothing and leave the call for the browser. rgb() does not get that treatment.

Workaround that should not be required: quote the value so Less does not parse it (~'rgb(from var(--surface) r g b / 0.9)'), or compile with --math=parens-division.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing the rgb() handling and colorOperand parsing described in #4346 and #4434, then compare the math=always path with the existing pass-through behavior for relative colors. Add a regression test for the alpha-slash form and verify that it emits the declaration unchanged without breaking ordinary Less rgb() construction.

Written by the indexing model from the issue text.

Assessment

Tech stack
css, javascript
Domain
compilers
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.