Резервные значения CSS переменных

Браузер не знает, является ли значение CSS переменной валидным, пока переменная не будет разрешена, а к тому времени её обработает каскад и отбросит возможные резервные значения.

Посмотрите на этот CSS код и угадайте, какого цвета будет текст. Никаких хитростей, это единственный релевантный код:

:root {
--color: green;
--color: notacolor;
color: red;
color: var(--color, blue);
}

Как это ни удивительно, но ответ — цвет текста по умолчанию, обычно чёрный (black). Давайте разберёмся, почему так происходит, и как написать работающий резервное значение.

Резервные значения var()

Значение blue выглядит вполне вероятным кандидатом на роль нашего цвета, не так ли? В конце концов, мы часто называем 2-й параметр var() его «резервным» значением. Однако этот тип резервного значения используется только в следующих случаях:

и…

Поскольку зарегистрированной 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;

Неудивительно, что это не валидно, и именно поэтому мы получили чёрный цвет.

See the Pen

Что можно сделать вместо этого

Всё это звучит плохо, но на самом деле есть несколько вариантов написания работающих резервных значений.

@property

Как уже говорилось, если зарегистрированная CSS переменная невалидна, она всегда будет использовать своё initial-value значение, а значит, его можно использовать в качестве резервного значения:

@property --color {
syntax: '<color>';
inherits: true;
initial-value: purple;
}

:root {
--color: notacolor;
color: var(--color);
}

Именно то, что нужно:

Если для вас это не проблема и/или вы всё равно регистрируете свои объекты 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:

Комментарии


Дополнительные материалы

Предыдущая Статья

Отсутствующий в Eloquent метод owns()

Следующая Статья

Модернизация с Web-платформой: Уменьшение движения