3

I want to give a CSS variable the value unset, so I can use it for things like outline: unset. (I am aware that the "unset" in --unset-it: unset refers to the variable itself.)

Can it be done? I have made some test here:

const theDump = document.getElementById("dump");
const root = document.documentElement;
let checked = false;

document.getElementsByName("unset-it").forEach((elt) => {
  function displayIt(elt) {
    root.style.setProperty("--unset-it", elt.value);
    const propVal = getComputedStyle(root).getPropertyValue("--unset-it");
    theDump.textContent = `--unset-it: ${propVal};`;
  }
  if (!checked) {
    checked = true;
    elt.checked = true;
    elt.focus();
    displayIt(elt);
  }
  elt.addEventListener("click", evt => {
    const elt = evt.target;
    displayIt(elt);
  });
});
* {
  box-sizing: border-box;
}

#ex {
  background: yellow;
  padding: 0.5rem;
  margin: 1rem 0;
}

input[type=radio] {
  position: relative;
  width: 15rem;
  margin: 0;
  margin-left: 1rem;
  margin-bottom: 0.5rem;
}

input[type=radio]::after {
  content: attr(value);
  position: absolute;
  left: 0;
  top: 0;
}

#div1 {
  outline: var(--unset-it, 2px dotted red);
}
<input type="radio" name="unset-it" value='"unset"'>
<input type="radio" name="unset-it" value="'unset'">
<input type="radio" name="unset-it" value='unset'>
<input type="radio" name="unset-it" value='whatever'>
<input type="radio" name="unset-it" value='"whatever"'>
<input type="radio" name="unset-it" value='5px solid green'>
<input type="radio" name="unset-it" value='"5px solid green"'>
<input type="radio" name="unset-it" value="initial">

<div id="ex">
  <p id="div1">
    outline: var(--unset-it, 2px dotted red);
  </p>
  <p id="dump">Dump</p>
</div>
Leo
  • 4,136
  • 6
  • 48
  • 72

1 Answers1

5

Like I did in this previous answer with inherit You need to make unset the fallback value and consider initial to activate it:

* {
  box-sizing: border-box;
}

#ex {
  background: yellow;
  padding: 0.5rem;
  margin: 1rem 0;
}


#div1 {
  --unset-it: 2px dotted red;
  outline: var(--unset-it, unset);
}
<div id="ex">
  <p id="div1">
    outline: 2px dotted red;
  </p>
  <p id="div1" style="--unset-it:initial">
    outline: unset;
  </p>
</div>

The trick should work with any global value like unset, inherit, initial and revert


Another idea is to consider invalid value at computed time. From the specification we can read:

A declaration can be invalid at computed-value time if it contains a var() that references a custom property with its initial value, as explained above, or if it uses a valid custom property, but the property value, after substituting its var() functions, is invalid. When this happens, the computed value of the property is either the property’s inherited value or its initial value depending on whether the property is inherited or not, respectively, as if the property’s value had been specified as the unset keyword.

* {
  box-sizing: border-box;
}

#ex {
  background: yellow;
  padding: 0.5rem;
  margin: 1rem 0;
}


#div1 {
  outline: var(--unset-it, 2px dotted red);
}
<div id="ex">
  <p id="div1">
    outline: 2px dotted red;
  </p>
  <p id="div1" style="--unset-it:'random-value-here'">
    outline: unset;
  </p>
</div>

You should pay attention when using this because it is closely related to the property where you will be using the CSS variable since the validity of the value depend on the property.

Examples of value valid for some property and invalid for others:

* {
  box-sizing: border-box;
}

#ex {
  background: yellow;
  padding: 0.5rem;
  margin: 1rem 0;
}


#div1 {
  outline: var(--unset-it, 2px dotted red); /*invalid here*/
}
#div1::before {
  content:var(--unset-it,"content"); /* valid here */
}
<div id="ex">
  <p id="div1" style="--unset-it:'random-value-here';">
    outline: unset;
  </p>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Thanks, but I cannot see how to use it here. I want to set the value to "unset" in ":root". – Leo May 14 '20 at 13:00
  • @Leo you simply do nothing, if you don't define the CSS variable the fallback (unset) will be used. If you already defined the variable and you want get back to unset you use `--unset-it:initial` – Temani Afif May 14 '20 at 13:06
  • Thanks, but I want to override the local value by the `:root` value. I can't see that you are doing that. – Leo May 14 '20 at 13:28
  • 1
    @Leo what local value? – Temani Afif May 14 '20 at 13:32
  • The local value for `outline`. – Leo May 14 '20 at 13:34
  • 1
    @Leo what you mean by *local*? what is a *local value* for you – Temani Afif May 14 '20 at 13:35
  • My scenario is this: I define local values like those here for DOM elements while working with some difficult layout problems. Just too see what happens when resizing. I do this in web components. When the component is ready to ship I want to change the value of a css variable to hide this debug stuff. (Yes, I could use classes instead. But with web components css variables are easier since nothing in the web component have to be changed.) – Leo May 14 '20 at 13:42
  • 1
    @Leo you cannot do it this way, you are thinking like C and C++. You need to redo your logic like I did. The falback value need to be unset (this is the default value) then you set the variable to the outline you want to use when debugging. What you are doing cannot work – Temani Afif May 14 '20 at 13:44
  • @Leo here is what you need to do: https://jsfiddle.net/9wqfu0oh/2/ – Temani Afif May 14 '20 at 13:46
  • Thanks, but that would be much more work, since I use different colors for different things when debugging. (So I might use a css variable to turn on/off a class instead in the web component.) – Leo May 14 '20 at 13:46
  • So what are the rules? Can a CSS variable not carry the value `unset`? Or is it the syntax/semantic for setting the value that is the problem? Is there a JavaScript work around for setting the value to `unset`? – Leo May 14 '20 at 13:51
  • 1
    @Leo unset cannot be stored inside a CSS variables because it's a value for CSS variables (read the link I share to understand this) so If you try to do this way then you have 0 luck to succeed (even with JS). The only solution like I described is to consider unset as the fallback value and toggle the CSS variables with the needed value. If the CSS variables is defined then you have the outline, if not then you have your unset. – Temani Afif May 14 '20 at 13:55
  • I see. So the rule is that a CSS variable can not carry the value `unset`. A spec mistake if someone asks me. (Though I can understand that it was implemented as "ordinary css variables" to begin with. But the compiler surely knows what the first parameter to `var()` is.) – Leo May 14 '20 at 14:07
  • 1
    @Leo it's not a Spec mistake. unset is defined to be either initial or inherit based on the property and CSS variables is an inherited property so unset will fallback to inherit and we will inherit the value from the parent and if not defined then it will be nothing (initial) and this is also defined clearly in the Spec – Temani Afif May 14 '20 at 14:12
  • I still think it is a mistake. Any spec has a context. The context for a css variable is a little bit different. And that difference can be used to make the spec better. – Leo May 14 '20 at 14:27
  • 1
    @Leo the most powerful feature of CSS variables is that they can be inherited and behave the same way as common property. If this was a mistake and global value should behave differently then CSS variables will become completely useless. You agree with me implicitely because you want to set them inside `:root` and how you think they can be used within the other elements? this is done with inheritance because all the element will inherit the value from :root AND unset == inherit so even if the *supposed* mistake is corrected you cannot do what you want because you will loss the inheritance. – Temani Afif May 14 '20 at 14:35
  • Sorry, I do not agree. The difference is the context. There can surely be a value for a css variable that makes it behave as `unset` when used in `var()`. That has nothing to do with the inheritance. – Leo May 14 '20 at 14:37
  • To be more clear: That value above could for example have a name like `var-unset`, `var-initial`, etc. – Leo May 14 '20 at 14:40
  • Thanks for being helpful. I disagree about the spec, but we obviously are just thinking a bit differently. I resorted to use something like `--unset-it: none` together with things like `border: var(--unset-it, 1px solid red)`. I know I do not know how it works, but at least it feels good. (`--unset-it: mynone` seems to work exactly the same. And I have no idea why, but I actually think the spec is a bit unfinished.) – Leo May 14 '20 at 20:17
  • 1
    @Leo here is the part of the Spec explaining what you are doing: https://www.w3.org/TR/css-variables-1/#invalid-variables ... you are using an invalid value that will fall at computed-time and this will make your property don't use the custom property. This will give you at some degree what you want because it behave as unset but it's not robust as it may behave differently based on the property and the value you are using. Will update my answer describing this part as it can be useful – Temani Afif May 14 '20 at 20:27
  • 1
    @Leo added more details to my answer so you understand how your last code works – Temani Afif May 14 '20 at 20:37
  • Ah, that is very nice. So at the moment I can trust using "--unset-it: bad-value;". – Leo May 14 '20 at 21:51