Почему браузер пытается использовать недопустимое объявление свойства, когда я ввожу переменную CSS?

#css #css-variables

#css #css-переменные

Вопрос:

Я использую два объявления фонового изображения, подобные этому, чтобы получить запасной вариант:

 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 bin: https://jsbin.com/jadoharoyi/edit?html ,css,вывод

Идея состоит в том, чтобы, например, Firefox показывал «обычный» bg, потому что он не поддерживает -webkit-image-set , и, например, Chrome показывал «1x» или «2x» bg (в зависимости от разрешения), потому что он поддерживает -webkit-image-set .

Пока все хорошо.

Но если я попытаюсь указать один или несколько URL-адресов изображений с помощью переменных CSS (по запутанным причинам), он падает:

 --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 bin: https://jsbin.com/vojiboqije/1/edit?html ,css,вывод

Теперь Firefox (85.0.2 на macOS Big Sur 11.1) вообще не показывает изображение bg. Насколько я могу судить, Firefox внезапно с радостью пытается использовать второе объявление фонового изображения, которое он не поддерживает.

Но это отлично работает в Chrome (88.0.4324.146).

Я не понимаю, почему. Есть идеи? Это ошибка? Или недоразумение с моей стороны?

Комментарии:

1. Исходя из предположения, что это проблема Firefox, я отправил отчет об ошибке: bugzilla.mozilla.org/show_bug.cgi?id=1691833

2. делаем заголовок более общим, поскольку он связан не только с Firefox или конкретным свойством

Ответ №1:

Вот как должны работать переменные CSS. При использовании переменных CSS браузер может оценивать значение только во время выполнения, поэтому значение будет считаться действительным (или, скажем, в режиме ожидания), пока мы не оценим переменную, и если браузер обнаружит, что все значение неверно, оно вернется к исходному или унаследует:

Объявление может быть недействительным во время вычисления значения, если оно содержит var(), который ссылается на пользовательское свойство с его начальным значением, как объяснено выше, или если оно использует допустимое пользовательское свойство, но значение свойства после замены его функций var() недопустимо. Когда это происходит,вычисленное значение свойства является либо унаследованным значением свойства, либо его начальным значением, в зависимости от того, наследуется свойство или нет, соответственно, как если бы значение свойства было указано в качестве ключевого слова unset . ссылка

Более явный пример с явно недопустимым значением:

 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> 

Комментарии:

1. Спасибо! Блестящий ответ. Очень хорошо объяснено. Тогда, я думаю, я мог бы достичь своей цели, полагаясь на наследование для резервного копирования.

2. @HenrikN добавил исправление к вашему конкретному случаю

3. Спасибо за фрагмент псевдо-элемента. Я боюсь, что в Chrome он показывает «обычный», а не ожидаемый «1x» или «2x». Я думаю, что разумнее всего было бы отказаться от фоновых изображений и вместо этого использовать решение с <img srcset> , но это было очень познавательно 🙂

4. @HenrikN Я допустил ошибку копирования / прошлого.. Я был уверен в своем ответе, что я даже не пробовал: p

5. Ах! Думаю, я взломал его. Я могу использовать переменную CSS только для резервного фона, а не резервную в качестве встроенного стиля: jsbin.com/kobivatuzo/edit?html ,css,вывод