Sass Guidelines

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

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

This version is exclusively maintained by contributors without the review of the main author, therefore might not be completely up-to-date, especially since it is currently in version 1.0 while the English version is in version 1.3.

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

Об авторе

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

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

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

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

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

// Nope
$font-stack: "Helvetica Neue Light", "Helvetica", "Arial", sans-serif;

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

// Nope
$font-stack: "Helvetica Neue Light", "Helvetica", "Arial", sans-serif

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

В предыдущем примере sans-serif не был обёрнут в кавычки, потому что это специальное значение CSS, которые должно быть без кавычек.

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

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

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

// Yep
$length: 0;

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

// Nope
$length: 0em

Самая распространённая ошибка о числах в 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 единиц измерения может быть немного запутанным. Действительно, при попытке преобразовать число в другую единицу измерения, добавление 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. Естественно, Sass является ценным союзником, когда дело доходит до управления цветами, в основном – путём предоставления мощных функций.

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

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

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

Во-первых, ключевые слова часто говорят сами за себя. Представление HSL – не только самое простое для человеческого мозга [указать источник], это также делает его лёгким для настройки цвета путём регулировки цветового тона, насыщенности и яркости индивидуально. RGB по-прежнему имеет преимущество, показывая прямо сейчас, если цвет более синий, зелёный или красный, но это не делает его лёгким в построении цвета из трёх частей. Наконец, шестнадцатеричное представление слишком сложно для расшифровки человеческим умом.

// Yep
.foo {
  color: red;
}

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

// Nope
.foo
  color: #FF0000

При использовании обозначений 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: #c69;
$sass-pink: #c69

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

$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;

// Nope
$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 (since it is 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 3.3, авторы таблиц стилей могут создавать карты – термин 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

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

@mixin debug-map($map) {
  @at-root {
    @debug-map {
      __toString__: inspect($map);
      __length__: length($map);
      __depth__: if(function-exists('map-depth'), map-depth($map), null);
      __keys__: map-keys($map);
      __properties__ {
        @each $key, $value in $map {
          #{'(' + type-of($value) + ') ' + $key}: inspect($value);
        }
      }
    }
  }
}
=debug-map($map)
  @at-root
    @debug-map
      __toString__: inspect($map)
      __length__: length($map)
      __depth__: if(function-exists('map-depth'), map-depth($map), null)
      __keys__: map-keys($map)
      __properties__
        @each $key, $value in $map
          #{'(' + type-of($value) + ') ' + $key}: inspect($value)

Если вы заинтересованы в глубине карты кода, добавьте следующую функцию. Примесь будет отображать её автоматически.

/// Вычислить максимальную глубину карты
/// @param {Map} $map
/// @return {Number} max depth of `$map`
@function map-depth($map) {
  $level: 1;

  @each $key, $value in $map {
    @if type-of($value) == 'map' {
      $level: max(map-depth($value) + 1, $level);
    }
  }

  @return $level;
}
/// Вычислить максимальную глубину карты
/// @param {Map} $map
/// @return {Number} max depth of `$map`
@function map-depth($map)
  $level: 1

  @each $key, $value in $map
    @if type-of($value) == 'map'
      $level: max(map-depth($value) + 1, $level)

  @return $level;
Дальнейшее чтение

Набор правил 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 будет в конечном итоге.

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

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

Исключения

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

.foo {
  color: red;

  &:hover {
    color: green;
  }

  &::before {
    content: 'псевдоэлемент';
  }
}
.foo
  color: red

  &:hover
    color: green

  &::before
    content: 'псевдоэлемент'

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

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

.foo {
  // …

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

  &.is-active
    font-weight: bold

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

.foo {
  // …

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

  .no-opacity &
    display: none

При работе с неопытными разработчиками, селекторы, такие как .no-opacity &, могут выглядеть немного странно. Для предотвращения какой-либо путаницы, можно построить очень короткую примесь, которая преобразует этот странный синтаксис в явный API.

/// Примесь для предоставления простого API вложенности
/// @param {String} $selector - Selector
@mixin when-inside($selector) {
  #{$selector} & {
    @content;
  }
}
/// Примесь для предоставления простого API вложенности
/// @param {String} $selector - Selector
=when-inside($selector) {
  #{$selector} &
    @content
}

Переписывание нашего предыдущего примера будет выглядеть следующим образом:

.foo {
  // …

  @include when-inside('.no-opacity') {
    display: none;
  }
}
.foo
  // …

  +when-inside('.no-opacity')
    display: none

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

Дальнейшее чтение

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

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

Есть несколько вещей, которым вы можете дать имена в Sass, и очень важно, чтобы названия были хорошими, чтобы весь код выглядел последовательным и легко читался:

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

Sass placeholder (%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, например, как библиотеку, фреймворк, сетку или что угодно, вы, возможно, захотите рассмотреть пространства имён всех своих переменных, функциях, примесях и placeholder’ах, так чтобы они не соперничали с чьим-либо кодом.

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

$su-configuration: (  );

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

@function su-rainbow($unicorn)
  // 

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

Дальнейшее чтение

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

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

Есть ещё много возможностей для комментариев в 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
Дальнейшее чтение

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

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

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

/**
 * Вертикальный ритм, использующийся во всём коде.
 * @type Length
 */
$vertical-rhythm-baseline: 1.5rem;
/**
 * Вертикальный ритм, использующийся во всём коде.
 * @type Length
 */
$vertical-rhythm-baseline: 1.5rem
/// Вертикальный ритм, использующийся во всём коде.
/// @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 Hugo 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 Hugo Giraudel
///
/// @access public
///
/// @param {Length} $width - `width` элемента
/// @param {Length} $height ($width) - `height` элемента
///
/// @example scss - Использование
/// .foo
///   +size(10em)
///
/// .bar
///   +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, совершенно безопасно (и на самом деле рекомендуется) использовать столько файлов, сколько необходимо в развитии, все они потом будут собраны в одной таблице стилей и так попадут на production.

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

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

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

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

Дальнейшее чтение

Компоненты

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

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

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

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

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

Шаблон 7-1

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

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

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

  • main.scss
Обои от Julien He
Обои от Julien He

В идеале, мы можем закончить с чем-то похожим:


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        # Стили, особые для главной страницы
|   |– _contact.scss     # Стили, особые для страницы контактов
|   …                    # и т.д.
|
|– themes/
|   |– _theme.scss       # Тема по умолчанию
|   |– _admin.scss       # Тема админа
|   …                    # и т.д.
|
|– utils/
|   |– _variables.scss   # Переменные Sass
|   |– _functions.scss   # Функции Sass
|   |– _mixins.scss      # Примеси Sass
|   |– _helpers.scss     # Помощники классов & placeholder’ов
|
|– vendors/
|   |– _bootstrap.scss   # Bootstrap
|   |– _jquery-ui.scss   # jQuery UI
|   …                    # и т.д.
|
|
`– main.scss             # главный файл Sass

Следующие файлы имеют то же соглашение по именованию, что и выше: они отделены нижним подчеркиванием.

Папка Base

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

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

Папка 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

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

Папка Utils

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

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

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

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

Папка 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. vendors/
  2. utils/
  3. base/
  4. layout/
  5. components/
  6. pages/
  7. themes/

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

  • один @import на строку;
  • не вставлять новые строки между файлами из одной папки;
  • новая строка после вставки последнего @import из одной и той же папки;
  • не писать расширения файлов и нижние подчеркивания.
@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 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
  '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';
@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

Для того, чтобы не импортировать каждый файл вручную, есть расширение Ruby Sass, которое называется sass-globbing, оно позволяет использовать glob-шаблон в @import Sass, например – @import "components/*".

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

Файл позора

Это интересная идея, которая стала популярна благодаря Гарри Робертсу, Дэйву Руперту и Крису Койеру и состоит в том, чтобы складывать все хаки и код, которым вы не гордитесь, в файл позора. Поэтому этот файл и имеет такое драматическое название – _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: (
  'seed': (min-width: 800px),
  'sprout': (min-width: 1000px),
  'plant': (min-width: 1200px),
);
$breakpoints: ('seed': (min-width: 800px), 'sprout': (min-width: 1000px), 'plant': (min-width: 1200px))
Дальнейшее чтение

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

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

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

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

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

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

Дальнейшее чтение

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

Не так давно были довольно жаркие дебаты о том, где именно должны быть описаны медиа-запросы: должны ли они быть в селекторах (как это позволяет 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. Из-за этого мы должны уделять много внимания при добавлении переменных из-за риска споров между ними.

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

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

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

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

Области видимости переменных в 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 – всё ещё замысловатая задумка, которая может принести больше вреда, чем пользы, особенно когда неправильно используется. Дело в том, что при расширении селектора, вы практически не имеете возможности ответить на вопросы о том, что происходит, не имея глубоких знаний всей кодовой базы:

  • где мой текущий селектор будет добавлен?
  • я, наверное, сейчас сделаю что-то нежелательное?
  • насколько много CSS генерируются одним указанием?

Чтобы вы знали, результат может различаться от ничего до создания катастрофических побочных эффектов. Из-за этого, мой совет – избегать указания @extend. Это может показаться жестоким, но, в конце концов, это может спасти вас от головной боли и неприятностей.

Как говорится:

Никогда не говори никогда.
— По-видимому, не Beyonce.

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

  • Используйте указание @extend внутри модуля, но не в разных модулях.
  • Используйте указание @extend исключительно на %placeholder‘ы, а не на обычные селекторы.
  • Убедитесь, что %placeholder, который вы расширяете, присутствует как можно реже в таблице стилей.

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

.foo {
  content: 'foo';
}

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

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

Вы не можете расширить внешний селектор из медиа-запроса с помощью @extend.
Вы можете делать @extend только с селекторами внутри одного и того же указания.

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

Если вы не можете использовать Gzip (или что-то похожее), то использование @extend может быть и не так плохо, пока вы понимаете, что делаете.

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

Дальнейшее чтение

Примеси

Примеси – одна из самых важных частей из всего языка 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 Hugo Giraudel
/// @param {Length} $width
/// @param {Length} $height
@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}
/// Помощник для определения размера
/// @author Hugo Giraudel
/// @param {Length} $width
/// @param {Length} $height
=size($width, $height: $width)
  width: $width
  height: $height
Дальнейшее чтение

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

Когда имеете дело с неизвестным количеством аргументов в примеси, используйте 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...)
Дальнейшее чтение

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

Написание пользовательских примесей для обработки префиксов для неподдерживаемых или частично поддерживаемых свойств 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 HugoGiraudel
/// @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 HugoGiraudel
/// @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)

Пожалуйста, помните о том, что это плохое решение. Например, это не поможет справиться со сложными placeholder’ами, такими как те, которые нужны для 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
  // 

При использовании условных операторов внутри функции для возврата другого результата, основанного на некоторых условиях, убедитесь, что @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($, $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 'Предполагаем, что #{$px} должно быть в пикселях, пытаемся явно преобразовать.';
    @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 'Предполагаем, что #{$px} должно быть в пикселях, пытаемся явно преобразовать.'
    @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)

Инструменты

Что приятно, так это, что 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 в процесс сборки Grunt, вам будет приятно знать, что есть расширение Grunt для этого – grunt-scss-lint.

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

Дальнейшее чтение

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

Подводя итог, мы хотим:

  • Отступ двумя (2) пробелами, никаких табов;
  • Строки шириной в 80 символов;
  • Правильно написанный многострочный CSS;
  • Осмысленное использование пробелов;
  • Строки и URL в одиночных кавычках;
  • Не опускать 0, обязательно ставить 0 в начале для чисел меньше 1;
  • Вычисления оборачивать в скобки;
  • Никаких магических чисел;
  • Цвета ключевыми словами > HSL > RGB > шестнадцатеричные;
  • Списки, разделённые запятыми;
  • Без завершающей запятой в списке, если он строчный;
  • Завершающие запятые в картах;
  • Не использовать вложенность селекторов, только для псевдоклассов и псевдоэлементов;
  • Именование через дефис;
  • Подробные комментарии;
  • SassDoc API для комментариев;
  • Ограниченное использование @extend;
  • Простые примеси;
  • Меньше циклов, насколько это возможно, без @while;
  • Уменьшенное число зависимостей;
  • Осмысленное использование ошибок и предупреждений.
Вернуться к началу