Issue with using calc() inside rgb()?

Issue with using calc() inside rgb()?
0

#1

I had plans to do something neat with CSS custom properties and calc(), and hit a block earlier when trying to use calc() inside rgb(). All was fine if I wanted to do something like rgb(120, 50, calc(3 * var(--some-variable))), but what I really wanted to do was rgb(120, 50, calc(.5 * var(--some-variable))). When doing that, my color would not appear.

At first I thought it was some bug with calc() and CSS custom properties. Removing that from the equation left me with the same results.

What doesn’t work? Using multiplication, subtraction, or addition with a floating-point number, and division of any kind. The following examples all fail:

rgb(120, 50, calc(100 * .5))
rgb(120, 50, calc(100 + 1.0))
rgb(120, 50, calc(100 * 4.0))
rgb(120, 50, calc(300 / 2))

I would understand if the result was something like 33.33. rgb() needs integers. But in the case of calc(100 * .5), for example, the result is an integer, and with calc(100 / 2), only integers are used and the result is an integer.

Here is a test pen:

Note: In Firefox this will not work at all anyhow since they don’t allow calc() to be used inside rgb() :frowning: However, I am still interested in learning why this is not working in browsers that support calc() inside rgb().

Does anyone have any insight into what’s going on? I’d appreciate any thoughts!


#2

The floating point number is causing the problem. I assume when the calculation takes place it creates a floating point number instead of an integer. rgb will not work unless all the numbers are integers. You could add the SCSS pre-processor using Codepen and do the following to achieve what you want:

div {
  width: 100%;
  max-width: 240px;
  height: 60px;
  margin: 1rem;
  display: inline-block;
  outline: 1px dashed grey;
  outline-offset: 1px;

  &:first-child {
    background-color: rgb(120, 50, ceil(100 * 0.5));
  }
  
  &:nth-child(2) {
   background-color: rgb(120, 50, ceil(100*4.3));
  }

  &:nth-child(3) {
    background-color: rgb(120, 50, ceil(100/2));
  }
}

/* general styles  */

body {
  text-align: center;
}

#3

While ‘strictly’ the above results produce what we would ‘recognize’ as ‘ints’, the floating point component is preserved.

As in your first example, 100 *.5 is ‘strictly ‘50’’, but if you put just
’50.0’ w/o the calc (i.e. rgb(120, 50, 50.0) you’ll notice it doesn’t properly interpret the floating point value. As to why 300/2 doesn’t work that I am less sure…


#4

I don’t think you’re supposed to use calc because calc is for calculating units, and not just numbers. Maybe at some point calc thinks that you’re calculating pixels instead of an actual number. Just a guess.

The alternative is to use pre-processor like SCSS, which instantly just allows you to just use math operators with variables without using calc.

CSS custom properties are experimental properties after all. If you want variables in your CSS, pre-processor variables are still the way to go. And of course, there’s JavaScript.


#5

@RandellDawson

Thanks for looking at this issue!

I realize the floating point number inside the calc() function is the cause of some of the issues, though I am unclear why, since the result when calc() is evaluated is an integer. So in the case of calc(100 * .5), the result is 50 and I would assume it would act the same as rgb(120, 50, 50).

And then there is the issue of calc(100 / 2) where only integers are used and it still fails.

Are you suggesting that calc() itself evaluates the result to a float even when integers are used inside the function (that calc(100 / 2) results in 50.0 and not 50, for example)? Reading the spec, I cannot find anything to suggest this would be the case.


#6

@jx2bandito

That’s not true about the use of calc(). calc(), according to the spec:

It can be used wherever length, frequency, angle, time, percentage, number, or integer values are allowed.

And if you test, calc(100 + 50), for example, it works.


#7

Do you have a reference for this? I am not asking because I don’t believe you, but because I am trying to understand the workings of how these things get computed, to better debug in the future.

I’m still quite stumped about the division of integers not working!


#8

I was just playing around with various calculations and noticed addition, subtraction, and multiplication of integers works fine. It is when the division operator is used with integers, it does not work. I will research a bit more and get back with you if I find out why this happens.


#9

Yeah but something is just ‘not’ implemented right here.

I mean just assume for a moment we ‘stay’ in ‘integer land’, just as Randell is suggesting.

In this case ‘this’ works:

background-color: rgb(120, 50, calc(50 * 2));

So without having to touch floating points at all, logically, this should also work:

background-color: rgb(120, 50, calc(50 * (2/1)));

But it doesn’t…

crawls back to his CPP :>


#10

@RandellDawson

Thank you so much! I look forward to seeing if you find an answer. This is one of those things that has gotten under my skin, not knowing what the issue is! :thinking:


#11

@abalducci

Do you believe this might be a browser bug then? I wish Firefox allowed calc() inside of rgb() so I could test.


#12

@mewmew OK, I think I found in the SPEC why it is doing this for devision. Under Section 8.1.2 Type Checking, it states:

At /, check that the right side is <number>. If the left side is <integer>, resolve to <number>. Otherwise, resolve to the type of the left side.

The above confirms my suspicion that it converts this calculation to a non-integer number.


#13

@RandellDawson

I believe (and could be wrong) that means number vs. say unit or percentage.

For example, calc(50% / 2) would resolve to 25% according to the spec, but calc(50 / 2) should resolve to 25.

When clicking on how the spec defines a number in that paragraph:

Number values are denoted by , and represent real numbers, possibly with a fractional component.

When written literally, a number is either an integer, or zero or more decimal digits followed by a dot (.) followed by…


#14

Not sure. Maybe someone else will be able to shed some light on this issue.


#15

@RandellDawson

Thanks for all of the help with this issue! I wish the spec gave examples for clarity on this issue.


#16

Well, yes, but I meant it may only do that in certain situations. For most of its uses, it eventually converts the number into a unit. What if the part of calc function that checks what type of unit it eventually is goes screwy in edge cases.

Also consider that, while less popular, rgb also takes in a percentage format e.g. rgb(50%, 5%, 100%). Now, this format can’t mix and match with whole numbers. And yet it also won’t work with calc(50 * (2/1)). However, calc(50% * (2/1)) works perfectly. So I suspect whatever calc(50 * (2/1)) ends up with is simply a foreign number type to rgb.

I guess it really does just automatically divided numbers into a float.


#17

@jx2bandito

Great catch about the % working. This works:

rgb(60%, 90%, calc(100% * .5))

It won’t help with what I originally hoped to do with it, but it’s a great workaround for other possible use cases.

It’s so strange to me that divided integers resolve to a float. I suppose they do that to account for divided mixed numbers and two floats. That it’s easier then to just resolve them all to floats for consistency. I wish it wasn’t the case, since it’s a bit limiting.


#18

Well, I’m now convinced that it just makes a lot more sense for calc to automatically assume that any calc(integer / integer) will be a float because there are a lot more scenarios where the result of that calc will be used in a more complex calculation that involves a unit. The calc function also isn’t allowed to lead a division with an integer in any other scenarios e.g. calc(2 / 100%). CSS can interpret 100% in a lot of different ways and of course it wouldn’t make sense for calc to divide 2 by 100px either.

The only other property besides rgb I can think of that uses an integer is z-index, and it will encounter the same problems as rgb did with the same calc parameters.

Plus, there aren’t really that many times in CSS where you’d divide an integer with another integer when you could just specify the resulting integer from the get-go. They just didn’t bother to differentiate.


#19

Right, in the cases I would use it, it would be using variables in place of the integers, which is why I needed calc. There are all sorts of neat things we’d be able to do with rgb and hsl values with just CSS using calc() and custom properties. It’s more limiting in my experimentation rather than something that will frustrate 99% of developers. :wink: