Подписывайтесь на мой твиттер, там всегда что-нибудь интересное!

Руководство по работе с переменными в CSS

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

Эта статья является переводом — CSS Variables Guide

Итак, откройте свой Codepen с нового проекта, чтобы быть готовым всё лично протестировать и пощупать.

Практически все языки программирования поддерживают переменные, но не CSS. Изначально он их и не поддерживал, нууу, до этого момента.

На самом деле, это скорее кастомные CSS свойства, чем “переменные”. Да, название технически неверное, но цепляет и даёт понимание.

CSS — это кромешный беспорядок

Любой, кто когда-либо работал с CSS, знает то, как тяжело держать код в порядке.

Для примера, использовать одни и те же значения в разных правилах это боль в плане поддержки и к тому же высока вероятность вызвать ошибку, например такую как опечатку и т.п. Множество раз мы предпочитали верить на слово нашему текстовому редактору и его замечательной функции “найти и заменить,” и это работало, правда одно время. Но чем больше проект, тем проще сделать изменение которое приведет к беспорядку, особенно в каком-нибудь ну уж самом неожиданном месте.

Но о чем всё это?

Вы уже наверное подумали о переменных в CSS? Но зачем они, если они уже есть в моем любимом препроцессоре?

Да, есть инструменты которые могут нам помочь в неких повторяющихся задачах. Препроцессоры такие как SASS и Stylus поддерживают переменные. Это делает нас более эффективными во время разработки и поддержки широкомасштабных приложений. Тем не менее, их недостаток в том, что довольно сложно произвести изменения во время выполнения кода.

Препроцессоры компилируют всё в стандартный CSS код. Альтернативой могла бы быть генерация кода динамически, как требуется. Но это было бы в конечном итоге довольно затруднительным процессом и к тому же очень медленным.

Причины для использования CSS переменных

Понятный код

Возможность делать изменения в больших проектах максимально просто

Избежание опечаток

Возможность делать изменения во время работы

Что это за кастомные CSS свойства?

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

Вот простой пример:

:root {
    --brand-color: #666;
}

#main {
    color: var(--brand-color);
}

:root {
    --brand-color: #666;
}
 
#main {
    color: var(--brand-color);
}

В котором –-brand-color это свойство, которое определяется значением #666. А var() это функция, которая позволяет нам получать доступ к значению, которое было предварительно определено, выдавая в результате color: #666;.

Синтаксис

Синтаксис довольно простой. Все свойства должны начинаться с двойного тире ( — — )

Свойства чувствительны к реестру, таким образом —-brand-color является уже отличным от —-Brand-color, который в свою очередь отличается от --Brand-Color.

Я знаю, возможно вы сейчас думаете, что синтаксис довольно непривлекателен и вы будете правы. Но двойное тире нужно для того, чтобы была некая ретро-совместимость с браузерами, которые до сих пор не поддерживают эти функции.

Каскадирование/Область видимости

Мы можем определять свойства, как «локальные» так и «глобальные». Переменные доступны в области видимости элементов, где они определены. Все элементы потомки имеют доступ к родительским свойствам, но не наоборот.

Кастомные свойства следуют тем же правилам, что и каскадные стили. Мы можем определить тоже свойство на разных уровнях, например:

:root { --text-color: green; }

div { --text-color: blue; }

.error { --text-color: red; }

* {
  color: var(--text-color);
}

<p>I'm green because got my color from root</p>

<div>I got blue from div selector</div>

<div class="error">
    I'm red because the .error.
    <p>Another red here because inheritance!</p>
</div>

Обратите внимание, что :root самый верхний элемент, эквивалентен глобальной области видимости. Тоже самое имя свойства может быть назначено с другим значением элементу потомку. Интересно, не так-ли?

Мы также можем изменять значения с помощью медиа запросов:

:root {
  --gutter: 10px 0;
}

main {
  padding: var(--gutter);
  display: flex;
  flex-flow: wrap;
  background-color: green;
}

@media (min-width: 600px) {
  :root {
    --gutter: 16px;
  }
}

article {
  padding: var(--gutter);
  background-color: yellow;
  height: 1rem;
  margin-bottom: 1rem;
  width: 100%;
}

article:last-of-type {
  margin-bottom: 0;
}

<main>
  <article></article>
  <article></article>
  <article></article>
</main>

Пример

Самые известные CSS препроцессоры не дадут вам определять переменные в медиа запросах. Внезапненько, да?

Тут даже можно определять свойства из уже существующих.

:root {
    --brand-color: red;
    --header-text-color: var(--brand-color);
}

:root {
    --brand-color: red;
    --header-text-color: var(--brand-color);
}

Примите во внимание, что это не работает с Microsoft Edge 15 из-за неизвестного бага.

Хоть это и мягко говоря не рекомендуется, но вы можете определять свойства в тэгах стилей, <html style = " --color: red;">.

Var()

Как написано на MDN, функция var() имеет такой синтаксис,

var( <custom-property-name>[, <declaration-value>]? )

Где custom-property-name это название свойства, которое вы определяете. Если оно неверное или не существует, declaration-value будет использовано вместо него.

Мы может объявлять больше одного разделенных запятой свойств, как в случае с font-family.

Будьте осторожны с запятыми. Для примера, чтобы указать более двух свойств для padding, сделайте это как тут:

.foo {
  padding: var(--gutter, 10px 0 0 5px);
}

--gutter это основное значение, а «10px 0 0 5px» используется если -- gutter имеет неверное значение или вообще не существует.

Ограничения

Само var() не поддерживает вставление в строки или их связывание.

В отличие от препроцессоров, var() не может быть использована, чтобы определять имена свойств.

Если вы хотите пойти немного глубже, вы можете детально обратить внимание на CSS синтаксис.

Циклические зависимости

Допустимо реализовывать зависимости между свойствами, как в примере ниже.

:root {
  --main-color: #c06;
  --accent-background: linear-gradient(to top, var(--main-     color), white);
}

Но циклические зависимости недопустимы. Вот пример:

:root {
  --one: #c06;
  --two: #ccc;
  --one: calc(var(--two) + 20px);
  --two: calc(var(--one)20px);
}

В этом случае, как -one, так и -two рассматривались бы как неверные. Их изначальные значения не переписаны и остаются такими же, таким образом они бы имели свои изначальные значения вместо указанных.

Указываем значения с помощью calc()

Функция calc() используется для выполнения вычислений и определения CSS значений. На данный момент она поддерживается всеми современными браузерами.

Вы можете комбинировать её с var(), чтобы указывать значения на лету.

header {
  --gutter: 20;
  padding: calc(var(--gutter) * 1px);
}

В этом случае, --gutter указан, как единичный знак с цифровым значением 20. Но padding нужна единица измерения (такая как px). Так как вы не можете связывать строки, вы можете вместо этого умножить на «1px», для того, чтобы искусственно подправить значение.

Работа с JavaScript

Чтобы получить значение кастомного свойства в JavaScript, используйте getPropertyValue() метод из CSSSTyleDeclaration.

<style>
    :root {--brand-color: cyan; }
    p { color: var(--brand-color); 
</style>

<p>This text is cyan</p>

<script>
    const styles = getComputedStyle(document.documentElement);
    const colorValue = styles.getPropertyValue('--brand-color');
    // colorValue = 'cyan';
</script>

<style>
    :root {--brand-color: cyan; }
    p { color: var(--brand-color); }
</style>
 
<p>This text is cyan</p>
 
<script>
    const styles = getComputedStyle(document.documentElement);
    const colorValue = styles.getPropertyValue('--brand-color');
    // colorValue = 'cyan';
</script>

С другой стороны, чтобы определить свойство, мы используем setProperty() метод:

<style>
    :root {--brand-color: cyan; }
    p { color: var(--brand-color); }
</style>

<p>This text is red</ p>

<script>
    document.documentElement.style.setProperty ('--brand-color', 'red');
</script>

<style>
    :root {--brand-color: cyan; }
    p { color: var(--brand-color); }
</style>
 
<p>This text is red</ p>
 
<script>
    document.documentElement.style.setProperty ('--brand-color', 'red');
</script>

Вы также можете определять значения свойств из значений других, используя старого друга var() с setProperty():

<style>
    :root {
        --brand-color: cyan;
        --secondary-color: yellow;
    }
</style>

p { color: var (--brand-color); }

<p>I'm yellow!</p>

<script>
    document.documentElement.style.setProperty ('--brand-color', 'var (--secondary-color)');
</script>

<style>
    :root {
        --brand-color: cyan;
        --secondary-color: yellow;
    }
</style>
 
p { color: var (--brand-color); }
 
<p>I'm yellow!</p>
 
<script>
    document.documentElement.style.setProperty ('--brand-color', 'var (--secondary-color)');
</script>

Поддержка на данный момент

Кастомные свойства на данный момент поддерживаются большинством браузеров.

У Microsoft Edge 15 есть проблемы. Следующие баги выявлены:

Игнорирование вложенных расчетов

Анимации с кастомными свойствами могут покрашить браузер

Вы не можете использовать эти свойства с псевдо-элементами