Резервные значения CSS переменных
Посмотрите на этот CSS код и угадайте, какого цвета будет текст. Никаких хитростей, это единственный релевантный код:
:root {
--color: green;
--color: notacolor;
color: red;
color: var(--color, blue);
}
Как это ни удивительно, но ответ — цвет текста по умолчанию, обычно чёрный (black
). Давайте разберёмся, почему так происходит, и как написать работающий резервное значение.
Резервные значения var()
Значение blue
выглядит вполне вероятным кандидатом на роль нашего цвета, не так ли? В конце концов, мы часто называем 2-й параметр var()
его «резервным» значением. Однако этот тип резервного значения используется только в следующих случаях:
- CSS переменная не определена
- Значением CSS переменной является ключевое слово
initial
. - CSS переменная привязана к анимации и используется в свойстве анимации.
и…
- CSS переменная не зарегистрирована с помощью
@property
.
Поскольку зарегистрированной CSS переменной должно быть присвоено начальное значение, она всегда будет иметь валидное значение и никогда не будет использовать свой резервный вариант.
CSS переменная определена, не анимирована, и её значение не является ключевым словом, поэтому можно выбросить blue
и продолжить поиски.
Объявления резервного значения
Обычно в CSS можно положиться на то, что каскад вернёт предыдущее валидное значение, что приводит к распространённой схеме, использующей двойное объявление свойства:
overflow: hidden;
overflow: clip;
Браузеры, не поддерживающие ключевое слово clip
, отбрасывают всё объявление и используют вместо него hidden
.
К сожалению, CSS переменные так не работают.
При анализе CSS переменной или соответствующей ему функции var()
браузер не знает, является ли она валидной, пока не придёт время вычислять её значение. Поэтому вместо этого они всегда рассматриваются как валидные, а все предыдущие объявления отбрасываются.
Это значит, что --color: green;
отбрасывается сразу после обнаружения --color: notacolor;
, а color: red;
отбрасывается, когда добирается до color: var(--notacolor, blue);
.
В итоге CSS вычисляется как:
color: notacolor;
Неудивительно, что это не валидно, и именно поэтому мы получили чёрный цвет.
Что можно сделать вместо этого
Всё это звучит плохо, но на самом деле есть несколько вариантов написания работающих резервных значений.
@property
Как уже говорилось, если зарегистрированная CSS переменная невалидна, она всегда будет использовать своё initial-value
значение, а значит, его можно использовать в качестве резервного значения:
@property --color {
syntax: '<color>';
inherits: true;
initial-value: purple;
}
:root {
--color: notacolor;
color: var(--color);
}
Именно то, что нужно:
- Можно определить только одно резервное значение для всего документа (что может быть достаточно хорошо, в зависимости от того, как организованы ваши CSS переменные).
- Смысл не очень очевиден, регистрация CSS переменной — это окольный путь к установке резервного значения.
Если для вас это не проблема и/или вы всё равно регистрируете свои объекты CSS переменные, то это отличный вариант.
@supports
Использование @supports
позволяет проверить валидность значения перед его объявлением, что даёт ещё большую гибкость в определении резервных значений. Рассмотрим два варианта его использования:
Сначала установим безопасное значение, а затем внутри блока @supports
объявим CSS переменную заново:
:root {
--color: red;
}
@supports (color: notacolor) {
:root {
--color: notacolor;
}
}
Тогда мы сможем использовать её везде, где захотим, не задумываясь о резервном значении:
p {
color: var(--color);
}
Мы можем задать её и забыть, будучи уверенными, что, когда значение не будет поддерживаться, останется предыдущее объявление, на которое можно будет опереться. В 9 случаях из 10 я использую именно это.
В качестве альтернативы давайте пропустим определение безопасного значения и определим CSS переменную только внутри @supports
:
@supports (color: notacolor) {
:root {
--color: notacolor;
}
}
Где же резервное значение? Поскольку CSS переменная объявляется только в том случае, если она поддерживается, можно использовать 2-й параметр var()
, чтобы записать резервное значение в строке:
p {
color: var(--color, red);
}
Если вам захочется использовать разные варианты резервного значения для одной и той же CSS переменной, этот вариант может оказаться более подходящим.
Будущие возможности
Со временем появится ещё больше возможностей для работы с невалидными значениями.
Новые возможности CSS, такие как ключевое слово revert-rule
и функция first-valid()
, позволят отказаться от @supports и писать резервное значение там, где это необходимо:
:root {
/* Множество резервных значений при объявлении CSS переменной */
--color: first-valid(notacolor, maybeacolor, red);
}
p {
/* Откат к другому правилу при использовании переменной */
color: first-valid(var(--color), revert-rule);
}
Наблюдать за их прогрессом можно на GitHub:
- Обсуждение, ведущее к разрешению ключевого слова
revert-rule
. - Обсуждение, ведущее к разрешению
first-valid()
.