当我引入 CSS 变量时,为什么浏览器会尝试使用原本无效的 属性 声明?

Why does the browser try to use an otherwise invalid property declaration when I introduce a CSS variable?

我使用两个像这样的背景图像声明来获得后备:

background-image: url(https://via.placeholder.com/300x150.png?text=regular);
background-image: -webkit-image-set(
  url(https://via.placeholder.com/300x150.png?text=1x) 1x,
  url(https://via.placeholder.com/600x300.png?text=2x) 2x
);

JS 容器:https://jsbin.com/jadoharoyi/edit?html,css,output

这个想法是为了例如。 Firefox 显示“常规”bg,因为它不支持-webkit-image-set,例如Chrome 显示“1x”或“2x”背景图(取决于分辨率)因为它确实支持-webkit-image-set。

到目前为止一切顺利。

但是,如果我尝试通过 CSS 变量(对于 convoluted reasons)提供一个或多个图像 URL,它会失败:

--image-1x: url(https://via.placeholder.com/300x150.png?text=1x);

background-image: url(https://via.placeholder.com/300x150.png?text=regular);
background-image: -webkit-image-set(
  var(--image-1x) 1x,
  url(https://via.placeholder.com/600x300.png?text=2x) 2x
);

JS 容器:https://jsbin.com/vojiboqije/1/edit?html,css,output

现在,Firefox(macOS Big Sur 11.1 上的 85.0.2)根本不显示背景图像。据我所知,Firefox 突然乐于尝试使用它不支持的第二个背景图像声明。

但它在 Chrome (88.0.4324.146) 中工作正常。

我不明白为什么。有任何想法吗?这是一个错误吗?还是我的误会?

这就是 CSS 变量的工作方式。当使用 CSS 变量时,浏览器只能在 运行 时评估该值,因此该值将被视为有效(或者说处于待机模式),直到我们评估变量并且如果浏览器发现整个值无效,它将回退到初始或继承:

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

具有明显无效值的更明确的示例:

html {
  background:linear-gradient(red,blue); /* this will be used */
  background:strange-gradient(red,blue); /* this is a joke */
  
  min-height:100%;
}

并且当使用 CSS 变量时,不幸的是第二个将被使用,使你的第一个指令无用。

html {
  --c:red;

  background:linear-gradient(red,blue);  /* this will be ignored */
  background:strange-gradient(var(--c),blue); /* the joke is being used ..*/
  
  min-height:100%;
}


这种行为在某种程度上是合乎逻辑的,因为通过更改变量,我们可以使值有效(如果它以前无效)或无效(如果它以前有效),因此浏览器应该考虑它。


对于你的特殊情况,你可以考虑伪元素技巧:

.my-div {
  width: 300px;
  height: 150px;
  background-color: red;
  position:relative;
  z-index:0;
  
  --image-1x: url(https://via.placeholder.com/300x150.png?text=1x);

  background-image: url(https://via.placeholder.com/300x150.png?text=regular);
}
/* the pseudo element will cover the main background if it's valid */
.my-div::before {
  content:"";
  position:absolute;
  z-index:-1;
  top:0;
  left:0;
  right:0;
  bottom:0;
  background-image: -webkit-image-set(
    var(--image-1x) 1x,
    url(https://via.placeholder.com/600x300.png?text=2x) 2x
  );
}
<div class="my-div">
</div>