Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS Variables inheritance and fallback

My question is why CSS Variables needed to add the concept of fallback and doesn't just rely on inheritance like the other CSS properties.

For example:

:root {
 --color1: red;
}

body {
 color: var(--color1);
}

body p {
 color: var(--color2);
 --color2: blue;
}

body span {
  --color3: green;
  color: var(--color3);
  color: yellow;
  color: var(--color4);
}
<body>
  Text 1
  <p>
    Text 2
  </p>
  <span>
    Text 3
  </span>
</body>

Text 3 ends up in being red instead of green or yellow. Although there are valid properties on the level, it takes the parent color value, instead of verifying if there are other valid values on the same level. This happens when the variable name is invalid.

Apparently there is a special fallback for CSS variables so you need to use something like:

color: var(--color4, yellow);

But this means again duplication of the color, since

color: var(--color4, --color3);

does not work. Neither is:

color: var(--color3, yellow, blue);

or any other multiple values.

Also no support for keywords like inherit, currentColor, initial, etc. So I'm not sure how I could rely on the inheritance since apparently I need to be very explicit with the given values. Tested on Firefox 57.0.1 and Chrome 63.

I know the CSS variables are still as Working Draft so maybe that's why the current behavior.

like image 361
Ecaterina Moraru Avatar asked Jan 03 '18 14:01

Ecaterina Moraru


People also ask

What is the fallback value of a CSS Variable?

The fallback value of a CSS variable is the second and optional argument of the var () function. For example, let’s consider we have some .box elements whose background is set to a variable of --c: If we haven’t explicitly specified a value for the --c variable elsewhere, then the fallback value #ccc is used.

Why does var () use fallbacks instead of inheritance?

This answer makes no attempt to address why var () was designed to use fallbacks instead of falling back on inheritance by default (though the fact that not all properties are inherited is a plausible, if not downright obvious, explanation).

Can a comma separated list have a fallback value?

First off, the fallback can be another CSS variable, which can have a CSS variable fallback itself and… we can fall down a really deep rabbit hole this way! Secondly, a comma separated list is a perfectly valid fallback value.

How to use fallback from Var () in JavaScript?

Furthermore, you are using it wrong, the correct way to use the fallback from var () should be this: var () only accepts 2 arguments, the value you want to use and the fallback.


2 Answers

As JoseAPL has stated, the reason var() expressions accept a fallback argument instead of defaulting to inheritance is simply because while custom properties do inherit, not all of the standard properties you will use them with do.

On the other hand, if you're asking why a var() expression doesn't default to the next best-cascaded value, that's because by the time the var() expression is evaluated, there are no other values to fall back to, because every other declaration in the cascade has been erased. See section 3.1, which defines the term invalid at computed-value time:

Note: The invalid at computed-value time concept exists because variables can’t "fail early" like other syntax errors can, so by the time the user agent realizes a property value is invalid, it’s already thrown away the other cascaded values.

Having said that, you can provide a custom property as a fallback value (as long as it's not the same one, for the same reason explained above) — that custom property just needs to appear in its own var() expression, since the fallback value is, well, a declaration value, not a property name.

So the result is a var() nested inside another var():

body span {
  --color3: green;
  color: var(--color3);
  color: yellow;
  color: var(--color4, var(--color3));
}

:root {
 --color1: red;
}

body {
 color: var(--color1);
}

body p {
 color: var(--color2);
 --color2: blue;
}

body span {
  --color3: green;
  color: var(--color3);
  color: yellow;
  color: var(--color4, var(--color3));
}
<body>
  Text 1
  <p>
    Text 2
  </p>
  <span>
    Text 3
  </span>
</body>
like image 101
BoltClock Avatar answered Oct 31 '22 15:10

BoltClock


First

Not all properties in CSS are inherited, see these links so you can see which CSS properties are inherited and how inheritance is applied:

https://www.w3.org/TR/CSS22/propidx.html
https://developer.mozilla.org/en-US/docs/Web/CSS/inheritance


Second

The order you declare duplicated properties is important.

If the color you want to be applied is green this is not the same:

// correct way to declare fallback color, it will be green and yellow if --color3 was not defined
body span {
  --color3: green;
  color: yellow;
  color: var(--color3);
}

as:

body span { // incorrect way to declare fallback color
  --color3: green;
  color: var(--color3);
  color: yellow;
  color: var(--color4);
}

The property color: var(--color4); will be taken since there is no invalid syntax on this declaration but since there is no variable declared as --color4 the inherited color will be applied (in this case from body { color: var(--color1) })

Because color is a inherited property


Third

Using the fallback with var() is another solution, but the previous one is a fallback for older browsers that don't support var()

Furthermore, you are using it wrong, the correct way to use the fallback from var() should be this:

color: var(--color4, var(--color3));

or this:

color: var(--color4, var(--color3, yellow));

var() only accepts 2 arguments, the value you want to use and the fallback.
See the syntax of how to use it in the following link


Conclusion

Since var() is not available in all browsers yet, using duplicated properties the right way is the option I would go with, or you can go with both of them.

:root {
 --color1: red;
}

body {
 color: var(--color1);
}

body p {
 color: var(--color2);
 --color2: blue;
}

body span {
  --color3: green;
  color: yellow;
  color: var(--color4, var(--color3));
}
<body>
  Text 1
  <p>
    Text 2
  </p>
  <span>
    Text 3
  </span>
</body>
like image 22
Jose Paredes Avatar answered Oct 31 '22 15:10

Jose Paredes