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
.element {
background: hsl(from hsl(0 50 50) h s calc((sin(l) + 1) * 50));
}
I've also created a JSFiddle to demonstrate the issue.
What the specs say
The spec of the relative color syntax says:
The component keywords return a <number>, or none; if they were originally specified as a <percentage> or an <angle>, that <percentage> is resolved to a <number> and the <angle> is resolved to a <number> of degrees (which is the canonical unit) in the range [0, 360].
The spec of the Trigonometric Functions says:
The sin(A), cos(A), and tan(A) functions all contain a single calculation which must resolve to either a <number> or an <angle>, 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.
Safari screenshot
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.
Chrome screenshot
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:
.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.
Firefox screenshot
- 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.
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:
.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:
Show graph
- The black, straight graph represents
calc(l / 2)
- The red, curved graph represents
calc((sin((l * pi) / 200) * 100) / 2))
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.
EDIT 2026-05-20
I've created an issue on the Chromium issue tracker:
https://issues.chromium.org/issues/515079560
I've found an already existing issue about this on the Firefox issue tracker:
https://bugzilla.mozilla.org/show_bug.cgi?id=1951206
Problem
If you use a color component value (like
h,s, orl) inside a trigonometric function (likesin()orcos()) 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
I've also created a JSFiddle to demonstrate the issue.
What the specs say
The spec of the relative color syntax says:
The spec of the Trigonometric Functions says:
Expected behavior
A lightness value of
50(or50%) in the origin-color should resolve to the <number>50. This means thatsin(l)andsin(50)should be equivalent. In both cases it should be interpreted assin(50rad)which yields the value-0.2623748537. By adding1and multiplying the value with100you get a value between0and100- in this case36.8812573148. That means the final color should behsl(0 50 36.8812573148).Actual behavior
Safari 26.5
Safari actually produces the correct color.
Safari screenshot
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%as50degwhen used insin(). So, it calculatessin(50deg)instead ofsin(50rad). I think so because:0in the origin-color produces a color with medium brightness90produces the lightest color180produces the same color as the value0270produces the darkest color360produces the same color as the value0I've checked all values from
0to360in steps of10to be sure.Chrome screenshot
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:
currentColor(hsl(from currentColor ...)) or a color function (like above) then the fallback color gets used.hsl(from var(--some-color) ...)) no color is used at all and the element gets rendered with a transparent background, ignoring allbackgroundrules all together.Firefox screenshot
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:
If it worked correctly it would result in every color having a lightness value between
0and50while dark colors would be brighter than they would be withcalc(l / 2).Here's a graph to visualize that:
Show graph
calc(l / 2)calc((sin((l * pi) / 200) * 100) / 2))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.
EDIT 2026-05-20
I've created an issue on the Chromium issue tracker:
https://issues.chromium.org/issues/515079560
I've found an already existing issue about this on the Firefox issue tracker:
https://bugzilla.mozilla.org/show_bug.cgi?id=1951206