Search code examples
javascriptcssthemingcss-variables

Why does the Cascade for CSS Custom Properties Not Work?


I have a full working CodePen here showing the problem. I'm using CSS custom properties like so:

:root {
  --global-primary-colour-hue: 211;
  --global-primary-colour-saturation: 100%;
  --global-primary-colour-lightness: 50%;
  --global-primary-colour-opacity: 1;
  --global-primary-colour: hsla(
    var(--global-primary-colour-hue),
    var(--global-primary-colour-saturation),
    var(--global-primary-colour-lightness),
    var(--global-primary-colour-opacity));
}

.box {
  background-color: var(--global-primary-colour);
  height: 100px;
  width: 100px;
}

Then I've set up a range slider and box to display the colour in my HTML:

<input id="hue-range" value="0" type="range" min="0" max="360">

<div class="box"></div>

Finally I want to use the range slider to drive the --global-primary-colour-hue property. I can get this to work like so:

var element = document.getElementById("hue-range");
element.onchange = function(){
  document.body.style.setProperty(
    "--global-primary-colour-hue", 
    this.value.toString());

  // Why does the box stop changing colour when I comment out this line?
  document.body.style.setProperty(
    "--global-primary-colour",
    "hsla(var(--global-primary-colour-hue),var(--global-primary-colour-saturation),var(--global-primary-colour-lightness),var(--global-primary-colour-opacity))");
}

My question is, why do I have to set the --global-primary-colour property? When I uncomment that last line, the colour in the box no longer changes.


Solution

  • In your script, you're setting the custom properties on the body element. However, in your stylesheet, your custom properties are all (as usual) specified for :root, the html element. So the value of --global-primary-colour-hue is unchanged for :root, and the value of --global-primary-colour in turn remains unchanged. This unchanged value then gets inherited by body and .box — the new value of --global-primary-colour-hue ends up never getting used.

    Setting the property for document.documentElement in your script, or changing the CSS rule to target body instead, allows your code to work correctly without needing that last line:

    var element = document.getElementById("hue-range");
    element.onchange = function(){
      document.documentElement.style.setProperty(
        "--global-primary-colour-hue", 
        this.value);
    }
    :root {
      --global-primary-colour-hue: 211;
      --global-primary-colour-saturation: 100%;
      --global-primary-colour-lightness: 50%;
      --global-primary-colour-opacity: 1;
      --global-primary-colour: hsla(
        var(--global-primary-colour-hue),
        var(--global-primary-colour-saturation),
        var(--global-primary-colour-lightness),
        var(--global-primary-colour-opacity));
    }
    
    .box {
      background-color: var(--global-primary-colour);
      height: 100px;
      width: 100px;
    }
    <input id="hue-range" value="0" type="range" min="0" max="360">
    
    <div class="box"></div>