Sass Guidelines

Руководство по написанию разумного, поддерживаемого и масштабируемого Sass.

You are viewing the Russian translation by Даниил Пронин, Pavel Demyanenko and Tim Arbaev of the original Sass Guidelines from Kitty Giraudel.

This version is exclusively maintained by contributors without the review of the main author, therefore might not be completely authentic.

Открыть панель опций

Об авторе

Меня зовут Kitty Giraudel, я frontend-разработчик из Франции, проживаю в Берлине (Германия) с 2015 года, сейчас работаю в Cofenster.

Я пишу на Sass на протяжении нескольких лет и являюсь автором множества Sass-проектов, таких как SassDoc, SitePoint Sass Reference и Sass-Compatibility. Если вы хотите узнать больше о моем вкладе в Sass сообщество, то ознакомьтесь с этим списком.

Я также являюсь автором книги о CSS (на французском) CSS3 Pratique du Design Web (Eyrolles editions) и книги о Sass (на английском) Jump Start Sass (Learnable editions).

Сотрудничество

Sass Guidelines – открытый проект, которым я руковожу в свободное время. Излишне говорить, что это довольно большой объем работы – держать всё задокументированным в последней версии. К счастью, мне помогает множество участников, особенно когда речь заходит о поддержании десятков разных переводов. Обязательно поблагодарите их!

Теперь, если вы чувстствуете, что готовы к сотрудничеству – пожалуйста, было бы здорово твитнуть, рассказать об этом или открыть Pull Request с исправлением ошибок в репозитории на GitHub!

Прежде, чем мы начнём: если вам понравился этот документ, или он оказался полезен вам или вашей команде, пожалуйста, подумайте о его поддержке, чтобы я мог продолжать работу над ним!

О Sass

Вот так Sass описывает сам себя в своей документации:

Sass — это расширение CSS, которое добавляет силу и элегантность к основному языку.

Основая цель Sass — исправить недостатки CSS. Как мы все знаем, это не самый лучший язык в мире [указать источник]. Являясь довольно простым для освоения, он может быстро стать запутанным, особенно на больших проектах.

Вот тут-то и вступает в свою роль Sass как метаязык, который улучшает синтаксис CSS, чтобы предоставить дополнительные возможности и удобные инструменты. Между тем, Sass остаётся консервативным в отношении языка CSS.

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

Есть много способов использовать эти возможности. Какие-то способы хороши, какие-то – не очень, а некоторые вообще необычные. Это руководство предназначено для того, чтобы дать последовательный и документированный подход для написания кода на Sass.

Ruby Sass или LibSass

Первый коммит в Sass был сделан в конце 2006, более 10 лет назад. Излишне говорить, что с тех пор проект прошёл долгий путь. Изначально разработанный на Ruby, он получил множество портов. Самый популяный – LibSass (написан на С/C++), сейчас близок к полной совместимости с оригинальной Ruby-версией.

В 2014 году команды Ruby Sass и LibSass решили подождать синхронизации версий, прежде чем двигаться дальше. С тех пор LibSass активно выпускает версии, чтобы догнать старшего брата. Последние оставшиеся несоответствия собраны и перечислены мной в проекте Sass-Compatibility. Если вы знаете о несовместимости между этими двумя версиями, которой нет в списке, пожалуйста, создайте соответствующий issue.

Вернёмся к выбору компилятора. На самом деле, всё зависит от вашего проекта. Если это Ruby on Rails, вам лучше использовать Ruby Sass, который идеально подходит для такого случая. Также следует помнить, что Ruby Sass — это эталонная реализация и всегда будет обгонять LibSass. А если вы хотите перейти c Ruby Sass на LibSass, то эта статья для вас.

В не-Ruby проектах, LibSass, вероятно, будет лучше, поскольку он в основном предназначем для интеграции. Так что, если вы хотите использовать, скажем, Node.js, node-sass — ваш выбор.

Sass или SCSS

Существует довольно много путаницы относительно семантики имени Sass, и не зря: Sass означает как препроцессор, так и свой собственный синтаксис. Не очень удобно, не так ли?

Как видите, Sass первоначально описал синтаксис, определяющей характеристикой которого является его чувствительность к вложености. Вскоре в Sass решили сократить разрыв между Sass и CSS, реализовав CSS-подобный синтаксис под названием SCSS или Sassy CSS. Девиз: “если это правильный CSS, то это правильный SCSS”.

С тех пор, Sass (препроцессор) предоставляет два различных синтаксиса: Sass (не все буквы заглавные, пожалуйста), также известный как синтаксис с отступами, и SCSS. Какой из них использовать, в значительной степени зависит от вас, так как оба строго равны по возможностям. Это лишь вопрос эстетики.

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

Лично я предпочитаю SCSS вместо Sass, потому что он близок к CSS и более дружелюбен для большинства разработчиков. Поэтому я буду использовать SCSS в этом руководстве. Вы можете переключиться на Sass-синтаксис .

Другие препроцессоры

Sass — это препроцессор, коих много. Самый серьёзный соперник — это Less, написанный на Node.js, который стал весьма популярен благодаря CSS-фреймворку Bootstrap (до 4-й версии). Также есть Stylus, который довольно много позволяет, но несколько сложнее в использовании, и к тому же имеет меньшее сообщество.

Почему стоит выбрать Sass, а не другой препроцессор? — действительно, актуальный вопрос. Не так давно мы рекомендовали использовать Sass для проектов на Ruby, потому что Sass был создан в Ruby и хорошо работает с Ruby on Rails. Теперь, когда LibSass догнал (в основном) оригинальный Sass, это уже не актуальный совет.

То, что мне нравится в Sass, так это его консервативный подход к CSS. Дизайн Sass основан на строгих принципах: большая часть проектного подхода приходит из мнений команды о том, что: а) добавление новых возможностей не должно вносить существенного усложнения, но быть адекватно полезным, и б) должно быть достаточно беглого взгляда на блок стилей, чтобы понять, что он делает. Кроме того, Sass более внимательно относится к деталям, чем другие препроцессоры. Насколько я могу судить, ведущие разработчики уделяют много внимания обеспечению совместимости с CSS и тщательно следят, чтобы поведение было предсказуемым. Иначе говоря, Sass – это программное обеспечение, предназначенное для решения актуальных задач; привносит полезную функциональность в CSS там, где CSS не справляется.

Кроме препроцессоров, стоит также упомянуть такие инструменты, как PostCSS и cssnext, которые получили значительное внимание в течение последних нескольких месяцев.

PostCSS обычно (и неправильно) называют «постпроцессором». Предполагаю, что это связано с не совсем удачным названием, а так же тем, что PostCSS парсит CSS, который уже был обработан препроцессором. Хоть PostCSS и можно так использовать, но это не обязательно, так что это скорее просто «процессор».

Он позволяет получать доступ к «токенам» таблиц стилей (таким, как селекторы, свойства и значения), обрабатывать их с помощью JavaScript, выполнять какие-либо операций и компилировать результаты в CSS. Например, популярная библиотека Autoprefixer работает на основе PostCSS. Она парсит каждое правило, проверяя нужны ли вендорные префиксы, сверяясь с базой CanIUse, после чего удаляет или добавляет необходимые префиксы.

Это невероятно мощный инструмент для создания библиотек, которые работают с любым препроцессором (а также ванильным CSS), но PostCSS не так прост в использовании. Для работы с ним нужно знать JavaScript, а API иногда может сбить с толку. В то время как Sass предоставляет лишь набор полезных возможностей для написания CSS, PostCSS предоставляет прямой доступ к CSS AST (абстрактное синтаксическое дерево) и JavaScript.

Короче говоря, Sass проще и может решить большинство ваших задач. С другой стороны, с PostCSS сложнее совладать (если вы не достаточно хорошо разбираетесь в JavaScript), но он невероятно мощный. Нет причин, чтобы не использовать и то и другое. PostCSS даже предоставляет для этого официальный SCSS-парсер.

Спасибо Cory Simmons за помощь в работе над этим разделом.

Введение

Почему стайлгайд

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

Излишне говорить, что чем больше разработчиков участвуют в проекте, тем больше руководств требуется. Так же, чем больше проект, тем более важен стайлгайд.

Гарри Робертс очень хорошо об этом говорит в CSS Guidelines:

Стайлгайд (заметьте, не визуальный) является ценным инструментом для команд, которые:

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

Дисклеймер

Во-первых: это не CSS-стайлгайд. В этом документе не будут обсуждаться именование CSS-классов, модульный подход и вопрос об ID в мире CSS. Это руководство направлено только на работу с Sass.

Кроме того, это мой личный стайлгайд, и поэтому он весьма субъективен. Думайте об этом, как о наборе методологий и советов, которые я улучшал на протяжении многих лет. Это также даёт мне возможность поделиться ссылками на полезные ресурсы, так что не забудьте проверять разделы дальнейшего чтения.

Очевидно, что данный подход не единственный, может подойдет к вашему проекту, а может и нет. Не стесняйтесь брать отсюда код и подстраивать его под себя. Как говорят у нас, ваш пробег может отличаться (в том смысле, что в вашей ситуации может быть по-другому).

Ключевые принципы

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

Спасибо моим глупым эскпериментам, таким как битовые операции, итераторы и генераторы и парсер JSON на Sass, и теперь мы все хорошо знаем что можно сделать с этим препроцессором.

Между тем, CSS является простым языком. Sass, предназначенный для написания CSS, не должен стать значительно сложнее обычного CSS. Принцип KISS (Keep It Simple Stupid, делай это проще, тупица) является ключевым моментом и в некоторых случаях может даже взять верх над принципом DRY (Don’t Repeat Yourself, не повторяйся).

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

Кроме того, позвольте мне еще раз процитировать Гарри Робертса, прагматизм – козырь совершенства. В какой-то момент, вы, вероятно, пойдёте против правил, описанных здесь. Если это имеет смысл — сделайте это. Код – просто средство, а не цель.

Расширение стайлгайда

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

Несмотря на это, я считаю, что руководство следует дополнять. Расширить Sass Guideliness довольно просто: достаточно иметь документ, в котором указано, что код следует данному стайлгайду за исключением некоторых моментов, которые перечисляются ниже.

Пример расширения стайлгайда можно найти в репозитории SassDoc:

Это расширение для Node Styleguide от Felix Geisendörfer. Все, что определяет этот документ, описано в Node Styleguide.

Синтаксис и форматирование

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

Когда несколько разработчиков участвуют в написании CSS проекта(ов), то лишь вопрос времени, когда один из них начнёт делать вещи по-своему. Руководство стилей способствуют не только в согласованности, но и помогает, когда приходит время читать и обновлять код.

Грубо говоря, мы хотим (бесстыдно вдохновлён CSS Guidelines):

  • двойные (2) отступы пробелом, никаких табов;
  • в идеале, 80-символьную ширину строк;
  • правильно написанные многострочные CSS правила;
  • осмысленное использование пробелов.
// Yep
.foo {
  display: block;
  overflow: hidden;
  padding: 0 1em;
}

// Nope
.foo {
    display: block; overflow: hidden;

    padding: 0 1em;
}
// Так как Sass использует отступозависивый синтаксис,
// то и не существует неправильного способа форматирования
.foo
  display: block
  overflow: hidden
  padding: 0 1em

Строки

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

Кодировка

Чтобы избежать потенциальных проблем с кодировкой символов, крайне рекомендуется использовать кодировку UTF-8 в основной таблице стилей, используя директиву @charset. Убедитесь, что она идёт первой строкой в таблице стилей и перед ней ничего нет.

@charset 'utf-8';
@charset 'utf-8'

Кавычки

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

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

Языки, которые не требуют, чтобы строки были в кавычках, определенно, в меньшинстве, так что строки должны всегда быть обёрнуты в одинарные кавычки в Sass (одну проще набрать, чем двойную, на QWERTY-клавиатуре). Кроме того, для согласованности с другими языками, в том числе с двоюродным братом CSS – JavaScript’ом, есть несколько причин для такого выбора:

  • названия цветов рассматриваются как цвета, когда без кавычек, что может привести к серьёзным проблемам;
  • большинство синтаксических анализаторов будут в шоке от строк без кавычек;
  • это помогает общей читаемости;
  • нет обоснованной причины не оборачивать строки в кавычки.
// Yep
$direction: 'left';

// Nope
$direction: left;
// Yep
$direction: 'left'

// Nope
$direction: left

Как указано в спецификации CSS, директива @charset должна быть объявлена в двойных кавычках чтобы считаться валидной. Однако, Sass позаботится об этом во время компилирования, так что финальный результат будет всегда правильный. Можете спокойно использовать одинарные кавычки даже для @charset.

Строки как значения CSS

Специальные значения CSS (идентификаторы), такие как initial или sans-serif требует не быть закавыченными. В самом деле, объявление font-family: 'sans-serif' потерпит неудачу, потому что CSS ожидает идентификатор, а не строку с кавычками. Именно поэтому, мы не оборачиваем в кавычки эти значения.

// Yep
$font-type: sans-serif;

// Nope
$font-type: 'sans-serif';

// Okay I guess
$font-type: unquote('sans-serif');
// Yep
$font-type: sans-serif

// Nope
$font-type: 'sans-serif'

// Okay I guess
$font-type: unquote('sans-serif')

Следовательно, можно провести различие между строками, которые должны использоваться как значения CSS (идентификаторы CSS), как в предыдущем примере, и строками, которые привязаны к типу данных Sass, например, к ключам мапы.

Первое без кавычек, а второе в одинарных кавычках.

Строки, содержащие кавычки

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

// Okay
@warn 'You can\'t do that.';

// Okay
@warn "You can't do that.";
// Okay
@warn 'You can\'t do that.'

// Okay
@warn "You can't do that."

УРЛы

URL тоже должны быть в кавычках, по тем же причинам, что и выше:

// Yep
.foo {
  background-image: url('/images/kittens.jpg');
}

// Nope
.foo {
  background-image: url(/images/kittens.jpg);
}
// Yep
.foo
  background-image: url('/images/kittens.jpg')

// Nope
.foo
  background-image: url(/images/kittens.jpg)

Числа

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

Нули

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

// Yep
.foo {
  padding: 2em;
  opacity: 0.5;
}

// Nope
.foo {
  padding: 2.0em;
  opacity: .5;
}
// Yep
.foo
  padding: 2em
  opacity: 0.5

// Nope
.foo
  padding: 2.0em
  opacity: .5

В Sublime Text и других редакторах с поддержкой поиска и замены регулярными выражениями очень легко добавить незначащий нуль в начало (большинства, если не всех) чисел с плавающей точкой. Просто замените \s+\.(\d+) на \ 0.$1. Обратите внимание на пробел перед 0.

Единицы измерения

При работе с длинами, 0 (нуль) никогда не должен иметь единицу измерения.

// Yep
$length: 0;

// Nope
$length: 0em;
// Yep
$length: 0

// Nope
$length: 0em

Внимательно, эта практика относится только к длинам. Нуль без единиц измерения для свойств времени, таких как transition-delay не допускается. Теоретически, если нуль без единиц измерения указан для продолжительности, это объявление признаётся недействительным и отбрасывается. Не все браузеры столько строги, но некоторые да. Короче говоря: опускать единицу измерения только для длин.

Самая распространённая ошибка о числах в Sass, которую можно только представить – это думать, что единицы измерения – всего лишь строки, которые можно запросто прилагать к числу. Хотя это и звучит как правда, но единицы измерения работают совсем не так. Думайте о единицах измерения в контексте алгебраических символов. Например, в реальном мире, умножение 5 дюймов на 5 дюймов даст вам 25 квадратных дюймов. Та же логика применима и к Sass.

Чтобы добавить единицу измерения в число, нужно умножить это число на 1 единицу измерения.

$value: 42;

// Yep
$length: $value * 1px;

// Nope
$length: $value + px;
$value: 42

// Yep
$length: $value * 1px

// Nope
$length: $value + px

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

$value: 42 + 0px;
// -> 42px

$value: 1in + 0px;
// -> 1in

$value: 0px + 1in;
// -> 96px
$value: 42 + 0px
// -> 42px

$value: 1in + 0px
// -> 1in

$value: 0px + 1in
// -> 96px

В конце концов, всё зависит от того, чего вы пытаетесь достичь. Просто имейте в виду, что добавление единицы измерения как строки – не лучшее решение.

Чтобы убрать единицу измерения из значения, нужно разделить его на одну единицу этой же меры.

$length: 42px;

// Yep
$value: $length / 1px;

// Nope
$value: str-slice($length + unquote(''), 1, 2);
$length: 42px

// Yep
$value: $length / 1px

// Nope
$value: str-slice($length + unquote(''), 1, 2)

Добавляя единицу измерения как строку в число превращает всё в строку, препятствуя любым дополнительным операциям над значением. Разделение числа и единицы измерения тоже возвращает строку. Это не то, чего вы хотите. Используйте длины, не строки.

Вычисления

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

// Yep
.foo {
  width: (100% / 3);
}

// Nope
.foo {
  width: 100% / 3;
}
// Yep
.foo
  width: (100% / 3)

// Nope
.foo
  width: 100% / 3

Магические числа

“Магическое число” - это термин старой школы программирования для неименованных числовых констант. В принципе, это просто случайное число, которое просто работает™ и ещё не привязано к какому-либо логическому объяснению.

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

/**
 * 1. Магическое число. Это минимальное число, которое я смог найти, чтобы выровнять верх
 * `.foo` с его предком. В идеале мы должны исправить это по-правильному.
 */
.foo {
  top: 0.327em; /* 1 */
}
/**
 * 1. Магическое число. Это минимальное число, которое я смог найти, чтобы выровнять верх
 * `.foo` с его предком. В идеале мы должны исправить это по-правильному.
 */
.foo
  top: 0.327em /* 1 */

По теме у CSS-Tricks есть потрясающая статья о магических числах в CSS, которую я призываю вас прочитать.

Цвета

Цвета занимают важное место в языке CSS. Естественно, Sass является ценным союзником, когда дело доходит до управления цветами, в основном – путём предоставления мощных функций.

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

Цветовые форматы

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

  1. Обозначение HSL;
  2. Обозначение RGB;
  3. Шестнадцатеричная нотация. Предпочтительно в нижнем регистре и по возможности укороченная.

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

Представление HSL – не только самое простое для человеческого мозга [указать источник], но у него и самый лёгкий способ настройки цвета путём регулировки цветового тона, насыщенности и яркости индивидуально.

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

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

// Yep
.foo {
  color: hsl(0, 100%, 50%);
}

// Also yep
.foo {
  color: rgb(255, 0, 0);
}

// Meh
.foo {
  color: #f00;
}

// Nope
.foo {
  color: #FF0000;
}

// Nope
.foo {
  color: red;
}
.foo
  color: hsl(0, 100%, 50%)

// Also yep
.foo
  color: rgb(255, 0, 0)

// Nope
.foo
  color: #f00

// Nope
.foo
  color: #FF0000

// Nope
.foo
  color: red

При использовании обозначений HSL или RGB, всегда пишите один пробел после запятой (,) и не ставьте пробел между скобками ((, )) и содержимым.

// Yep
.foo {
  color: rgba(0, 0, 0, 0.1);
  background: hsl(300, 100%, 100%);
}

// Nope
.foo {
  color: rgba(0,0,0,0.1);
  background: hsl( 300, 100%, 100% );
}
// Yep
.foo
  color: rgba(0, 0, 0, 0.1)
  background: hsl(300, 100%, 100%)

// Nope
.foo
  color: rgba(0,0,0,0.1)
  background: hsl( 300, 100%, 100% )

Цвета и переменные

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

$sass-pink: hsl(330, 50%, 60%);
$sass-pink: hsl(330, 50%, 60%)

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

$main-theme-color: $sass-pink;
$main-theme-color: $sass-pink

Так вы воспрепятствуете изменениям темы, ведущим к чему-то вроде $sass-pink: blue. Эта статья здорово объясняет, почему важно учитывать ваши цветовые переменные.

Осветление и затемнение цветов

Обе функции lighten и darken манипулируют цветами пространства HSL, добавляя или вычитая в пространстве HSL. В принципе, они ни что иное, как алиасы для параметра $lightness в функции adjust-color.

Дело в том, что эта функция часто не даёт ожидаемого результата. С другой стороны, функция mix является хорошим способом осветлить или затемнить цвет, смешивая его либо с white, либо с ` black`.

Преимуществом использования mix перед одной из двух указанных функций выше является то, что она будет постепенно меняться на чёрный (или белый), когда вы уменьшаете долю цвета, в то время как darken и lighten быстро меняют цвет на чёрный или белый.

Иллюстрация разницы между функциями lighten/darken и mix в Sass от KatieK
Иллюстрация разницы между функциями lighten/darken и mix в Sass от KatieK

Если вы не хотите писать функцию mix каждый раз, вы можете создать две простых в использовании функции tint и shade (которые также являются частью Compass), чтобы сделать то же самое:

/// Немного осветлить цвет
/// @access public
/// @param {Color} $color - цвет для осветления
/// @param {Number} $percentage - процент от `$color` в возвращаемом цвете
/// @return {Color}
@function tint($color, $percentage) {
  @return mix(white, $color, $percentage);
}

/// Немного затемнить цвет
/// @access public
/// @param {Color} $color - цвет для затемнения
/// @param {Number} $percentage - процент от `$color` в возвращаемом цвете
/// @return {Color}
@function shade($color, $percentage) {
  @return mix(black, $color, $percentage);
}
/// Немного осветлить цвет
/// @access public
/// @param {Color} $color - цвет для осветления
/// @param {Number} $percentage - процент от `$color` в возвращаемом цвете
/// @return {Color}
@function tint($color, $percentage)
  @return mix($color, white, $percentage)

/// Немного затемнить цвет
/// @access public
/// @param {Color} $color - цвет для затемнения
/// @param {Number} $percentage - процент от `$color` в возвращаемом цвете
/// @return {Color}
@function shade($color, $percentage)
  @return mix($color, black, $percentage)

Функция scale-color разработана, чтобы изменять свойства более плавно, принимая во внимание, насколько они уже изменены. Результат так же хорош, как и от mix, но с более удобным вызовом. Хотя, множитель масштабирования – не совсем то же самое.

Списки

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

Списки должны соблюдать следующие правила:

  • либо в одну строку, либо многострочный;
  • многострочный необходим, если не помещается в 80-символьную строку;
  • если используется для целей CSS, всегда разделяется запятой;
  • всегда оборачивается круглыми скобками;
  • в многострочном списке ставится запятая после последнего элемента.
// Yep
$font-stack: ('Helvetica', 'Arial', sans-serif);

// Yep
$font-stack: (
  'Helvetica',
  'Arial',
  sans-serif,
);

// Nope
$font-stack: 'Helvetica' 'Arial' sans-serif;

// Nope
$font-stack: 'Helvetica', 'Arial', sans-serif;

// Nope
$font-stack: ('Helvetica', 'Arial', sans-serif,);
// Yep
$font-stack: ('Helvetica', 'Arial', sans-serif)

// Nope (not supported)
$font-stack: (
  'Helvetica',
  'Arial',
  sans-serif,
)

// Nope
$font-stack: 'Helvetica' 'Arial' sans-serif

// Nope
$font-stack: 'Helvetica', 'Arial', sans-serif

// Nope
$font-stack: ('Helvetica', 'Arial', sans-serif,)

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

$shadows: (0 42px 13.37px hotpink);

// Yep
$shadows: append($shadows, $shadow, comma);

// Nope
$shadows: $shadows, $shadow;
$shadows: (0 42px 13.37px hotpink)

// Yep
$shadows: append($shadows, $shadow, comma)

// Nope
$shadows: $shadows, $shadow

В этой статье, я привожу множество советов и хитростей, как правильно обрабатывать и управлять списками в Sass.

Карты

С помощью Sass, авторы таблиц стилей могут создавать карты (maps) – термин Sass для связных массивов, хэшей или даже объектов JavaScript. Карта – это структура данных, сопоставляющая ключи со значениями. И ключ и значение может быть любым типом данных, включая карты, хотя я это не рекомендую использовать сложные типы данных как ключи карты, просто во имя здравого смысла.

Код карт следует писать следующим образом:

  • пробел после двоеточия (:);
  • открывающая скобка (() на той же строке, что и двоеточие (:);
  • ключи в кавычках, если это строки (а это так в 99% случаев);
  • каждая пара ключ/значение на своей строке;
  • запятая (,) на конце каждой пары ключ/значение;
  • закрывающая запятая (,) на последней паре, чтобы легче добавлять, удалять или переставлять пункты;
  • закрывающая скобка ()) на своей новой строке;
  • без пробела или новой строки между закрывающей скобкой ()) и точкой с запятой (;).

Пример:

// Yep
$breakpoints: (
  'small': 767px,
  'medium': 992px,
  'large': 1200px,
);

// Nope
$breakpoints: ( small: 767px, medium: 992px, large: 1200px );
// Yep
$breakpoints: ('small': 767px, 'medium': 992px, 'large': 1200px,)

// Nope
$breakpoints: ( 'small': 767px, 'medium': 992px, 'large': 1200px )

// Nope
$breakpoints: (small: 767px, medium: 992px, large: 1200px,)

// Nope (since it is not supported)
$breakpoints: (
  'small': 767px,
  'medium': 992px,
  'large': 1200px,
)

Записей о картах Sass не столь много, сколь ожидаема была эта возможность. Вот три статьи, которые я рекомендую: Using Sass Maps, Extra Map functions in Sass, Real Sass, Real Maps.

Набор правил CSS

На данный момент, это, в основном, пересмотр того, что все и так знают, но вот как набор правил CSS должен быть написан (по крайней мере, по мнению большинства руководств, в том числе CSS Guidelines):

  • связанные селекторы на одной строке; не связанные селекторы на новой строке;
  • открывающая скобка ({) отделяется от последнего селектора одним пробелом;
  • каждое объявление на собственной новой строке;
  • пробел после двоеточия (:);
  • завершающая точка с запятой (;) в конце всех объявлений;
  • закрывающая скобка (}) на своей новой строке;
  • новая строка после закрывающей скобки }.

Пример:

// Yep
.foo, .foo-bar,
.baz {
  display: block;
  overflow: hidden;
  margin: 0 auto;
}

// Nope
.foo,
.foo-bar, .baz {
    display: block;
    overflow: hidden;
    margin: 0 auto }
// Yep
.foo, .foo-bar,
.baz
  display: block
  overflow: hidden
  margin: 0 auto

// Nope
.foo,
.foo-bar, .baz
    display: block
    overflow: hidden
    margin: 0 auto

Дополняя те руководства по CSS, мы должны обратить внимание на:

  • локальные переменные объявляются перед любыми объявлениями, потом отделяются от деклараций новой строкой;
  • вызовы примесей без @content идут перед любым объявлением;
  • вложенные селекторы всегда идут после новой строки;
  • вызовы примесей с @content идут после вложенных селекторов;
  • без новых строк перед закрывающей фигурной скобкой (}).

Пример:

.foo, .foo-bar,
.baz {
  $length: 42em;

  @include ellipsis;
  @include size($length);
  display: block;
  overflow: hidden;
  margin: 0 auto;

  &:hover {
    color: red;
  }

  @include respond-to('small') {
    overflow: visible;
  }
}
.foo, .foo-bar,
.baz
  $length: 42em

  +ellipsis
  +size($length)
  display: block
  overflow: hidden
  margin: 0 auto

  &:hover
    color: red

  +respond-to('small')
    overflow: visible

Порядок объявлений

Невозможно удержать в голове все обсуждения, где мнения о сортировке объявлений в CSS настолько разнятся. Вообще, можно выявить два лагеря:

  • придерживаться алфавитного порядка;
  • упорядочивание по назначению (position, display, colors, font, miscellaneous…).

Есть плюсы и минусы в обоих вариантах. С одной стороны, сортировка в алфавитном порядке является универсальной (по крайней мере, для языков, использующих латинский алфавит), поэтому нет никаких споров о сортировке свойств. Тем не менее, мне весьма странно видеть свойства, такие как bottom и top, не рядом друг с другом. Почему анимации должны быть перед display? Есть много странностей с алфавитным упорядочиванием.

.foo {
  background: black;
  bottom: 0;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
  height: 100px;
  overflow: hidden;
  position: absolute;
  right: 0;
  width: 100px;
}
.foo
  background: black
  bottom: 0
  color: white
  font-weight: bold
  font-size: 1.5em
  height: 100px
  overflow: hidden
  position: absolute
  right: 0
  width: 100px

С другой стороны, сортировка свойств по типу имеет смысл. Каждые объявления, относящиеся к шрифтам, располагаются рядом, как top и bottom, и чтение набора правил отчасти становится похожим на чтение рассказа. Но пока вы строго не придерживаетесь определённых соглашений, типа Idiomatic CSS, существует много спорных моментов. Где будет white-space – рядом со шрифтами или с display? Где расположить overflow? Что такое порядок свойств в группе (может быть в алфавитном порядке, о ирония)?

.foo {
  height: 100px;
  width: 100px;
  overflow: hidden;
  position: absolute;
  bottom: 0;
  right: 0;
  background: black;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
}
.foo
  height: 100px
  width: 100px
  overflow: hidden
  position: absolute
  bottom: 0
  right: 0
  background: black
  color: white
  font-weight: bold
  font-size: 1.5em

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

.foo {
  width: 100px;
  height: 100px;
  position: absolute;
  right: 0;
  bottom: 0;
  background: black;
  overflow: hidden;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
}
.foo
  width: 100px
  height: 100px
  position: absolute
  right: 0
  bottom: 0
  background: black
  overflow: hidden
  color: white
  font-weight: bold
  font-size: 1.5em

Должен сказать, я сам не могу определиться. Недавний опрос на CSS-Tricks установил, что более 45% разработчиков упорядочивают свойства по их назначению, против 14% в алфавитном порядке. Кроме того, 39% об этом вообще не думают, и я в том числе.

График, показывающий, как разработчики упорядочивают свой CSS
График, показывающий, как разработчики упорядочивают свой CSS

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

Недавние исследования показали, что использование CSS Comb (которое использует упорядочивание по типу) помогает уменьшить общий размер файла на 2.7% при сжатии Gzip, в сравнении с 1.3%, когда происходит упорядочение по алфавиту.

Вложенность селекторов

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

Общие правила

Например, такая вложенность Sass:

.foo {
  .bar {
    &:hover {
      color: red;
    }
  }
}
.foo
  .bar
    &:hover
      color: red

…создаст такой CSS:

.foo .bar:hover {
  color: red;
}

Начиная с Sass 3.3 можно использовать ссылку на текущий селектор, используя &, чтобы создать дополнительные селекторы. Например:

.foo {
  &-bar {
    color: red;
  }
}
.foo
  &-bar
    color: red

…сгенерирует такой CSS:

.foo-bar {
  color: red;
}
.foo-bar
  color: red

Этот метод часто используется в методологии BEM для генерации селекторов .block__element и .block--modifier, основанных на базовом селекторе (т.е. .block в данном примере).

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

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

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

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

Тем не менее, есть, очевидно, несколько исключений из этого правила, и как мы увидим в следующем разделе, это мнение кажется довольно популярным. Вы можете прочитать об этом детальнее в статьях Beware of Selector Nesting и Avoid nested selectors for more modular CSS.

Исключения

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

.foo {
  color: red;

  &:hover {
    color: green;
  }

  &::before {
    content: 'pseudo-element';
  }
}
.foo
  color: red

  &:hover
    color: green

  &::before
    content: 'pseudo-element'

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

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

.foo {
  // …

  &.is-active {
    font-weight: bold;
  }
}
.foo
  // …

  &.is-active
    font-weight: bold

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

.foo {
  // …

  .no-opacity & {
    display: none;
  }
}
.foo
  // …

  .no-opacity &
    display: none

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

Соглашения по именованию

В этом разделе мы не будем иметь дело с лучшими соглашениями по именованию в CSS для сопровождения и масштабирования; не только потому, что это остаётся за вами, а также потому, что они из области Sass Styleguide. Я предлагаю те, что рекомендованы в CSS Guidelines.

Вот чему можно присвоить имена в Sass, и очень важно, чтобы эти имена были хорошими, чтобы весь код выглядел последовательным и легко читался:

  • переменные;
  • функции;
  • примеси.

Плейсхолдеры Sass (%placeholder) намеренно исключены из этого списка, так как их можно рассматривать как обычные селекторы CSS и использовать теже принципы именования, что и для классов.

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

$vertical-rhythm-baseline: 1.5rem;

@mixin size($width, $height: $width) {
  // …
}

@function opposite-direction($direction) {
  // …
}
$vertical-rhythm-baseline: 1.5rem

=size($width, $height: $width)
  // …

@function opposite-direction($direction)
  // …

Константы

Если вы являетесь разработчиком фреймворка или библиотеки, вы можете столкнуться с переменными, которые не должны изменяться ни при каких обстоятельствах: константами. К сожалению (или к счастью?), Sass не даёт какой-либо способ определения таких переменных, поэтому мы должны придерживаться строгих соглашений об именовании.

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

// Yep
$CSS_POSITIONS: (top, right, bottom, left, center);

// Nope
$css-positions: (top, right, bottom, left, center);
// Yep
$CSS_POSITIONS: (top, right, bottom, left, center)

// Nope
$css-positions: (top, right, bottom, left, center)

Если вы действительно хотите покумекать над идеей констант в Sass, вам стоит почитать эту специальную статью.

Пространство имён

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

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

$su-configuration: (  );

@function su-rainbow($unicorn) {
  // …
}
$su-configuration: (  )

@function su-rainbow($unicorn)
  // …

У Kaelig есть очень проницательная статья о глобальном пространстве имен CSS, на случай, если эта тема вас заинтересовала.

Обратите внимание, что автоматическое создание пространств имён, безусловно, цель для предстоящего проекта @import из Sass 4.0. И так как приход его всё ближе и ближе, то использование библиотек с пространством имён, написанным вручную, может стать сложнее в использовании.

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

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

Для комментариев в CSS есть обширное поле деятельности. Они могут пояснять:

  • структуру и/или роль файла
  • цель набора правил;
  • смысл использования волшебного числа;
  • причину объявления CSS;
  • порядок объявления в CSS;
  • ход мысли, почему это было сделано так, а не иначе.

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

Написание комментариев

В идеале, любой набор CSS правил должен предшествовать комментарию в стиле Си, объясняя цель блока CSS. Этот комментарий также принимает пронумерованные объяснения по поводу конкретных частей набора правил. Например:

/**
 * Вспомогательный класс для усечения и добавления многоточия в слишком длинную строку
 * на одной строке.
 * 1. Предотвращает сворачивание содержимого, оставляет его на одной строке.
 * 2. Добавляет многоточие на конце строки.
 */
.ellipsis {
  white-space: nowrap; /* 1 */
  text-overflow: ellipsis; /* 2 */
  overflow: hidden;
}
/**
 * Вспомогательный класс для усечения и добавления многоточия в слишком длинную строку
 * на одной строке.
 * 1. Предотвращает сворачивание содержимого, оставляет его на одной строке.
 * 2. Добавляет многоточие на конце строки.
 */
.ellipsis
  white-space: nowrap /* 1 */
  text-overflow: ellipsis /* 2 */
  overflow: hidden

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

Комментируя раздел Sass, используйте встроенные комментарии Sass вместо блока в Си-стиле. Это сделает комментарий невидимым на выходе, даже в расширенном режиме в процессе разработки.

// Добавить текущий модуль в список импортируемых модулей.
// `!global` флаг требуется для обновления глобальной переменной.
$imported-modules: append($imported-modules, $module) !global;
// Добавить текущий модуль в список импортируемых модулей.
// `!global` флаг требуется для обновления глобальной переменной.
$imported-modules: append($imported-modules, $module) !global

Заметьте, что такой же способ поддерживается и Гайдлайном CSS в его разделе Комментирование.

Документирование

Каждая переменная, функция, примесь и плейсхолдер, который предназначен для повторного использования во всём коде, должны быть задокументированы как часть глобального API с использованием SassDoc.

/// Вертикальный ритм, используемый во всём коде.
/// @type Length
$vertical-rhythm-baseline: 1.5rem;
/// Вертикальный ритм, используемый во всём коде.
/// @type Length
$vertical-rhythm-baseline: 1.5rem

Необходимо три слеша (/).

SassDoc выполняет две основные задачи:

  • обходит стандартные комментарии, используя систему аннотации на основе всего, что является частью открытого или закрытого API;
  • позволяет создавать HTML-версию документации API с помощью любого инструмента генерирования SassDoc (CLI tool, Grunt, Gulp, Broccoli, Node…).
Документация, сгенерированная в SassDoc
Документация, сгенерированная в SassDoc

Вот пример примеси, обширно документированной в SassDoc:

/// Примесь позволяет определять `width` и `height` одновременно.
///
/// @author Kitty Giraudel
///
/// @access public
///
/// @param {Length} $width - `width` элемента
/// @param {Length} $height ($width) - `height` элемента
///
/// @example scss - Использование
///   .foo {
///     @include size(10em);
///   }
///
///   .bar {
///    @include size(100%, 10em);
///   }
///
/// @example css - Вывод CSS
///   .foo {
///    width: 10em;
///    height: 10em;
///   }
///
///   .bar {
///    width: 100%;
///    height: 10em;
///   }
@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}
/// Примесь позволяет определять `width` и `height` одновременно.
///
/// @author Kitty Giraudel
///
/// @access public
///
/// @param {Length} $width - `width` элемента
/// @param {Length} $height ($width) - `height` элемента
///
/// @example scss - Использование
///   .foo {
///     @include size(10em);
///   }
///
///   .bar {
///    @include size(100%, 10em);
///   }
///
/// @example css - Вывод CSS
///   .foo {
///    width: 10em;
///    height: 10em;
///   }
///
///   .bar {
///    width: 100%;
///    height: 10em;
///   }
=size($width, $height: $width)
  width: $width
  height: $height

Архитектура

Разработка архитектуры CSS-проекта, вероятно, одна из самых сложных задач, которую придётся сделать в жизни проекта. Сохранять архитектуру последовательной и значимой – ещё сложнее.

К счастью, одно из главных преимуществ использования CSS-препроцессоров – в возможности разделить кодовую базу на несколько файлов без ущерба для производительности (в отличие от CSS-правила @import). Благодаря @import в Sass, совершенно безопасно (и на самом деле рекомендуется) использовать столько файлов, сколько необходимо в разработке, все они потом будут собраны в одну таблицу стилей и так попадут на продакшен.

Кроме того, я не могу не подчеркнуть потребность в папках, даже на небольших проектах. Дома вы не кладёте каждый лист бумаги в один и тот же ящик. Вы используете папки; одну для дома, другую для банка, третью для счетов, и так далее. Нет причины поступать иначе при определении структуры CSS-проекта. Разделяйте кодовую базу на папки, чтобы позже легко было найти нужный код.

Существует много популярных архитектур для CSS проектов: OOCSS, Atomic Design, Bootstrap, Foundation и тому подобные… все они имеют свои достоинства, плюсы и минусы.

Я сам использую подход, который очень похож на SMACSS от Джонатана Снука, который фокусируется на сохранении простоты и очевидности.

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

Компоненты

Существует главное отличие между тем, чтобы сделать код работающим, или сделать его хорошим. Опять таки, CSS – довольно путаный язык [указать источник]. Чем меньше CSS, тем лучше. Мы не хотим иметь дело с мегабайтами кода. Чтобы держать файлы стилей короткими и эффективными – и это не будет для вас сюрпризом – чаще всего будет хорошей идеей подумать об интерфейсе, как о наборе компонентов.

Компоненты могут быть чем угодно, до тех пор, пока они:

  • делают одну и только одну вещь;
  • могут быть повторно используемы;
  • независимы.

Например, форма поиска должна рассматриваться в качестве компонента. Она должна иметь возможность быть используемой повторно на разных страницах, в различных ситуациях. Она не должна зависеть от положения в DOM (в подвале, в боковой панели, в основном содержимом…).

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

Структура компонента

В идеале, компоненты должны располагаться в своих собственных файлах (partial) (в папке components/, как описано в главе 7-1 pattern), например components/_button.scss. Стили, описываемые в каждом компоненте должны быть связаны только с:

  • стилем самого компонента;
  • стилем вариантом компонента, его модификаторами и состояниями;
  • стили потомков компонента (то есть детей), если это необходимо.

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

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

Вот пример компонента кнопка:

// Переменные для кнопки
$button-color: $secondary-color;

// … включите сюда любые правила для кнопки:
// - миксины
// - плейсхолдеры
// - функции

/**
 * Кнопки
 */
.button {
  @include vertical-rhythm;
  display: block;
  padding: 1rem;
  color: $button-color;
  // … etc.

  /**
   * Инлайновая кнопка для больших экранов
   */
  @include respond-to('medium') {
    display: inline-block;
  }
}

/**
 * Иконки внутри кнопок
 */
.button > svg {
  fill: currentcolor;
  // … etc.
}

/**
 * Инлайновая кнопка
 */
.button--inline {
  display: inline-block;
}
// Переменные для кнопки
$button-color: $secondary-color

// … включите сюда любые правила для кнопки:
// - миксины
// - плейсхолдеры
// - функции

/**
 * Кнопки
 */
.button
  +vertical-rhythm
  display: block
  padding: 1rem
  color: $button-color
  // ... etc.

  /**
   * Инлайновая кнопка для больших экранов
   */
  +respond-to('medium')
    display: inline-block
}

/**
 * Иконки внутри кнопок
 */
.button > svg
  fill: currentcolor
  // ... etc.

/**
 * Инлайновая кнопка
 */
.button--inline
  display: inline-block

Благодарю David Khourshid за помощь и экспертизу этой главы.

Шаблон 7-1

Возвратимся к архитектуре? Я обычно использую так называемый Шаблон 7-1: 7 папок, 1 файл. Обычно весь наш код можно распределить в 7 папок и один файл в корневом каталоге (обычно с именем main.scss), который импортирует их все.

  • base/
  • components/
  • layout/
  • pages/
  • themes/
  • utils/
  • vendors/

И, конечно же:

  • main.scss

Если вы хотите использовать паттерн 7-1, вот готовый шаблон на Гитхабе. В нём содержится всё что нужно для начала работы по этой архитектуре.

Обои от Julien He
Обои от Julien He

В идеале, в конечном итоге у нас получится что-то такое:

sass/
|
| abstracts/
|   | _variables.scss    # Sass переменные
|   | _functions.scss    # Sass функции
|   | _mixins.scss       # Sass миксины
|   | _placeholders.scss # Sass плейсхолдеры
|
| base/
|   | _reset.scss        # Reset/normalize
|   | _typography.scss   # Типографика
|                        # Прочее
|
| components/
|   | _buttons.scss      # Кнопки
|   | _carousel.scss     # Карусель
|   | _cover.scss        # Обложка
|   | _dropdown.scss     # Выпадашка
|                        # и так далее
|
| layout/
|   | _navigation.scss   # Навигация
|   | _grid.scss         # Сетка
|   | _header.scss       # Шапка
|   | _footer.scss       # Футер
|   | _sidebar.scss      # Сайдбар
|   | _forms.scss        # Формы
|                        # и так далее
|
| pages/
|   | _home.scss         # Специфичные стили страницы Home
|   | _contact.scss      # Специфичные стили страницы Contact
|                        # и так далее
|
| themes/
|   | _theme.scss        # Тема по умолчанию
|   | _admin.scss        # Тема админа
|                        # и так далее
|
| vendors/
|   | _bootstrap.scss    # Bootstrap
|   | _jquery-ui.scss    # jQuery UI
|                        # и так далее
|
`– main.scss              # Главный Sass файл

Файлы с тем же соглашением по именованию, что и выше, начинаются с нижнего подчеркивания.

Папка Base

Папка base/ содержит то, что мы можем назвать общим шаблоном проекта. Там вы можете найти файл сброса, некоторые типографские правила, и, вероятно, стили (я привык их называть _base.scss), определяющие некоторые стандартные стили для часто используемых элементов HTML.

  • _base.scss
  • _reset.scss
  • _typography.scss

Если в проекте много CSS анимации, то, возможно, стоит добавить файл \_animations.scss, который будет содержать определения @keyframes всех анимаций. Если же в проекте всего пара таких штук, оставьте их рядом с селекторами, использующие их.

Папка Layout

Папка layout/ содержит всё, что принимает участие в постройке раскладки сайта или приложения. Эта папка может содержать стили для основных частей сайта (шапка, подвал, навигация, боковая панель…), сетка или даже CSS-стили для всех форм.

  • _grid.scss
  • _header.scss
  • _footer.scss
  • _sidebar.scss
  • _forms.scss
  • _navigation.scss

Папка layout/ может быть названа partials/, на ваше усмотрение.

Папка Components

Для маленьких компонентов существует папка components/. В то время, как layout/основные (определяют общий каркас), код внутри components/ больше сфокусирован на виджетах и содержит все модули, вроде слайдера, загрузчика и тому подобных виджетов. Обычно файлов в components/ много, если приложение или сайт состоит из множества мелких модулей.

  • _media.scss
  • _carousel.scss
  • _thumbnails.scss

Папка components/ может называться modules/, на ваше усмотрение.

Папка Pages

Если у вас есть стили, зависящие от страницы, лучше положить их в папку pages/, в файл, названный как и страница. Например, не редкость – иметь очень специфичные стили для главной страницы, следовательно, существует потребность в _home.scss в pages/.

  • _home.scss
  • _contact.scss

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

Папка Themes

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

  • _theme.scss
  • _admin.scss

Это очень зависит от проекта и не сильно распространено.

Папка Abstracts

Папка abstracts/ собирает все инструменты и хелперы Sass в проекте. Каждая глобальная переменная, функция и примесь должна быть помещена сюда.

Правило для этой папки в том, что она не должна создавать CSS при компиляции сама по себе. Это не что иное, как Sass хелперы.

  • _variables.scss
  • _mixins.scss
  • _functions.scss
  • _placeholders.scss (часто называется _helpers.scss)

При работе над очень большим проектом с большим количеством абстракций, возможно, стоит их группировать по темам, а не по типу. Например, типографику (_typography.scss), темы оформления (_theming.scss), и так далее. Каждый файл содержит все относящиеся к нему хелперы: переменные, функции, миксины и плейсхолдеры. Это сильно облегчит понимание и обслуживание кода, особенно когда файлы становятся очень длинными.

Папка abstracts/ может также быть названа utilities/ или helpers/, на ваше усмотрение.

Папка Vendors

И последнее, но не менее важное, большинство проектов будут иметь папку vendors/, содержащую все CSS-файлы из внешних библиотек и фреймворков – Normalize, Bootstrap, jQueryUI, FancyCarouselSliderjQueryPowered и так далее. Нахождение этих файлов в этой папке – хороший способ сказать: “Эй, это не я писал, не мой код, не моя ответственность”.

  • _normalize.scss
  • _bootstrap.scss
  • _jquery-ui.scss
  • _select2.scss

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

Например, файл vendors-extensions/_boostrap.scss, содержит все CSS-правила, переопределящие стандартные CSS-правила Bootstrap. Это сделано для того, чтобы не править сами внешние модули, что является совсем не здоровской идеей.

Файл Main

Главный файл (обычно называемый main.scss) должен быть единственным файлом Sass, который не начинается с нижнего подчеркивания. Этот файл не должен содержать ничего, кроме @import и комментариев.

Файлы должны быть импортированы в соответствии с папками размещения, один за другим, в следующем порядке:

  1. abstracts/
  2. vendors/
  3. base/
  4. layout/
  5. components/
  6. pages/
  7. themes/

Чтобы улучшить читаемость, главный файл должен следовать этим рекомендациям:

  • один файл на один @import;
  • один @import на строку;
  • не вставлять пустую строку между файлами из одной папки;
  • вставлять пустую строку после последнего импорта из папки;
  • не писать расширения файлов и нижние подчеркивания.
@import 'abstracts/variables';
@import 'abstracts/functions';
@import 'abstracts/mixins';
@import 'abstracts/placeholders';

@import 'vendors/bootstrap';
@import 'vendors/jquery-ui';

@import 'base/reset';
@import 'base/typography';

@import 'layout/navigation';
@import 'layout/grid';
@import 'layout/header';
@import 'layout/footer';
@import 'layout/sidebar';
@import 'layout/forms';

@import 'components/buttons';
@import 'components/carousel';
@import 'components/cover';
@import 'components/dropdown';

@import 'pages/home';
@import 'pages/contact';

@import 'themes/theme';
@import 'themes/admin';
@import vendors/bootstrap
@import vendors/jquery-ui

@import utils/variables
@import utils/functions
@import utils/mixins
@import utils/placeholders

@import base/reset
@import base/typography

@import layout/navigation
@import layout/grid
@import layout/header
@import layout/footer
@import layout/sidebar
@import layout/forms

@import components/buttons
@import components/carousel
@import components/cover
@import components/dropdown

@import pages/home
@import pages/contact

@import themes/theme
@import themes/admin

Существует ещё один способ импорта, который я считаю допустимым. С одной стороны, файл становится более читаемым. С другой стороны, его обновление становится немного более болезненным. Во всяком случае, вам решать, что лучше, это не имеет большого значения. Основной файл следует следующим принципам:

  • один @import на папку;
  • разрыв строки после @import;
  • каждый файл на отдельной строке;
  • пустая строка после последнего импорта файла из папки;
  • не писать расширения файлов и нижние подчеркивания.
@import
  'abstracts/variables',
  'abstracts/functions',
  'abstracts/mixins',
  'abstracts/placeholders';

@import
  'vendors/bootstrap',
  'vendors/jquery-ui';

@import
  'base/reset',
  'base/typography';

@import
  'layout/navigation',
  'layout/grid',
  'layout/header',
  'layout/footer',
  'layout/sidebar',
  'layout/forms';

@import
  'components/buttons',
  'components/carousel',
  'components/cover',
  'components/dropdown';

@import
  'pages/home',
  'pages/contact';

@import
  'themes/theme',
  'themes/admin';
@import
  vendors/bootstrap,
  vendors/jquery-ui

@import
  utils/variables,
  utils/functions,
  utils/mixins,
  utils/placeholders

@import
  base/reset,
  base/typography

@import
  layout/navigation,
  layout/grid,
  layout/header,
  layout/footer,
  layout/sidebar,
  layout/forms

@import
  components/buttons,
  components/carousel,
  components/cover,
  components/dropdown

@import
  pages/home,
  pages/contact

@import
  themes/theme,
  themes/admin

О глобинге

В программировании, глоб шаблоны определяют набор файлов через символы подстановки, такие как *.scss. В общем, глоббинг означает определение набора файлов на основе выражения вместо списка имен файлов. Применимо к Sass, это означает импортирование составных файлов в основной файл main file с глоб шаблоном, а не перечислением их по отдельности. Таким образом, основной файл будет выглядеть так:

@import 'abstracts/*';
@import 'vendors/*';
@import 'base/*';
@import 'layout/*';
@import 'components/*';
@import 'pages/*';
@import 'themes/*';
@import 'abstracts/*'
@import 'vendors/*'
@import 'base/*'
@import 'layout/*'
@import 'components/*'
@import 'pages/*'
@import 'themes/*'

Sass не поддерживает глоббинг из коробки, потому что он может привести к опасным последствиям, так как в CSS имеет значение порядок определения правил. При динамическом импорте правил (обычно в алфавитном порядке) отсутствует контроль над порядком импорта файлов, что может привести к трудноотлавливаемым побочным эффектам.

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

При использовании Ruby Sass, есть Ruby гем, который называется sass-globbing который включает именно эту возможность. Если разработка на node-sass, остаётся полагаться либо на Node.js, либо на любой сборщик, который позаботится о подобной сборке (Gulp, Grunt и т.д.).

Файл позора

Это интересная идея, которая стала популярна благодаря Гарри Робертсу, Дэйву Руперту и Крису Койеру и состоит в том, чтобы складывать все хаки и код, которым вы не гордитесь, в файл позора. Поэтому этот файл и имеет такое драматическое название – _shame.scss, и импортируется в самом конце.

/**
 * Особое исправление навигации.
 *
 * Кто-то использовал ID в коде шапки (`#header a {}`), который перекрывает
 * селекторы nav (`.site-nav a {}`). Используйте `!important`, чтобы
 * перекрыть их, до тех пор, пока я не перепишу шапку.
 */
.site-nav a {
    color: #BADA55 !important;
}
/**
 * Особое исправление навигации.
 *
 * Кто-то использовал ID в коде шапки (`#header a {}`), который перекрывает
 * селекторы nav (`.site-nav a {}`). Используйте `!important`, чтобы
 * перекрыть их, до тех пор, пока я не перепишу шапку.
 */
.site-nav a
    color: #BADA55 !important

Адаптивный веб-дизайн и точки останова

Я не думаю, что надо рассказывать, что такое адаптивный веб-дизайн, так как он сейчас везде. Тем не менее, вы можете задаться вопросом: почему раздел об адаптивном веб-дизайне находится в руководсте по Sass? Просто есть кое-какие приёмы для облегчения работы с точками останова, так что я подумал, что будет неплохо перечислить их здесь.

Именование точек останова

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

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

// Yep
$breakpoints: (
  'medium': (min-width: 800px),
  'large': (min-width: 1000px),
  'huge': (min-width: 1200px),
);

// Nope
$breakpoints: (
  'tablet': (min-width: 800px),
  'computer': (min-width: 1000px),
  'tv': (min-width: 1200px),
);
// Yep
$breakpoints: ('medium': (min-width: 800px), 'large': (min-width: 1000px), 'huge': (min-width: 1200px))

// Nope
$breakpoints: ('tablet': (min-width: 800px), 'computer': (min-width: 1000px), 'tv': (min-width: 1200px))

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

$breakpoints: (
  'семечка': (min-width: 800px),
  'росток': (min-width: 1000px),
  'растение': (min-width: 1200px),
);
$breakpoints: ('семечка': (min-width: 800px), 'росток': (min-width: 1000px), 'растение': (min-width: 1200px))

В предыдущем примере используются вложенные карты для определения точек останова, но по большому счёту, всё зависит от способа управления точками останова, на который вы опираетесь. Вы можете выбрать строки, а не внутренние карты для большей гибкости (например '(min-width: 800px)').

Управление точками останова

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

/// Управление отзывчивостью.
/// @access public
/// @param {String} $breakpoint - точка останова
/// @requires $breakpoints
@mixin respond-to($breakpoint) {
  $raw-query: map-get($breakpoints, $breakpoint);

  @if $raw-query {
    $query: if(
      type-of($raw-query) == 'string',
      unquote($raw-query),
      inspect($raw-query)
    );

    @media #{$query} {
      @content;
    }
  } @else {
  @error 'Не указано значение для `#{$breakpoint}`. '
       + 'Пожалуйста, убедитесь, что точка останова объявлена в карте `$breakpoints`.';
  }
}
/// Управление отзывчивостью.
/// @access public
/// @param {String} $breakpoint - точка останова
/// @requires $breakpoints
=respond-to($breakpoint)
  $raw-query: map-get($breakpoints, $breakpoint)

  @if $raw-query
    $query: if(type-of($raw-query) == 'string', unquote($raw-query), inspect($raw-query))

    @media #{$query}
      @content

  @else
    @error 'Не указано значение для `#{$breakpoint}`. '
         + 'Пожалуйста, убедитесь, что точка останова объявлена в карте `$breakpoints`.';

Очевидно, что это довольно упрощенный менеджер точек останова, который не будет работать, когда надо обрабатывать произвольные или множественные точки остановки. Если вам нужно управление отзывчивостью с расширенными настройками, могу порекоммендовать вам не изобретать колесо, а воспользоваться отличными решениями: Sass-MQ, Breakpoint или include-media.

Если вы хотите больше узнать о том, как разобраться с медиа-запросами в Sass, то и SitePoint, и CSS-Tricks имеют отличные статьи на эту тему.

Использование медиа-запросов

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

.foo {
  color: red;

  @include respond-to('medium') {
    color: blue;
  }
}
.foo
  color: red

  +respond-to('medium')
    color: blue

Это создаст следующий CSS:

.foo {
  color: red;
}

@media (min-width: 800px) {
  .foo {
    color: blue;
  }
}

Вы могли слышать, что это правило приводит к дублированию медиа-запросов в получаемом CSS. Это, безусловно, верно. Хотя, были сделаны тесты, которые говорят о том, что это не имеет особого значения, если за дело берётся Gzip (или аналог):

… мы выяснили, были ли влияния на производительность комбинированных и рассеяных медиа-запросов, и пришли к выводу, что различие является минимальным в худшем случае, а по существу и не существует.
— Сэм Ричардс относительно Breakpoint

Теперь, если вы действительно обеспокоены дублированием медиа-запросов, вы всё ещё можете использовать инструмент, чтобы объединить их, например, этот gem, однако я должен вас предупредить о возможных побочных эффектах перемещения CSS-кода. Вы остаетёсь без представления о порядке кода, что является важным.

Переменные

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

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

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

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

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

Области видимости

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

Документация говорит о затенении глобальной переменной. При объявлении переменной, уже существующей в глобальной области видимости, во внутренней области видимости (селектор, функция, примесь…), локальная переменная называется затенённой глобальной. В общем, она перезаписывается только для локальной области видимости.

Следующий фрагмент кода объясняет задумку затенения переменных.

// Объявить глобальную переменную на корневом уровне.
$variable: 'initial value';

// Создать примесь, которая перезаписывает глобальные переменные.
@mixin global-variable-overriding {
  $variable: 'mixin value' !global;
}

.local-scope::before {
  // Создать локальную переменную, которая затеняет глобальную.
  $variable: 'local value';

  // Применить примесь: она перезапишет глобальную переменную.
  @include global-variable-overriding;

  // Вывести значение переменной.
  // Здесь значение из **локальной** переменной, так как она глобальную.
  content: $variable;
}

// Вывести эту переменную в другом селекторе, который не делает затенения.
// Здесь значение **глобальное**, как и ожидалось.
.other-local-scope::before {
  content: $variable;
}
// Объявить глобальную переменную на корневом уровне.
$variable: 'initial value'

// Создать примесь, которая перезаписывает глобальные переменные.
@mixin global-variable-overriding
  $variable: 'mixin value' !global

.local-scope::before
  // Создать локальную переменную, которая затеняет глобальную.
  $variable: 'local value'

  // Применить примесь: она перезапишет глобальную переменную.
  +global-variable-overriding

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

// Вывести эту переменную в другом селекторе, который не делает затенения.
// Здесь значение **глобальное**, как и ожидалось.
.other-local-scope::before
  content: $variable

Флаг !default

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

$baseline: 1em !default;
$baseline: 1em !default

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

// Переменная разработчика
$baseline: 2em;

// Ваша библиотека определяет `$baseline`
@import 'your-library';

// $baseline == 2em;
// Переменная разработчика
$baseline: 2em

// Ваша библиотека определяет `$baseline`
@import your-library

// $baseline == 2em

Флаг !global

Флаг !global следует использовать только тогда, когда вы переопределяете глобальную переменную из локальной области видимости. При определении переменной на корневом уровне, флаг global должен быть опущен.

// Yep
$baseline: 2em;

// Nope
$baseline: 2em !global;
// Yep
$baseline: 2em

// Nope
$baseline: 2em !global

Много переменных или карты

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

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

/// Карта Z-index’ов, собирает все Z-слои приложения
/// @access private
/// @type Map
/// @prop {String} key - Имя слоя
/// @prop {Number} value - значение Z, соответствущее ключу
$z-indexes: (
  'modal': 5000,
  'dropdown': 4000,
  'default': 1,
  'below': -1,
);

/// Получение значения z-index из имени слоя
/// @access public
/// @param {String} $layer - Имя слоя
/// @return {Number}
/// @require $z-indexes
@function z($layer) {
  @return map-get($z-indexes, $layer);
}
/// Карта Z-index’ов, собирает все Z-слои приложения
/// @access private
/// @type Map
/// @prop {String} key - Имя слоя
/// @prop {Number} value - значение Z, соответствущее ключу
$z-indexes: ('modal': 5000, 'dropdown': 4000, 'default': 1, 'below': -1,)

/// Получение значения z-index из имени слоя
/// @access public
/// @param {String} $layer - Имя слоя
/// @return {Number}
/// @require $z-indexes
@function z($layer)
  @return map-get($z-indexes, $layer)

Extend

Директива @extend – одна из возможностей, которая сделала Sass таким известным несколько лет назад. Напомню: она позволяет Sass оформить элемент A именно так, как будто бы он также соответствует селектору B. Разумеется, эта возможность является ценным союзником при написании модульного CSS.

Однако настоящее предназначение @ extend заключается в поддержании отношений (соединений) в расширенных селекторах между наборами правил. Что именно это значит?

  • Селекторы имеют соединения (например, .bar в .foo > .bar должны иметь родителя .foo);
  • Эти соединения соотносятся на расширенный селектор (например, ``.baz { @extend .bar; } будет создавать .foo > .bar, .foo > .baz`);
  • Объявления расширенного селектора будут использоваться совместно с расширяемым селектором.

Учитывая это, нетрудно увидеть, как расширение селекторов с мягкими соединениями могут привести к взрыву селектора. Если .baz .qux расширяет .foo .bar, то результирующий селектор может быть .foo .baz .qux или .baz .foo .qux, так как .foo и .baz являются общими предками. Они могут быть родителями, дедушками и далее.

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

Для наследования стилей используйте только @extend, если расширяется селектор .class или %placeholder - это своего рода расширенный селектор. Например, .error своего рода .warning, так что .error может расширять .warning.

%button {
  display: inline-block;
  // … стили кнопки

  // Взаимосвязь: %button есть ребёнок %modal
  %modal > & {
    display: block;
  }
}

.button {
  @extend %button;
}

// Yep
.modal {
  @extend %modal;
}

// Nope
.modal {
  @extend %modal;

  > .button {
    @extend %button;
  }
}
%button
  display: inline-block
  // … стили кнопки

  // Взаимосвязь: %button есть ребёнок %modal
  %modal > &
    display: block

.button
  @extend %button

// Yep
.modal
  @extend %modal

// Nope
.modal
  @extend %modal

  > .button
    @extend %button

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

  • Используйте указание @extend исключительно на %placeholder‘ы, а не на обычные селекторы.
  • Когда расширяете классы, делайте это только с другим классом и никогда с составным селектором.
  • Убедитесь, что %placeholder, который вы расширяете, присутствует как можно реже в таблице стилей.
  • Избегайте расширять общие селекторы предков (т.е. .foo .bar) или общие селекторы-сиблинги (одного уровня) (т.е. .foo ~ .bar). This is what causes selector explosion. Это и вызывает селекторный взрыв.

Говорят, что @extend уменьшает размер файла, так как комбинирует селекторы, а не дублирует код. Это правда, однако разница нивелируется, если используется сжатие Gzip

Таким образом, если вы не можете использовать Gzip (или любой аналог) то применение @extend может и имеет смысл, особенно если вес таблицы стилей является узким местом производительности проекта.

Extend и медиа-запросы

Расширять селекторы стоит только в пределах одной медиа-видимости (директива @media). Думайте о медиа-запросе как о другой области.

%foo {
  content: 'foo';
}

// Nope
@media print {
  .bar {
  // Это не работает. Ещё хуже: это ломает.
    @extend %foo;
  }
}

// Yep
@media print {
  .bar {
    @at-root (without: media) {
      @extend %foo;
    }
  }
}

// Yep
%foo {
  content: 'foo';

  &-print {
    @media print {
      content: 'foo print';
    }
  }
}

@media print {
  .bar {
    @extend %foo-print;
  }
}
%foo
  content: 'foo'

// Nope
@media print
  .bar
  // Это не работает. Ещё хуже: это ломает.
    @extend %foo

// Yep
@media print
  .bar
    @at-root (without: media)
      @extend %foo

// Yep
%foo
  content: 'foo'

  &-print
    @media print
      content: 'foo print'

@media print
  .bar
    @extend %foo-print

По поводу @extend мнения чрезвычайно полярны, до такой степени, что многие разработчики, включая меня, выступают против него, о чём вы можете прочитать в следующих статьях:

Подводя итог вышесказанному, я советую использовать @extend только для обслуживания взаимосвязи между селекторами. Если два селектора имеют схожие характеристики, то это идеальный случай для применения @extend. Если они независят друг от друга, но у них совпадают несколько свойств, то @mixin может подойти здесь лучше. Лучше понять как выбрать что-то из этих двух вариантов в этой статье.

Благодарю David Khourshid за его помощь и экспертизу этой главы.

Примеси (Миксины)

Примеси – одна из самых важных частей из всего языка Sass. Они являются ключом к повторному использованю и DRY компонентам. Они позволяют авторам определить стили, которые будут повторно использоваться по всей таблице стилей, без надобности к использованию таких неосмысленных классов, как .float-left.

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

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

Основы

Как было сказано, примеси чрезвычайно полезны, и вы должны их использовать. Правило гласит, что если вам случится встретить набор свойств CSS, которые всегда появляются вместе по какой-либо причине (то есть не случайно), то вы можете поместить их в примесь. Хак Micro-clearfix от Николаса Галлагера заслуживает быть помещенным в примесь (без аргументов), например.

/// Помощник для сброса внутреннего обтекания
/// @author Николас Галлагер
/// @link http://nicolasgallagher.com/micro-clearfix-hack/ Micro Clearfix
@mixin clearfix {
  &::after {
    content: '';
    display: table;
    clear: both;
  }
}
/// Помощник для сброса внутреннего обтекания
/// @author Николас Галлагер
/// @link http://nicolasgallagher.com/micro-clearfix-hack/ Micro Clearfix
@mixin clearfix
  &::after
    content: ''
    display: table
    clear: both

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

/// Помощник для определения размера
/// @author Kitty Giraudel
/// @param {Length} $width
/// @param {Length} $height
@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}
/// Помощник для определения размера
/// @author Kitty Giraudel
/// @param {Length} $width
/// @param {Length} $height
=size($width, $height: $width)
  width: $width
  height: $height

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

Безаргументные примеси

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

В таких случаях, можно безопасно опустить скобки при их вызове. Ключевое слово @include (или знак + в синтаксисе на отступах) уже действует как индикатор что это вызов примеси; нет необходимости в скобках в данном случае.

// Yep
.foo {
  @include center;
}

// Nope
.foo {
  @include center();
}
// Yep
.foo
  +center

// Nope
.foo
  +center()

Список аргументов

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

@mixin shadows($shadows...) {
  // type-of($shadows) == 'arglist'
  // …
}
=shadows($shadows...)
  // type-of($shadows) == 'arglist'
  // …

Теперь, когда вы делаете примесь, которая принимает несколько аргументов (3 или более), подумайте дважды, прежде чем создавать один список – может быть, будет легче передавать их по одному.

Sass очень умён в работе с примесями и объявлениями функций, так что вы можете передавать список или карту в качестве списка аргументов для функции или примеси, и это считается как рядом аргументов.

@mixin dummy($a, $b, $c) {
  // …
}

// Yep
@include dummy(true, 42, 'kittens');

// Yep but nope
$params: (true, 42, 'kittens');
$value: dummy(nth($params, 1), nth($params, 2), nth($params, 3));

// Yep
$params: (true, 42, 'kittens');
@include dummy($params...);

// Yep
$params: (
  'c': 'kittens',
  'a': true,
  'b': 42,
);
@include dummy($params...);
=dummy($a, $b, $c)
  // …

// Yep
+dummy(true, 42, 'kittens')

// Yep but nope
$params: (true, 42, 'kittens')
$value: dummy(nth($params, 1), nth($params, 2), nth($params, 3))

// Yep
$params: (true, 42, 'kittens')
+dummy($params...)

// Yep
$params: ('c': 'kittens', 'a': true, 'b': 42,)
+dummy($params...)

Для получения дополнительной информации о том, лучше ли использовать несколько аргументов, список или список аргументов, SitePoint имеет приятную статью по этой теме.

Примеси и вендорные префиксы

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

К сожалению, Autoprefixer не всегда подходит. Если вы используете Bourbon или Compass, вы уже наверника знаете, что они поставляют коллекцию примесей для обработки вендорных префиксов. Используйте их.

Если вы не можете использовать ни Autoprefixer, ни Bourbon и Compass, то тогда вы должны использовать вашу собственную примесь для подстановки префиска свойствам CSS. Но, пожалуйста, не делайте по примеси на каждое свойство, вручную выводя каждый вендор.

// Nope
@mixin transform($value) {
  -webkit-transform: $value;
  -moz-transform: $value;
  transform: $value;
}
// Nope
=transform($value)
  -webkit-transform: $value
  -moz-transform: $value
  transform: $value

Делайте это по-умному.

/// Примесь, которая выводит вендорные префиксы
/// @access public
/// @author KittyGiraudel
/// @param {String} $property - свойство CSS без префикса
/// @param {*} $value - Сырое значение свойства CSS
/// @param {List} $prefixes - Список префиксов для вывода
@mixin prefix($property, $value, $prefixes: ()) {
  @each $prefix in $prefixes {
    -#{$prefix}-#{$property}: $value;
  }

  #{$property}: $value;
}
/// Примесь, которая выводит вендорные префиксы
/// @access public
/// @author KittyGiraudel
/// @param {String} $property - свойство CSS без префикса
/// @param {*} $value - Сырое значение свойства CSS
/// @param {List} $prefixes - Список префиксов для вывода
=prefix($property, $value, $prefixes: ())
  @each $prefix in $prefixes
    -#{$prefix}-#{$property}: $value

  #{$property}: $value

Использование этой примеси будет очень простым:

.foo {
  @include prefix(transform, rotate(90deg), ('webkit', 'ms'));
}
.foo
  +prefix(transform, rotate(90deg), ('webkit', 'ms'))

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

Условные операторы

Вы уже, наверное, знаете, что Sass предоставляет условные операторы, такие как @if и @else. Пока в вашем коде нет сложной логики, нет необходимости и в условных операторах. Фактически, они в основном нужны для библиотек и фреймворков.

Тем не менее, если вам когда-нибудь понадобится использовать их, пожалуйста, следуйте следующим рекоммендациям:

  • Никаких скобок, покуда они не обязательны;
  • Всегда пустая строка перед @if;
  • Всегда разрыв строки после открывающей фигурной скобки ({);
  • @else на одной строке с предыдущей закрывающей скобкой (});
  • Всегда новая пустая строка после последней закрывающей скобки (}), если на следующей строке не стоит закрывающая скобка (}).
// Yep
@if $support-legacy {
  // …
} @else {
  // …
}

// Nope
@if ($support-legacy == true) {
  // …
}
@else {
  // …
}
// Yep
@if $support-legacy
  // …
@else
  // …

// Nope
@if ($support-legacy == true)
  // …
@else
  // …

При тестировании на отрицающее значение, всегда используйте ключевое слово not, а не проверки на false или null.

// Yep
@if not index($list, $item) {
  // …
}

// Nope
@if index($list, $item) == null {
  // …
}
// Yep
@if not index($list, $item)
  // …

// Nope
@if index($list, $item) == null
  // …

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

// Yep
@if $value == 42 {
  // …
}

// Nope
@if 42 == $value {
  // …
}
// Yep
@if $value == 42
  // …

// Nope
@if 42 == $value
  // …

При использовании условных операторов внутри функции для возврата другого результата, основанного на некоторых условиях, убедитесь, что @return находится вне условных операторов.

// Yep
@function dummy($condition) {
  @if $condition {
    @return true;
  }

  @return false;
}

// Nope
@function dummy($condition) {
  @if $condition {
    @return true;
  } @else {
    @return false;
  }
}
// Yep
@function dummy($condition)
  @if $condition
    @return true

  @return false;

// Nope
@function dummy($condition)
  @if $condition
    @return true
  @else
    @return false

Циклы

Благодаря предоставленным в Sass комплексным структурам данных, таких, как списки и карты, не удивляет и возможность перебора по этим объектам.

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

Each

Цикл @each, безусловно, наиболее часто используемый из трёх циклов, предусмотренных Sass. Он предоставляет чистый API для перебора списка или карты.

@each $theme in $themes {
  .section-#{$theme} {
    background-color: map-get($colors, $theme);
  }
}
@each $theme in $themes
  .section-#{$theme}
    background-color: map-get($colors, $theme)

При переборе карты всегда используйте имена переменных $key и $value ради последовательности.

@each $key, $value in $map {
  .section-#{$key} {
    background-color: $value;
  }
}
@each $key, $value in $map
  .section-#{$key}
    background-color: $value

Также соблюдайте следующие принципы, чтобы сохранить читаемость:

  • Всегда пустая строка перед @each;
  • Всегда пустая строка после закрывающей скобки (}), если на следующей строке нет закрывающей скобки (}).

For

Цикл @for может быть полезным, когда скомбинирован с псевдоклассом CSS :nth-*. Исключая сценарии, когда предпочтительнее использовать цикл @each, если вам надо пройтись по какому-нибудь объекту.

@for $i from 1 through 10 {
  .foo:nth-of-type(#{$i}) {
    border-color: hsl($i * 36, 50%, 50%);
  }
}
@for $i from 1 through 10
  .foo:nth-of-type(#{$i})
    border-color: hsl($i * 36, 50%, 50%)

Всегда используйте $i как переменную для соблюдения соглашения, пока у вас нет веских причин изменить её, никогда не используйте ключевое слово to, используйте through. Многие разработчики даже и не знают, что Sass предоставляет такие варианты; использование разных может привести к путанице.

Также следуйте следующим принципам, чтобы сохранить читаемость:

  • Всегда пустая строка перед @each;
  • Всегда пустая строка после закрывающей скобки (}), если на следующей строке не закрывающая скобка (}).

While

Цикл @while не имеет абсолютно никакого применения в реальном проекте Sass, особенно из-за того, что нет способа остановить цикл изнутри. Не используйте его.

Ошибки и предупреждения

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

  • @debug;
  • @warn;
  • @error.

Отложим @debug в сторону, так как очевидно, что он нацелен на отладку SassScript, который не является нашей целью здесь. Нам остаются @warn и @error, которые с виду одинаковые, за исключением того, что один останавливает компилятор, а другой нет. Позволю вам самим додумать, какой из них что делает.

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

Предупреждения

Возьмём функцию из Sass-MQ, предполагающую конвертирование из px в em, например:

@function mq-px2em($px, $base-font-size: $mq-base-font-size) {
  @if unitless($px) {
    @warn 'Assuming #{$px} to be in pixels, attempting to convert it into pixels.';
    @return mq-px2em($px + 0px);
  } @else if unit($px) == em {
    @return $px;
  }
   @return ($px / $base-font-size) * 1em;
}
@function mq-px2em($px, $base-font-size: $mq-base-font-size)
  @if unitless($px)
    @warn 'Assuming #{$px} to be in pixels, attempting to convert it into pixels.'
    @return mq-px2em($px + 0px)
  @else if unit($px) == em
    @return $px
   @return ($px / $base-font-size) * 1em

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

Ошибки

Ошибки, в отличие от предупреждений, предотвращают компилятор от дальнейшей работы. Обычно они останавливают сборку и выводят сообщение в поток вывода, также как и в stack trace, что весьма удобно для отладки. Из-за этого ошибки должны быть выведены только тогда, когда уже нет возможности для выполнения программы. Когда есть возможность, попробуйте обойти эту проблему и вывести вместо этого предупреждение.

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

/// Карта Z-index’ов, собирает все Z-слои приложения
/// @access private
/// @type Map
/// @prop {String} key - Имя слоя
/// @prop {Number} valye - значение Z, соответствующее ключу
$z-indexes: (
  'modal': 5000,
  'dropdown': 4000,
  'default': 1,
  'below': -1,
);

/// Получение значения z-index из имени слоя
/// @access public
/// @param {String} $layer - Имя слоя
/// @return {Number}
/// @require $z-indexes
@function z($layer) {
  @if not map-has-key($z-indexes, $layer) {
    @error 'Нет слоя с именем `#{$layer}` в $z-indexes. '
         + 'Слой должен быть одним из #{map-keys($z-indexes)}.';
  }

  @return map-get($z-indexes, $layer);
}
/// Карта Z-index’ов, собирает все Z-слои приложения
/// @access private
/// @type Map
/// @prop {String} key - Имя слоя
/// @prop {Number} valye - значение Z, соответствущее ключу
$z-indexes: ('modal': 5000, 'dropdown': 4000, 'default': 1, 'below': -1,)

/// Получение значения z-index из имени слоя
/// @access public
/// @param {String} $layer - Имя слоя
/// @return {Number}
/// @require $z-indexes
@function z($layer)
  @if not map-has-key($z-indexes, $layer)
    @error 'Нет слоя с именем `#{$layer}` в $z-indexes. '
       + 'Слой должен быть одним из #{map-keys($z-indexes)}.';

  @return map-get($z-indexes, $layer)

Больше информации, как эффективно использовать @error, в этом введении в обработку ошибок.

Инструменты

Здорово, что такие пополярные CSS-препроцессоры, как Sass, поставляются с экосистемой фреймворков, плагинов, библиотек и инструментов. После восьми лет существования мы приближаемся к точке, где всё, что может быть написано на Sass, будет написано на Sass.

Тем не менее, я советую снизить количество зависимостей к минимальному. Управление зависимостями чем-то похоже на ад, в котором вы не хотели бы оказаться. Кроме того, нет особой необходимости использовать внешние зависимости, когда работаете с Sass.

Compass

Compass – основной фреймворк Sass, разработанный Крисом Эппстейном, одним из ключевых разработчиков Sass, и я не вижу веских причин для снижения его популярности в последнее время, если вам интересно моё мнение.

Тем не менее, я больше не использую Compass, и главная причина в том, что он сильно замедляет Sass. Ruby Sass сам по себе весьма медленный, так что добавить ещё больше Ruby и сверху положить Sass не сильно помогает.

Дело в том, что мы используем очень малую часть фреймворка. Compass огромен. Кросс-браузерная поддержка – лишь вершина айсберга. Математические функции, помощники изображений, спрайты… Есть ещё много того, что может быть сделано этим фреймворком.

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

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

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

Системы сеток

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

Позвольте сказать прямо: я не большой поклонник систем сеток. Конечно, я вижу их потенциал, но я думаю, что большинство из них полностью излишни и в основном используются для рисования красных столбцов на белом фоне в презентациях дизайнеров. Когда в последний раз вы думали, что слава-Богу-что-я-использовал-этот-инструмент-для-построения-этой-сетки-2-5-3.1-π? Всё верно, никогда. Потому что в большинстве случаев вы просто хотите обычную 12-колоночную сетку и ничего больше.

Если вы используете CSS-фреймворк для вашего проекта, такой как Bootstrap или Foundation, то он, скорее всего, уже включает в себя систему сеток и в этом случае я бы рекомендовал использовать именно его, чтобы избежать необходимость иметь дело с ещё одной зависимостью.

Если вы не привязаны к особой системе сеток, то вам будет приятно знать, что есть два первоклассных движка модульных сеток для Sass: Susy и Singularity. Обе делают гораздо больше, чем вам когда-нибудь понадобится, так что вы можете выбрать тот, который вы предпочитаете между этими двумя, и убедитесь, что все ваши крайние случаи – даже самые изящные из них – будут покрыты. Если вы спросите меня, то я отвечу: Susy имеет немного лучшее сообщество, но это лишь моё мнение.

Или вы можете использовать что-то менее популярное, типа csswizardry-grids. В общем, ваш выбор не будет иметь большого влияния на стиль написания кода, так что это в значительной степени зависит от вас.

SCSS-lint

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

SCSS-lint инструмент для сохранения читаемости и чистоты вашх файлов CSS. Он полностью настраиваемый и легко встраивается в ваш существующий набор инструментов.

К счастью, рекомендации SCSS-lint очень похожи на те, что описаны в данном документе. Для того, чтобы настроить SCSS-lint в соответствии с Sass Guidelines, я рекомендую следующие настройки:

linters:

  BangFormat:
    enabled: true
    space_before_bang: true
    space_after_bang: false

  BemDepth:
    enabled: true
    max_elements: 1

  BorderZero:
    enabled: true
    convention: zero

  ChainedClasses:
    enabled: false

  ColorKeyword:
    enabled: true

  ColorVariable:
    enabled: false

  Comment:
    enabled: false

  DebugStatement:
    enabled: true

  DeclarationOrder:
    enabled: true

  DisableLinterReason:
    enabled: true

  DuplicateProperty:
    enabled: false

  ElsePlacement:
    enabled: true
    style: same_line

  EmptyLineBetweenBlocks:
    enabled: true
    ignore_single_line_blocks: true

  EmptyRule:
    enabled: true

  ExtendDirective:
    enabled: false

  FinalNewline:
    enabled: true
    present: true

  HexLength:
    enabled: true
    style: short

  HexNotation:
    enabled: true
    style: lowercase

  HexValidation:
    enabled: true

  IdSelector:
    enabled: true

  ImportantRule:
    enabled: false

  ImportPath:
    enabled: true
    leading_underscore: false
    filename_extension: false

  Indentation:
    enabled: true
    allow_non_nested_indentation: true
    character: space
    width: 2

  LeadingZero:
    enabled: true
    style: include_zero

  MergeableSelector:
    enabled: false
    force_nesting: false

  NameFormat:
    enabled: true
    convention: hyphenated_lowercase
    allow_leading_underscore: true

  NestingDepth:
    enabled: true
    max_depth: 1

  PlaceholderInExtend:
    enabled: true

  PrivateNamingConvention:
    enabled: true
    prefix: _

  PropertyCount:
    enabled: false

  PropertySortOrder:
    enabled: false

  PropertySpelling:
    enabled: true
    extra_properties: []

  PropertyUnits:
    enabled: false

  PseudoElement:
    enabled: true

  QualifyingElement:
    enabled: true
    allow_element_with_attribute: false
    allow_element_with_class: false
    allow_element_with_id: false

  SelectorDepth:
    enabled: true
    max_depth: 3

  SelectorFormat:
    enabled: true
    convention: hyphenated_lowercase
    class_convention: '^(?:u|is|has)\-[a-z][a-zA-Z0-9]*$|^(?!u|is|has)[a-zA-Z][a-zA-Z0-9]*(?:\-[a-z][a-zA-Z0-9]*)?(?:\-\-[a-z][a-zA-Z0-9]*)?$'

  Shorthand:
    enabled: true

  SingleLinePerProperty:
    enabled: true
    allow_single_line_rule_sets: false

  SingleLinePerSelector:
    enabled: true

  SpaceAfterComma:
    enabled: true

  SpaceAfterPropertyColon:
    enabled: true
    style: one_space

  SpaceAfterPropertyName:
    enabled: true

  SpaceAfterVariableColon:
    enabled: true
    style: at_least_one_space

  SpaceAfterVariableName:
    enabled: true

  SpaceAroundOperator:
    enabled: true
    style: one_space

  SpaceBeforeBrace:
    enabled: true
    style: space
    allow_single_line_padding: true

  SpaceBetweenParens:
    enabled: true
    spaces: 0

  StringQuotes:
    enabled: true
    style: single_quotes

  TrailingSemicolon:
    enabled: true

  TrailingZero:
    enabled: true

  TransitionAll:
    enabled: false

  UnnecessaryMantissa:
    enabled: true

  UnnecessaryParentReference:
    enabled: true

  UrlFormat:
    enabled: false

  UrlQuotes:
    enabled: true

  VariableForProperty:
    enabled: false

  VendorPrefixes:
    enabled: true
    identifier_list: base
    include: []
    exclude: []

  ZeroUnit:
    enabled: true

Если вы не уверены в необходимости использования SCSS-lint, то советую прочитать эти замечательные статьи: Clean Up your Sass with SCSS-lint, Improving Sass code quality on theguardian.com и An Auto-Enforceable SCSS Styleguide.

Если вы хотите подключить SCSS-lint в процесс сборки Grunt, вам будет приятно знать, что есть расширение Grunt для этого – grunt-scss-lint.

Кроме того, если вы в погоне за приложением, которое работает с SCSS-lint и тому подобным, ребята из Thoughtbot (Bourbon, Neat…) работают над Hound.

Слишком длинно; Не читал

Это руководство по написанию стилей весьма длинное и часто полезно иметь под рукой краткие итоги. Ниже представлен такой конспект.

Ключевые принципы

  • Любое руководство по стилю предназначено для поддержания постоянства. Если вы не согласны с какими-то правилами из Гайдлана Sass, это будет справедливо только пока вы постоянно не согласны с ними.
  • Код Sass должен быть как можно более простым. Избегайте построения сложным систем, пока таковая не будет совершенно необходима.
  • Помните, что иногда KISS (Keep It Simple, Stupid) лучше, чем DRY (Don’t Repeat Yourself).

Syntax & formatting

  • Отступ двумя (2) пробелами, никаких табов.
  • Строки должны быть по возможности короче 80 сиволов. Смело разбивайте их на несколько строк при необходимости.
  • CSS должен быть правильно написан, по возможности следуя CSS Guidelines от Harry Roberts.
  • Пользуйтесь пробелами, отделяйте ими разные элементы, правила и декларации. Не стесняйтесь пользоваться пустыми строками, от этого плохо не бывает.

Строки

  • Крайне рекомендуется объявлять директиву @charset первой строкой таблицы стилей.
  • Когда применяются как идентификаторы CSS, строки должны быть заключены в одинарные кавычки. УРЛы также должны быть заключены в кавычки.

Numbers

  • Sass не различает числа, целые и с точкой, так что незначащие нули в конце следует опустить. А вот нуль перед точкой улучшает читаемость и должен ставиться.
  • Нулевая (0) длина не должна содержать единицу измерения.
  • Манипуляции с единицами измерения следует рассматривать как арифметические действия, а не строковые операции.
  • Для улучшения читаемости, вычисления оборачиваются круглыми скобками. Также, сложные математические операции могут быть разбиты на меньшие фрагменты.
  • Магические числа драматически ухудшают поддержку кода и их следует избегать всегда. Если уж используете их, подробно комментируйте их сомнительную полезность в каждом случае.

Colors

  • Цвета должны указываться по возможности в HSL, затем RGB, только затем шестнадцетиричное представление (в нижнем регистре и укороченной форме). Ключевые слова цветов использовать не следует.
  • Когда осветляется или затемняется цвет, предпочтительно использовать mix(..) вместо darken(..) и lighten(..).

Списки

  • Списки должны разделяться запятыми, кроме случая прямой передачи значений CSS строкой с разделителями-пробелами.
  • Оборачивание в круглые скобки также служит улучшению читаемости кода.
  • Списки в строку не должны иметь завершающей запятой, а многострочные — должны.

Карты

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

Порядок объявлений

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

Вложенность селекторов

  • Избегайте вложенность селектором, если это не необходимо (то есть в большинстве случаев).
  • Применяйте вложенность селекторов для псевдоклассов и псевдоэлементов.
  • Медиа-запросы также могут вкладываться в селектор, к которому они относятся.

Соглашения по именованию

  • Лучше всего придерживаться способа именования как в CSS, то есть (кроме пары исключений) нижний регистр и разделение дефисом.

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

  • CSS — хитроумный язык; не стесняйтесь писать подробные комментарии о коде, который выглядит (или является) странным.
  • Для переменных, функций, примесей и плейсхолдеров, расположенных в публичном API, используйте комментарии SassDoc.

Переменные

  • Обязательно используйте флаг !default для любых переменных публичного API, чтобы их можно было безопасно изменить.
  • Не используйте флаг !global на корневом уровне, так как это может привести к нарушению синтаксиса Sass в будущем.

Extend

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