[csswg-drafts] [css-color-5] [css-values] Using color components in trigonometric functions in relative color syntax yield different results in different browsers (#13938)

alinnert has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-color-5] [css-values] Using color components in trigonometric functions in relative color syntax yield different results in different browsers ==
## Problem

If you use a color component value (like `h`, `s`, or `l`) inside a trigonometric function (like `sin()` or `cos()`) inside the relative color syntax the result differs in every major browser.

I found there seem to be no WPT tests that check for this specific combination of CSS features.

### Example

``` css
.element {
  background: hsl(from hsl(0 50 50) h s calc((sin(l) + 1) * 50));
}
```

I've also created [a JSFiddle](https://jsfiddle.net/alinnert/4o0gwzby/) to demonstrate the issue.

## What the specs say

[The spec of the relative color syntax](https://drafts.csswg.org/css-color-5/#relative-syntax) says:

> The [component keywords](https://drafts.csswg.org/css-color-5/#component-keyword) return a [\<number>](https://drafts.csswg.org/css-values-4/#number-value), or [none](https://drafts.csswg.org/css-color-5/#valdef-light-dark-none); if they were originally specified as a [\<percentage>](https://drafts.csswg.org/css-values-4/#percentage-value) or an [\<angle>](https://drafts.csswg.org/css-values-4/#angle-value), that \<percentage> is resolved to a \<number> and the \<angle> is resolved to a \<number> of degrees (which is the [canonical unit](https://drafts.csswg.org/css-values-4/#canonical-unit)) in the range [0, 360].

[The spec of the Trigonometric Functions](https://drafts.csswg.org/css-values/#trig-funcs) says:

> The sin(A), cos(A), and tan(A) functions all contain a single [calculation](https://drafts.csswg.org/css-values/#calc-calculation) which must resolve to either a [\<number>](https://drafts.csswg.org/css-values/#number-value) or an [\<angle>](https://drafts.csswg.org/css-values/#angle-value), and compute their corresponding function by interpreting the result of their argument as radians. (That is, sin(45deg), sin(.125turn), and sin(3.14159 / 4) all represent the same value, approximately .707.)

## Expected behavior

A lightness value of `50` (or `50%`) in the origin-color should resolve to the \<number> `50`. This means that `sin(l)` and `sin(50)` should be equivalent. In both cases it should be interpreted as `sin(50rad)` which yields the value `-0.2623748537`. By adding `1` and multiplying the value with `100` you get a value between `0` and `100` - in this case `36.8812573148`. That means the final color should be `hsl(0 50 36.8812573148)`.

## Actual behavior

### Safari 26.5

Safari actually produces the correct color.

<details>
<summary>Safari screenshot</summary>

<img width="2630" height="1708" alt="Image" src="https://github.com/user-attachments/assets/b319d800-b120-4c4f-a33d-62a0db8109f4" />

</details>

### Chrome 148.0.7778.168

Chrome produces a different color: `hsl(0 49.8 88.3)`

After trying a few different values it seems that Chrome interprets `50%` as `50deg` when used in `sin()`. So, it calculates `sin(50deg)` instead of `sin(50rad)`. I think so because:

- A lightness value of `0` in the origin-color produces a color with medium brightness
- A value of `90` produces the lightest color
- A value of `180` produces the same color as the value `0`
- A value of `270` produces the darkest color
- A value of `360` produces the same color as the value `0`

I've checked all values from `0` to `360` in steps of `10` to be sure.

<details>
<summary>Chrome screenshot</summary>

<img width="2630" height="1708" alt="Image" src="https://github.com/user-attachments/assets/46f75ab5-cd9d-4a81-b7c2-2878938e1d7a" />

</details>

### Firefox 150.0.3

Firefox cannot calculate any color doing that. So, some form of internal error seems to occur. But the exact result depends on what the origin-color actually is.

Assuming I'm using a fallback color like this:

``` css
.element {
  background: gray;
  background: hsl(from hsl(0 50 50) h s calc((sin(l) + 1) * 50));
}
```

- If the origin-color is `currentColor` (`hsl(from currentColor ...)`) or a color function (like above) then the fallback color gets used.
- If the origin-color is a custom property however (`hsl(from var(--some-color) ...)`) no color is used at all and the element gets rendered with a transparent background, ignoring all `background` rules all together.

<details>
<summary>Firefox screenshot</summary>

- The orange box (with the diagonal line from bottom left to top right) represents the case where the fallback color gets ignored and the target element gets rendered with a transparent background.
- The red boxes (with the diagonal line from top left to bottom right) represent the case where the fallback color gets used.

<img width="2630" height="1708" alt="Image" src="https://github.com/user-attachments/assets/72a42604-e30f-4d55-bf97-3d680fd8c738" />

</details>

## What was I trying to do?

I was trying to make any color darker in a way so that light colors get way more dark than colors that are already somewhat dark. That's why I came up with this formula:

``` css
.element {
  background: hsl(from currentColor h s calc((sin((l * pi) / 200) * 100) / 2));
}
```

If it worked correctly it would result in every color having a lightness value between `0` and `50` while dark colors would be brighter than they would be with `calc(l / 2)`.

Here's a graph to visualize that:

<details>
<summary>Show graph</summary>

- The black, straight graph represents `calc(l / 2)`
- The red, curved graph represents `calc((sin((l * pi) / 200) * 100) / 2))`

<img width="2546" height="1706" alt="Image" src="https://github.com/user-attachments/assets/836accb6-da1d-4039-a12c-7c3c9ad9ca06" />

</details>

## Next steps, questions

I wanted to make sure that my version of "expected behavior" is correct. If that's the case I try to open issues on Firefox' and Chrome's issue tracker as well as create WPT tests.

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/13938 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Sunday, 17 May 2026 23:52:56 UTC