После своего появления в мире фронтенда несколько лет назад, препроцессоры были объявлены спасителями CSS, принесшими модульность, смысл и даже некоторую степень сексуальности. Термины вроде “Архитектура Sass” стали общим местом, а новое поколение фронтенд разработчиков стремится расширить пределы допустимого с этой новой силой. Результаты бывают как волшебные, так и нежелательные.

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

CSS и препроцессоры

В наши дни разработка CSS без препроцессоров считается плохой практикой. Результаты этого подхода не всегда хорошие. Изображение: SXSW

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

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

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

Как бы не были противоречивы эти взгляды, они являются неизбежным побочным эффектом эволюции препроцессоров и повлекли создание превосходных руководств по написанию стилей — это работы Криса Койера и Хьюго Жираделя, а также SassDoc для генерации документации Sass.

Что является основой препроцессора?

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

Хотя эта статья в основном о переменных препроцессоров, мы коснемся и нативных переменных CSS. Я рассчитываю, что вы знакомы с Sass, LESS, Stylus или PostCSS — в этой статье все примеры и демонстрации реализованы на Sass.

Почему переменные должны быть ядром в проектах с препроцессорами

Неважно, работаете ли вы с кодом стороннего разработчика или с кодом вашей команды, вы знаете, что наличие хорошей документации и руководства по стилю облегчит вашу жизнь. Однако вам по прежнему надо разбираться в фрагментах Sass, LESS или Stylus начиная с хелперов, миксинов, раскладки и т.д. Естественно, ситуация выходит из под контроля, поскольку многие моменты остаются открытыми для интерпретации:

  • “Добавить ли это свойство к глобальным переменным или вынести в файл с базовыми стилями?”
  • “Почему это находится в файле с раскладкой? Может лучше переместить в файл, содержащий сетку?”
  • “Сделать из этого класс, миксин, расширение или все вместе?”

Атомы для одного это молекулы для другого, даже если вы тщательно следуете методике атомарного дизайна, созданной Брэдом Фростом и Дэйвом Олсеном.

Следующий сценарий: вы встретили миксин, написанный разработчиком А, включающий миксин для вендорных префиксов от разработчика В, добавленный в определенной точке; расширение из другого фрагмента (добавленное разработчиком A по принципу DRY), а также две переменные, заданные в разных файлах…

По причине высокого уровня абстракции, доступного препроцессорам, я рекомендую делать файл с переменными единственным источником истины.

Хранение переменных CSS

Во многих проектах, использующих препроцессоры есть файл _variables.scss, который по моему мнению должен содержать каждую (или почти каждую) переменную проекта (за исключением нескольких локальных переменных). Этот файл играет решающую роль для CSS проекта. При любых сомнениях, разработчик может сразу ссылаться на фрагмент с конфигурацией или переменными.

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

Когда надо создавать переменную?

Если значение появляется в стилях не один раз, то, скорее всего, двумя появлениями дело не ограничится. Также, если есть шанс, что значение (цвет, длина, размер и т.д.) будет обновляться, стоит создать новую переменную.

Ниже показан общий пример использования переменной для хедера, зафиксированного в области просмотра:

See the Pen Reusing Sass variables by Karen Menezes (@imohkay) on CodePen.

Как видно из демо, переменная $height-header используется для задания высоты хедера, триггера меню и поля поиска, а также верхнего пэддинга элемента body. И обновление этого кода будет простым.

Также отметьте, как переменная $height-footer интерполируется для получения минимальной высоты элемента main, при этом используется синтаксис #{$variable-name}, позволяющий Sass в момент компиляции подставить на это местно нужное значение (40 пикселей).

Ограничивайте количество миксинов и расширений самым необходимым

Одним из самых простых способов обеспечить первичную ссылку в проекте на файл с переменными будет задание всех переменных в одном месте (за исключением некоторых локальных переменных) и устранение все излишних миксинов и расширений. Это основательно уменьшает абстрактность кода. Инструмент типа Autoprefixer создаст для вас все вендорные префиксы, тем самым уменьшая количество миксинов до самых общих типа клирфикса, меди-запросов, сетки, расчетов, а также треугольников на CSS и обрезки текста.

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

Пишите чистый CSS в случаях, когда абстракция лишь вносит путаницу

/* Избегайте миксинов, которые легко заменить простым CSS. */

@mixin center-element {
   display: block;
   margin-left: auto;
   margin-right: auto;
}

.selector {
   @include center-element;
}

/* Есть несколько способов центрирования в CSS и использованный миксин не дает подсказки о конкретном методе (без ссылки на фрагмент, в котором он содержится).
Это может быть как флексбокс, так и выравнивание текста, автоотступы или вертикальное центрирование. */

/*-----------------------------------------------------*/
/* Почему бы не написать простой CSS и не уменьшить абстракцию в данном случае? */

.selector {
   display: block;
   margin-left: auto;
   margin-right: auto;
}

Используйте миксины или расширения для общеизвестных приемов

/* А так можно: хак для очистки флоатов широко известен и большинство разработчиков его распознают */

/* Плейсхолдер с клирфиксом: применяется к контейнерам плавающих элементов для очистки */

%clearfix {
   &:after {
      content: '';
      display: table;
      clear: both;
   }
}

.selector  {
  @extend %clearfix; /* вместо плейсхолдера можно использовать и аналогичный миксин */
}

Если миксин в глобальных стилях не требует аргументов, замените его переменной

/* Избегайте такого кода: */

@mixin box-shadow {
  box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}

/*-----------------------------------------------------*/

/* Вместо этого просто создайте переменную: */

$box-shadow-base: 2px 2px 4px rgba(0, 0, 0, 0.1);

.selector {
   box-shadow: $box-shadow-base; /* Используйте переменную там, где уместно. */
}

Не мучайте себя, используя миксины для вендорных префиксов

/* Избегайте этого.
Это привязывает вас к миксину и создает излишнюю абстрактность.
*/

@mixin flexbox {
   display: -webkit-box;
   display: -webkit-flex;
   display: -moz-flex;
   display: -ms-flexbox;
   display: flex;
}

.selector {
   @include flexbox;
}

/* Рассмотрите возможность использования Autoprefixer, который занимается только вендорными префиксами и делает это хорошо. */

.selector {
   display: flex;
   /* Автопрефиксер сгенерирует все необходимые префиксы для ваших стилей. */
}

Используйте локальные переменные только по необходимости

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

  • В разных препроцессорах переменные обрабатываются по-разному. Например, в Sass есть возможность задавать переменным значения по умолчанию с помощью флага !default. А в LESS есть возможность “ленивой загрузки” переменных.
  • Убедитесь, что вы понимаете область видимости переменной. Об этом есть отличная статья Джорджа Марцукоса.
  • Очень легко неумышленно создать две локальные переменные с одним именем, в двух разных блоках кода или миксинах. Хотя это и не проблема (переменные ограничены зоной видимости), это может вызвать затруднения с номенклатурой переменных в долгосрочной перспективе.
  • Если у вас несколько локальных переменных с одинаковыми значениями, подумайте об их замене на одну глобальную переменную. Вот образец:
/* Избегайте создания множественных локальных переменных с одинаковым значением. */

$spacing: 10px; // глобальная переменная из файла variables.scss 

.accordion {
   $spacing-accordion: ($spacing * 2);
   padding: $spacing-accordion;
}

.card {
   $spacing-card: ($spacing * 2);
   padding: $spacing-card;
}

.banner {
   $spacing-card: ($spacing * 2);    
   padding: $spacing-banner;
}

/*-------------------------------------*/

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

$spacing: 10px;
$spacing-double: ($spacing * 2);

.accordion {
   padding: $spacing-double;
}

.card {
   padding: $spacing-double;
}

.banner {
   padding: $spacing-double;
}

/* Это новая переменная, которую можно использовать где угодно. */

.header {
   padding: $spacing $spacing-double;
}

.main {
   margin-top: $spacing-double;
}

Нарушайте правила: зачастую собственные миксины и плейсхолдеры бывают полезны

/* Миксины могут быть очень полезны, за счет освобождения разметки от излишних классов и установления конвенций, экономящих время.
Задание миксинам имен, соответствующим реализуемым в них свойствам CSS помогает создать ассоциацию между ними. */

/* Миксин для обрезки текста с многоточием */
@mixin text-ellipsis {
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
}

.selector {
   @include text-ellipsis;
}

Переменные: создаем их без фанатизма

Должны ли мы создавать много переменных только потому, что у нас есть такая возможность? Изучение объемного файла с переменными может быть мучительным.

Думайте перед созданием новой переменной

Нужно ли создавать новую переменную для базового цвета ваших кнопок (например, $button-color-base)? Нет, до тех пор пока у вас не появится отдельная причина сделать это, например, если вы пишете огромное приложение или фреймворк и хотите, чтобы у людей была возможность переписывать это значение (в Bootstrap 3-ей версии используется @btn-default-color в файле buttons.less). А так почему бы просто не использовать $color-brand-1 или другой имеющийся цвет для кнопок.

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

Рассмотрим это на примере. Базовый класс notify и дополнительные классы-модификаторы notify--success, notify--info и notify--error.

Почему бы не задать эти цвета в разделе для цветов файла с переменными и не добавить их потом к классам notify?

/* Заданы в разделе цветов файла с переменными */  
$green-pista: #93c572;
$blue-ink: #000f55;
$red-velvet: #d22836;

/* Используются в другом файле */
.notify {
   background-color: $blue-ink; //set a default color
   /* more base styles here */
}

.notify--success {
   background-color: $green-olive;
}

.notify--info {
   background-color: $blue-ink;
}

.notify--error {
   background-color: $red-velvet;
}

Но если у вас много модулей интерфейса, использующих эти состояния (например, кнопки и подсказки), будет логичней объявить переменные следующим образом:

/* Заданы в разделе цветов файла с переменными */
$green-pista: #93c572;
$blue-ink: #000f55;
$red-velvet: #d22836;

$color-state-success: $green-pista;
$color-state-info: $blue-ink;
$color-state-error: $red-velvet;

/* Используются в других файлах */
.notify--success {
   background-color: $color-state-success;
}

.tooltip--success {
   background-color: $color-state-success;
}

При использовании переменной подумайте, может вам хватит нативных CSS-значений?

В CSS есть два важных значения, inherit и currentColor, которые могут использоваться для расширения каскада. Значение currentColor использует унаследованное значение цвета от ближайшего предка, у которого оно задано и может использоваться со всеми свойствами принимающими значение цвета, включая границы и свойства SVG.

Примечание: значение inherit работает во всех браузерах, а currentColor во всех браузерах, кроме Internet Explorer 8-. В Safari есть баг с currentColor.

Ниже показан общий случай использования ключевого слова inherit:

See the Pen DRY: Use the CSS inherit keyword by Karen Menezes (@imohkay) on CodePen.

В следующем примере в псевдоэлементе (треугольник) используется currentColor:

See the Pen DRY: Use the CSS currentcolor keyword by Karen Menezes (@imohkay) on CodePen.

Неправильное повторное использование переменных

Может показаться заманчивым использовать $height-input в модуле интерфейса, никак не связанном с полями ввода. Не стоит этого делать — с препроцессорами законы кармы очень быстро настигнут вас.

/* Высота поля ввода задана в файле с переменными */
$height-input: 50px;

/* Лучше лишний раз повториться, чем неправильно использовать переменные */
.image-thumbnail {
   width: $height-input;
   height: $height-input;
}

Способы именования переменных

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

  • Обеспечивайте согласованность иерархии при именовании (например, $font-primary, $heading-primary или же $font-family-1, $heading-1 и т.д. ).
  • Используйте суффиксы base или default там, где это имеет смысл (например, $color-font-base, $box-shadow-base и т.д.).
  • Группируйте схожие переменные с помощью общих ключевых слов ($font-family-1, $font-size-base, $font-bold и т.д.).

Есть несколько способов, которые можно выбрать для именования переменных. Предположим, у вас типовой проект с шрифтами Roboto (без засечек) и Roboto Slab (с засечками). На этом примере со шрифтами мы и рассмотрим способы именования в следующих разделах.

Более описательный, но с ограничениями

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

$roboto:'Roboto', sans-serif;        // body copy
$roboto-slab: 'Roboto Slab', serif;  // headings

body {
   font-family: $roboto;  
}

h1, h2, h3, h4, h5, h6 {
   font-family: $roboto-slab;
}

Просто описательный, с меньшими ограничениями

Он кажется интуитивным, потому что вы можете поменять значения для шрифтов с засечками и без засечек без дополнительных усилий (т.е. просто заменить Roboto на San Francisco или другой шрифт без засечек). Однако переменная $font-serif потеряет свой смысл, если в будущем нее поместить шрифт без засечек.

$font-sans-serif: 'Roboto', sans-serif;
$font-serif: 'Roboto Slab', serif;

body {
   font-family: $font-sans-serif;
}

h1, h2, h3, h4, h5, h6 {
   font-family: $font-serif;
}

Скорее сравнительный, чем описательный

Смысл этого метода в создании иерархии шрифтов в вашем проекте. В данном случае пример немного вымученный, и переменные типа $font-quaternary и $font-quinary могут слегка выжрать вам мозг. Конечно, всегда надо стремиться использовать минимальное количество кастомных шрифтов, но в проекте может произойти и не такое.

$font-primary: 'Roboto', sans-serif;        // body copy
$font-secondary: 'Roboto Slab', serif;      // headings
$font-tertiary: 'Cutive Mono', monospace;   // monospace for code blocks
$font-quaternary: 'Open Sans Condensed', sans-serif; // navigation in header

body {
   font-family: $font-primary;
}

h1, h2, h3, h4, h5, h6 {
   font-family: $font-secondary;
}

nav {
   font-family: $font-tertiary;
}

Немного описательный, немного сравнительный

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

$font-family-1: 'Roboto', sans-serif;       // body copy
$font-family-2: 'Roboto Slab', serif;      // headings

body {
   font-family: $font-family-1;
}

h1, h2, h3, h4, h5, h6 {
   font-family: $font-family-2;
}

Распространенные переменные для веб-приложения

Ниже перечислены некоторые переменные, которые я обычно использую, сгруппированные по логическим секциям.

Переменные для измерений

Названа ли переменная, исходя из ее связи с определенным элементом, даже если она влияет на другие элементы? Возьмем наш прошлый пример с высотой хедера — ее имя привязано к селектору (например, $header-height или лучше $height-header).

Я предпочитаю $height-header вместо $header-height, потому как это значение высоты хедера, которое используется для задания высоты и внутренних отступов у других элементов.

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

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

/*-------------------------------------*/
// WIDTHS AND HEIGHT

// Header: Высота
$height-header: 60px; // or em
/* может также использоваться для задания высоты, внутреннего отступа и позиционирования других элементов */

// Off-Canvas Navigation: Ширина
$width-nav: 250px;
/* может использоваться для позиционирования меню с помощью отрицательного отступа */

// Text Inputs: Высота
$height-form-widget: 45px;
/* может использоваться для селектов и кнопок отправки форм. */

Переменные для пространства: внутренние и внешние отступы

Я обычно создаю одну базовую переменную $spacing и использую ее для внутренних и внешних отступов.

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

С другой стороны, вы можете использовать и более специфичные переменные типа $spacing-padding и $spacing-margin.

Избегайте использования переменных для пространства с абсолютно позиционированными элементами. Изменение глобальных значений переменных будет влиять на эти элементы.

$spacing: 10px;                  
$spacing-half: ($spacing/ 2);
$spacing-double: ($spacing* 2);
$spacing-third: ($spacing* 3);

Переменные для цвета

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

Я нахожу эту систему элегантной и выразительной. Но если вас тянет к именам типа “grey-lightest-xx” (или еще хуже “grey-darker–1x”), можете работать и с такими именами.

Я также разделяю несколько брендовых цветов, особенно для больших приложений. С двумя брендовыми цветами (например с бирюзовым и красно-коралловым) я использую имена переменных $color-brand-1 и $color-brand-2. Такие вещи я обычно обсуждаю с другими разработчиками, участвующими в проекте: некоторые из них предпочтут названия типа $red, но возможность легко изменить брендовый цвет их убеждает. Кроме того, кто-то может привести в качестве контраргумента то, что в большом проекте должно быть руководство по стилю, которое устраняет подобные проблемы. На самом деле при работе над руководством по стилю я предпочитаю не ставить значения в HEX, RG или HSL, а использовать имена переменных.

/* Colors: Brand */
$color-brand-1: #008080;  // blue teal - $color-brand-blue is an OK compromise
$color-brand-2: #ff7f50;  // orange coral

/* Colors: Common: Grey */
$grey-mist: #f5f5f5;
$grey-platinum: #bbb;
$grey-gravel: #444;

/* Colors: Common: Cool */
$green-pista: #93c572;
$green-mint: #98ff98;

/* Colors: Common: Warm */
$blue-cornflour: #659cef;
$blue-turquoise: #00c5cd;

/* Colors: Brands for Social Media */
$blue-facebook: #3b5998;
$blue-twitter: #55acee;
$red-youtube: #cd201f;

/* Colors: Links */
$color-link: $color-brand-1;
$color-link-hover: darken($link-color, 15%);

Переменные для z-index

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

Крис Койер предлагает использовать переменные для z-index, создавая новый файл Sass или использовать ассоциативный массив Sass. А Хьюго Жирадель предлагает решение с использованием функции и карт Sass.

Меня устраивает помещение переменных для z-index в файл _variables.scss.

Учитывайте, что плагины от сторонних производителей часто используют z-index. Я создаю для них новые переменные, например:

$zindex-header: 1000;
$zindex-dropdown: 1100;
$zindex-lib-select2: 1200;  // plugin: Select2    
$zindex-notify: 1300;
$zindex-modal: 1400;

Переменные для border-radius

Для всех приложений мне хватало трех размеров скругленных уголков:

$border-radius: 5px;                              
$border-radius-half: $border-radius / 2;     
$border-radius-double: $border-radius * 2;

Ранее я использовала переменную $border-radius-circle со значением 50%, пока здравый смыл не подсказал мне, что это пустая трата усилий. Вы всегда может нарисовать круг с помощью border-radius: 50% — просто напишите border-radius: 50% в своих стилях.

Переменные для типографики

При их именовании полезно использовать префикс font-.

Вот несколько переменных, которые я использую для шрифтов:

$font-family-1: 'Roboto', sans-serif;
$font-family-2: 'Playfair Display', sans-serif;
$font-size-base: 100%; // or px / em / rem

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

Жирность шрифта это несколько иное. Вот скриншот с Google Fonts с различными вариантами жирности шрифта Exo:

Шрифт Exo различной жирности

Шрифт Exo различной жирности. (увеличенная версия)

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

$font-thin: 100;
$font-extra-light: 200;
$font-light: 300;
$font-normal: 400;
$font-medium: 500;
$font-semi-bold: 600;
$font-bold: 700;
$font-extra-bold: 800;
$font-ultra-bold: 900;

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

Взглянем на пример со шрифтом Amble, сгенерированный с помощью сервиса Font Squirrel. Мы используем только обычное и жирное начертание и только формат WOFF, поддерживаемый в современных браузерах.

/* Это CSS сгенерированный Font Squirrel, добавленный к файлу с типографикой. Ссылки на форматы EOT, SVG и TTF удалены. */

@font-face {
   font-family: 'ambleregular';
   src: url('Amble-Regular-webfont.woff') format('woff');
   font-weight: normal;
   font-style: normal;
}

@font-face {
   font-family: 'amblebold';
   src: url('Amble-Bold-webfont.woff') format('woff');
   font-weight: normal;
   font-style: normal;
}

/* Мы пойдем дальше и создадим для этого две переменные: */
$font-family-1-regular: 'ambleregular', sans-serif; // or $font-amble-regular;
$font-family-1-bold: 'amblebold', sans-serif;

/*
И, возможно, еще переменные для italic, italic + bold:
$font-family-1-regular-italic: 'ambleitalic', sans-serif;
$font-family-1-bold-italic: 'amblebold_italic', sans-serif;
*/

/* Ситуация ухудшается если у нас используется несколько шрифтов. */

/*
$font-family-1-regular: ...;
$font-family-1-regular-italic: ...;
$font-family-1-bold: ...;
$font-family-1-bold-italic: ...;
$font-family-2-regular: ...;
$font-family-2-regular-italic: ...;
$font-family-2-bold: ...;
$font-family-2-bold-italic: ...;
*/

/* Нам также надо сбросить дефолтные стили для тегов <em>, <i>, <strong>
и <bold>. */

/*
strong, bold {
   font-family: $font-family-1-bold;
   font-weight: normal;
   font-style: normal;
}

em, i {
   font-family: $font-family-1-regular-italic;
   font-style: normal;
   font-weight: normal;
}
*/

/* Кажется, что-то пошло не так. */

Давайте рассмотрим решение по работе с кастомными шрифтами, в котором нет излишней абстракции:

See the Pen Sass variables with the @font-face rule by Karen Menezes (@imohkay) on CodePen.

Переменные для иконочных шрифтов

$font-icon: 'icomoon'; /* This allows you to swap out for another
icon font family (for example, Font Awesome) without having to update in multiple
places. */

Переменные для медиа-запросов

/* Создайте в файле с переменными */

$media-lg: 1200px; // or em
$media-mid: 740px;
$media-sm: 600px;

/* Добавьте в файл с сеткой или в иной файл */

// Media query mixin
@mixin media($media) {
   @media only screen and (min-width: $media) {
      @content;
   }
}

// Используйте

.selector {
   width: 25%;

   @include media($media-sm) {
      width: 50%;
   }

   @include media($media-lg) {
      width: auto;
   }
}

/* Если вам нужны еще медиа-запросы, добавьте их сразу в файл с переменными. */

Для приложений со сложными контрольными точками, стоит использовать библиотеку include-media, написанную Эдуардо Букасом и Хьюго Жираделем.

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

/*-------------------------------------*/
// VARIABLES: GLOBAL
/*-------------------------------------*/

/*-------------------------------------*/
// GRID

// Grid: Gutters
$grid-gutter: 15px;

/*-------------------------------------*/
// MEDIA QUERIES

$media-lg: 1200px;
$media-mid: 900px;
$media-sm: 600px;

/*-------------------------------------*/
// DIMENSIONS: WIDTHS, HEIGHTS

// Header: Height
$height-header: 45px;

// Nav Menu: Width
$width-nav: 280px;

// Inputs: Height
$height-form-widget: 60px;

/*-------------------------------------*/
// COLORS

// Colors: Brand
$color-brand-1: #7dcfb8;

// Colors: Common: Greys
$grey-mist: #f5f5f5;
$grey-platinum: #bbb;
$grey-cement: #8f8f8f;
$grey-gravel: #444;

// Colors: Common: Cool
$blue-ink: #000f55;
$green-pista: #93c572;

// Colors: Common: Warm
$red-hibiscus: #f0214f;
$orange-marigold: #ff753b;

// Colors: Brands for Social Media
$blue-facebook: #3b5998;
$blue-twitter: #55acee;
$blue-vimeo: #1ab7ea;
$red-youtube: #cd201f;

// Colors: Links
$color-link: $color-brand-1;
$color-link-hover: darken($color-link, 12%);

// Colors: Base Font
$color-font-base: $grey-gravel;

/*-------------------------------------*/
// FONTS

// Fonts: Base styles
$font-family-1: Roboto, sans-serif;
$font-size-base: 18px;

// Fonts: Weights
$font-light: 300;
$font-normal: 400;
$font-bold: 700;

// Fonts: Sizes
$font-sm: 90%;
$font-sm-x: 80%;
$font-lg: 110%;
$font-lg-x: 120%;

/*-------------------------------------*/
// FONT-ICONS: FAMILY

$font-icon: 'icomoon';

/*-------------------------------------*/
// SPACING

$spacing: 10px;                
$spacing-double: $spacing * 2;   
$spacing-third: $spacing * 3;    
$spacing-half: $spacing / 2;

/*-------------------------------------*/
// Z:INDEX

$zindex-header: 1000;
$zindex-dropdown: 1100;
$zindex-lib-select2: 1200;  // plugin: Select2
$zindex-notify: 1300;
$zindex-modal: 1400;

Нативные переменные CSS

Они еще не вполне годны для использования на продакшене (на данный момент их статус кандидат в рекомендации), но переменные CSS это шаг в нужном направлении, несмотря на их слегка многословный синтаксис.

Учитывайте, что когда мы используем термин “переменные CSS”, мы ссылаемся на пользовательские свойства, которые определяют переменные, через ссылки с помощью нотации var(). Синтаксис для пользовательских свойств может измениться в будущем.

Пользовательские свойства на данный момент поддерживаются только в Firefox, в Chrome они находятся на стадии разработки, а в Edge на стадии рассмотрения.

Ниже пример с использованием пользовательских свойств CSS:

div {
   --color-brand-1: teal;       /* Prepend custom property with -- */       
   color: var(--color-brand-1); /* Then reference with var() notation */
}

А вот как мы можем определить пользовательские свойства, которые можно использовать глобально:

/* Мы можем использовать псевдо-селектор :root pseudo в качестве решения.
   Фактически, в документе HTML у него более высокая специфичность, чем у элемента <html>! */

   :root {
      --color-brand-1: teal;
      --font-size-base: 18px;
   }  

  /* Используйте */
   .ui-module {
      color: var(--color-brand-1);
      font-size: var(--font-size-base);
   }

Пользовательские свойства можно каскадировать, вы можете проверить это на демонстрации (работает только в Firefox):

Вот некоторые из преимуществ нативных переменных перед переменными из препроцесоров:

  • В статье Питера Гастона обсуждается, как пользовательские свойства CSS могут быть доступны с помощью JavaScript (метод setProperty() в объекте стилей). Вы можете поэкспериментировать с его демонстрацией в Firefox.
  • Переменные CSS можно использовать непосредственно внутри SVG для стилизации свойств SVG через презентационные атрибуты типа fill и stroke. Это можно посмотреть в демо, разумеется, пока что только в Firefox.

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

Ресурсы

  • Rethinking Your Sass Variables,” Stuart Robson Робсон предлагает альтернативный метод в сравнении с предложенным в данной статье — а именно разделение “глобальных” и “компонентных” переменных на разные файлы.
  • Stop Using So Many Sass Variables,” Ben Smithett Смитетт показывает способ уменьшения абстракции и именования переменных в более общей манере. Хотя это нельзя рассматривать в качестве лучшей практики, в статье есть несколько интересных идей, также стоит взглянуть и на комментарии.
  • Simplicity in Front-End Tooling,” Hans Christian Reinl Райнл обсуждает “анти-тренд по удалению сложной структуры и инструменты для простого выполнения работы”.
  • Variables Across CSS Preprocessors,” Alexander Futekov Футеков рассматривает синтаксис и возможности переменных в Sass, LESS и Stylus, включая “лениво загружаемые” переменные и значения по умолчанию.
  • Using Sass for Theming,” Tim Hartmann Это всеобъемлющая статья по темизации в Sass на основе центричности переменных. Хартманн также использует интересную технику создания переменных для хранения ссылок на изображения.
  • CSS Custom Properties for Cascading Variables Module Level 1,” W3C В текст спецификации поможет точнее понять нюансы нативных переменных CSS. Над этим модулем будет проведено еще много работы.
  • Use Cases for CSS Variables,” James Steinbach Штейнбах рассматривает уникальные случаи, в которых обычные переменные оказываются эффективнее переменных препроцессоров.