Почему мы предпочитаем CSS(--variable) переменным SASS($variable)?

Автор оригинала: Sebastiano Guerriero
  • Перевод
С момента релиза CodyHouse Framework, пару месяцев назад, многие пользователи спрашивали нас, почему мы выбрали переменные CSS вместо переменных SASS, хотя мы используем SASS в этой среде. В этой статье я расскажу о преимуществах использования пользовательских свойств и о том, почему они стали решающими в нашем рабочем процессе.



Определение переменных


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

В SCSS:

$color-primary: hsl(220, 90%, 56%);

.link {
  color: $color-primary;
}

В CSS:

:root {
  --color-primary: hsl(220, 90%, 56%);
}

.link {
  color: var(--color-primary);
}

Собственные, пользовательские свойства позволяют вам определять переменные без необходимости использовать расширения для CSS (т.е. не нужно использовать SASS).

Они одинаковы? На самом деле, нет! В отличие от переменных SASS, пользовательские свойства 1) ограничены элементом на котором они объявлены, 2) каскадом, 3) ими можно управлять в JavaScript. Эти три особенности, открывают целый мир новых возможностей. Позвольте мне показать вам несколько практических примеров!

Создание и применение цветовых тем


Вот пример того, как вы могли бы создать две (упрощенные) цветовые темы, используя переменные SASS:

$color-primary: blue;
$color-text: black;
$color-bg: white;
/* Инверсия */
$color-primary-invert: red;
$color-text-invert: white;
$color-bg-invert: black;

.component {
  color: $color-text;
  background-color: $color-bg;

  a {
    color: $color-primary;
  }
}

.component--dark {
  color: $color-text-invert;
  background-color: $color-bg-invert;

  a {
    color: $color-primary-invert;
  }
}

В приведенном выше примере, у нас есть тема «по умолчанию» и «темная» тема, где мы инвертируем цвета для фона и текста. Обратите внимание, что в темной теме нам нужно пройтись по каждому свойству, в котором использовались переменные цвета, и обновить их новой переменной.

Пока мы придерживаемся упрощенных (нереалистичных) примеров, никаких проблем не возникает. Но что если у нас есть компонент с большим количеством элементов? Нам бы пришлось переписать все свойства, где используются переменные для цвета, и заменить переменные на новые. И если вы измените основной компонент, вы должны дважды проверить все модификаторы. Да… не так удобно!

При создании нашего фреймворка мы придумали другой подход, основанный на переменных CSS. Прежде всего, давайте определим переменные для цвета:

:root, [data-theme="default"] {
  --color-primary: blue;
  /* Цветовой контраст */
  --color-bg: white;
  --color-contrast-lower: hsl(0, 0%, 95%);
  --color-contrast-low: hsl(240, 1%, 83%);
  --color-contrast-medium: hsl(240, 1%, 48%);
  --color-contrast-high: hsl(240, 4%, 20%);
  --color-contrast-higher: black;
}

[data-theme] {
  background-color: var(--color-bg);
  color: var(--color-contrast-high);
}

[data-theme="dark"] {
  --color-primary: red;
  /* Цветовой контраст */
  --color-bg: black;
  --color-contrast-lower: hsl(240, 6%, 15%);
  --color-contrast-low: hsl(252, 4%, 25%);
  --color-contrast-medium: hsl(240, 1%, 57%);
  --color-contrast-high: hsl(0, 0%, 89%);
  --color-contrast-higher: white;
}

К сведению: в приведенном выше примере мы используем атрибуты data-* для применения цветовой темы, но это не имеет ничего общего с переменными CSS и переменными SASS. Кроме того, мы определили шкалу нейтральных значений, используя номенклатуру на основе «уровня контрастности»

Важным моментом является то, что нам не нужно создавать новые цветовые переменные для нашей второй, третьей, четвертой… (темной) темы. В отличие от SASS, мы можем переопределить значение существующих пользовательских свойств.

Вот как применить переменные цвета к компоненту:

.component {
  color: var(--color-contrast-higher);
  background-color: var(--color-bg);
  border-bottom: 1px solid var(--color-contrast-low);

  a {
    color: var(--color-primary);
  }
}

Как насчет темного варианта компонента? Нам не нужны дополнительные CSS. Поскольку мы переопределяем и не заменяем переменные, нам нужно применить правильные переменные для цвета, только при первом создании компонента. Неважно насколько сложным становится компонент, после того как вы установили цветовые темы в файле _colors.scss и применили цветовые переменные к элементам своих компонентов.

Вы можете применять цветовые темы очень простым способом:

<section data-theme="dark">
  <div class="component">
    <div class="child" data-theme="default"></div>
  </div>
</section>

В приведенном выше примере мы применили тему «темного» цвета к разделу и тему цвета «по умолчанию» к элементу «.child». Все верно, вы можете вкладывать цветные темы!

Это стало возможным благодаря использованию CSS свойств. Что позволило в кратчайшие сроки делать такие интересные вещи, как эта.



Вот несколько ссылок на тот случай, если вы хотите узнать больше об управлении цветами с помощью фреймворка CodyHouse:


Управление шкалой шрифтов


Модульная шкала — это набор гармоничных(размерных) значений, которые применяются к типографским элементам. Вот как вы можете установить масштаб шрифта в SCSS, используя переменные SASS:

$text-xs: 0.694em;
$text-sm: 0.833em;
$text-base-size: 1em;
$text-md: 1.2em;
$text-lg: 1.44em;
$text-xl: 1.728em;

Стандартным подходом было бы создание шкалы шрифтов с использованием стороннего инструмента (или выполнение математических операций), а затем импорт значений в ваш стиль, как в примере выше.

При создании фреймворка мы решили включить всю формулу масштаба в файл _typography.scss. Вот как мы устанавливаем масштаб шрифта с помощью переменных CSS:

:root {
  // Базовый размер шрифта
  --text-base-size: 1em;
  
  // Шкала типов
  --text-scale-ratio: 1.2;
  --text-xs: calc((1em / var(--text-scale-ratio)) / var(--text-scale-ratio));
  --text-sm: calc(var(--text-xs) * var(--text-scale-ratio));
  --text-md: calc(var(--text-sm) * var(--text-scale-ratio) * var(--text-scale-ratio));
  --text-lg: calc(var(--text-md) * var(--text-scale-ratio));
  --text-xl: calc(var(--text-lg) * var(--text-scale-ratio));
  --text-xxl: calc(var(--text-xl) * var(--text-scale-ratio));
  --text-xxxl: calc(var(--text-xxl) * var(--text-scale-ratio));
}

В чем преимущество такого подхода? Это дает возможность контролировать всю типографскую систему, редактируя только две переменные: --text-base-size (размер базового шрифта) и --text-scale-ratio (множитель масштаба).

«Разве нельзя сделать то же самое, используя переменные sass»? Нет, если вы хотите изменить типографику в определенных точках при медиа-запросах:

:root {
  @include breakpoint(md) {
    --text-base-size: 1.25em;
    --text-scale-ratio: 1.25;
  }
}

Данный фрагмент является краеугольным камнем нашего гибкого подхода. Поскольку мы используем относительные единицы измерения Ems, при изменении --text-base-size (размер базового шрифта) это влияет, как на типографику, так и на интервалы. В итоге вы получаете систему, которая изменяет размеры всех ваших компонентов практически без необходимости задавать медиа-запросы на уровне компонентов.



Вот несколько полезных ссылок по теме:


Управление шкалой расстояний


Шкала интервалов эквивалентна шкале типов, но применяется к пространственным значениям. Еще раз, включение формулы масштаба в структуру позволило нам контролировать систему интервалов и сделать ее отзывчивой:

:root {
    --space-unit:  1em;
    --space-xxxxs: calc(0.125 * var(--space-unit)); 
    --space-xxxs:  calc(0.25 * var(--space-unit));
    --space-xxs:   calc(0.375 * var(--space-unit));
    --space-xs:    calc(0.5 * var(--space-unit));
    --space-sm:    calc(0.75 * var(--space-unit));
    --space-md:    calc(1.25 * var(--space-unit));
    --space-lg:    calc(2 * var(--space-unit));
    --space-xl:    calc(3.25 * var(--space-unit));
    --space-xxl:   calc(5.25 * var(--space-unit));
    --space-xxxl:  calc(8.5 * var(--space-unit));
    --space-xxxxl: calc(13.75 * var(--space-unit));
}

@supports(--css: variables) {
  :root {
    @include breakpoint(md) {
      --space-unit:  1.25em;
    }
  }
}

Этот подход становится особенно мощным в сочетании с методом типографики, рассмотренным выше. С помощью всего лишь нескольких строк CSS вы получите отзывчивые компоненты:



Еще одна вещь, которая мне нравится в использовании модулей Ems вместе с этой системой интервалов, заключается в том, что если размеры интервалов и типографики выглядят правильно в конкретной точке медиа-запроса, они почти наверняка смотрятся правильно на всех точках медиа-запросов, независимо от того, что вы обновляете значение --space-unit. Поэтому, я могу проектировать без необходимости изменять размеры окон браузера (кроме случаев, когда я хочу изменить поведение компонента).

Больше ссылок по теме:


Редактирование «вертикального ритма» на уровне компонентов


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

Вот пример, когда вы устанавливаете вертикальный интервал для текстового компонента, вы, вероятно, захотите указать line-height и margin-bottom для ваших элементов:

.article {
  h1, h2, h3, h4 {
    line-height: 1.2;
    margin-bottom: $space-xs;
  }

  ul, ol, p, blockquote {
    line-height: 1.5;
    margin-bottom: $space-md;
  }
}

Тем не менее, этот темп варьируется в зависимости от того, где используется этот текст. Например, если вы хотите, чтобы ваш текст был более сжатым, вам нужно создать вариант компонента(модификатор), в котором вы применяете разные значения интервалов:

.article--sm {
  h1, h2, h3, h4 {
    line-height: 1.1;
    margin-bottom: $space-xxxs;
  }

  ul, ol, p, blockquote {
    line-height: 1.4;
    margin-bottom: $space-sm;
  }
}

… и так каждый раз, если вы захотите обновить «вертикальный ритм».

Вот альтернативный подход, основанный на переменных CSS:

.text-component {
  --component-body-line-height: calc(var(--body-line-height) * var(--line-height-multiplier, 1));
  --component-heading-line-height: calc(var(--heading-line-height) * var(--line-height-multiplier, 1));
  --line-height-multiplier: 1;
  --text-vspace-multiplier: 1;

  h1, h2, h3, h4 {
    line-height: var(--component-heading-line-height);
    margin-bottom: calc(var(--space-xxxs) * var(--text-vspace-multiplier));
  }

  h2, h3, h4 {
    margin-top: calc(var(--space-sm) * var(--text-vspace-multiplier));
  }

  p, blockquote, ul li, ol li {
    line-height: var(--component-body-line-height);
  }
  
  ul, ol, p, blockquote, .text-component__block, .text-component__img {
    margin-bottom: calc(var(--space-sm) * var(--text-vspace-multiplier));
  }
}

--line-height-multiplier и --text-vspace-multiplier являются двумя переменными управления текстового компонента. Когда мы создаем модификатор класса .text-component, для редактирования вертикального интервала нам нужно только переопределить эти две переменные:

.article.text-component { //  Например сообщение в блоге
  --line-height-multiplier: 1.13; // Увеличить высоту строки статьи
  --text-vspace-multiplier: 1.2; // Увеличить вертикальный интервал
}

В случае, если вы хотите попробовать сами и взять на заметку:



Абстрагирование компонентов поведения


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

Примером является наш компонент Auto Sized Grid, где мы используем CSS-сетку для создания макета, где элементы галереи автоматически заполняют доступное пространство на основе минимальной ширины, заданной в CSS, затем мы абстрагируем значение min-width элементов, храня его в переменной.


Значение минимальной ширины — это единственное, что нужно изменить при создании варианта компонента сетки Auto Size.

В чем подвох?


В двух словах: поддержка браузерами… Вы можете использовать переменные CSS всеми способами, описанными в этой статье, с помощью плагина PostCss. Есть ряд ограничений для старых браузеров(<IE11). В остальном, можно свободно использовать. Посмотрите нашу документацию, для получения дополнительной информации, об ограничениях использования переменных CSS сегодня.

Могу ли я использовать переменные CSS с препроцессором?


Да! Пока SASS (или любой другой препроцессор) позволяет вам делать то, чего вы не можете делать в CSS, и вам это нужно, почему бы не использовать его? SASS — это не библиотека, которую пользователи должны загружать при доступе к вашему сайту. Это инструмент в вашем рабочем процессе. Мы используем SASS, например, для определения цветовых функций, которые работают с переменными CSS.

Заключение


В этой статье мы рассмотрели несколько примеров, демонстрирующих преимущество использования переменных CSS над переменными SASS. Мы сфокусировались на том, как они позволяют вам создавать «элементы управления», которые ускоряют процесс изменения компонентов или устанавливать правила, влияющие на типографику и интервалы. Мы многое рассмотрели, и я надеюсь, что вы сможете что-то почерпнуть из этого поста и использовать его в своей работе.

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

А вы используете CSS переменные?

  • +24
  • 10k
  • 4
Поддержать автора
Поделиться публикацией

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

    +2
    Управление шкалой типов

    В контексте статьи type это «шрифт», а не «тип».
      +1
      Спасибо, поправил.
      0
      Не используем, но рассматриваем в качестве основного кандидата для темизации.
      Номер два всякие CSS-in-JS решения, однако решение с CSS переменными позволит избежать переписывания существующего кода, так как мы уже используем CSS-modules и SCSS.
        0
        «преимущество использования переменных CSS над переменными SASS»
        Вот зачем путать тёплое с мягким? Просто sass-переменные они для AOT (Ahead-of-Time), а custom properties для JIT (Just-in-time). Их вполне нормально использовать совместно.

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

        Самое читаемое