csscss-variables

Using var() inside color-mix() appears to make it readable to browsers that don't support color-mix(), preventing the fallback from working


I'm using color-mix to take a button's background-color (set via a custom property) and make it lighter on hover. I expected browsers that don't support color-mix to simply ignore the hover state and use the non-hover background-color, but instead the button was turning transparent on hover. I added an explicit fallback background-color in the hover state styles but this had no effect:

.button {
  background: var(--buttonColor);
  &:hover, &:focus {
    background-color: var(--buttonColor);
    background-color: color-mix(in srgb, var(--buttonColor) 80%, white);
  }
}

After trying multiple variations of this, it seems the issue is that using a custom property inside color-mix() somehow makes browsers that don't support color-mix() think that they do, and their failed attempt at interpreting it results in the background-color becoming transparent (discovered by looking at which rules are used/discarded in dev tools). Because of this, my fallback is being ignored in the browsers that need it.

As a side note, the same issue was also happening with border-color, though this was defaulting to white rather than transparent.

After writing most of this out, I realised I can get around it by wrapping my color-mixes inside @supports (background-color: color-mix(in srgb, black 50%, white)), but decided to still post it in the hopes that:

  1. it helps someone else who's struggling with the same thing, and
  2. someone can shed some light on why this is happening so that I'm aware of the root issue in future

Solution

  • It's not about color-mix() but the use of CSS variables.

    Any property that contain var() is consider as valid even if it contains non-supported features

    If a property contains one or more var() functions, and those functions are syntactically valid, the entire property’s grammar must be assumed to be valid at parse time. It is only syntax-checked at computed-value time, after var() functions have been substituted. ref

    In your case, the property will fail at computed-value time and fallback to initial (transparent background)

    A declaration can be invalid at computed-value time if it contains a var() that references a custom property with the guaranteed-invalid 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 is one of the following depending on the property’s type:

    ...

    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. ref