Классный CSS: подход к таблицам стилей Sass как к программе
Перевод статьи Classy CSS: a Programmatic Approach to Sass Stylesheets с сайта una.im, опубликовано на css-live.ru с разрешения автора — Юны Кравец.
Я начала писать этот пост через несколько недель после выхода полусатирической статьи на Sitepoint с моим взглядом на смесь нынешних тенденций систем наименования и организации CSS, под названием «Атомный OOBEMITSCSS». Это было ещё в августе, но на меня навалилось много дел. Я назвала её «Атомный OOBEMITSCSS» в шутку, но люди начали применять это название и использовать его в реальных проектах (что, если честно, было довольно забавно, поскольку при встрече меня стали засыпать вопросами об этом). Споры за и против использования @extend
на SassConf в этом году (и в Twitter в последнее время) напомнили мне об этой идее.
Классный CSS, пожалуйста
В вышеупомянутой статье («Атомный OOBEMITSCSS») я объясняла, как я размечаю компоненты (на примере Pinterest) и оформляю их соответственно. Я по-прежнему использую этот пост в качестве удобного руководства для всех, кто интересуется нашей фронтенд-архитектурой, которая с тех пор прошла полномасштабную проверку на большой библиотеке паттернов.
Один пример из статьи на Sitepoint
В результате я столкнулась с необходимостью объяснять эту систему (и сам Sass) людям с традиционным программистским образованием. Благодаря чему я осознала, насколько система CSS-архитектуры, которую я описала, похожа на программу и основана на классах. Поэтому я хочу воспользоваться моментом, чтобы дать ей заслуженное название, за которое мне не было бы стыдно, «Классный CSS», а также объяснить ее так, чтобы это оказалось чуть ближе традиционным программистам.
У этого поста три цели:
- Отстоять
@extend
- Познакомить с Классным CSS (выговорить намного легче, чем «Атомный OOBEMITSCSS»)
- Рассказать о масштабируемом, модульном и основанном на классах подходе к CSS
Классная кнопка
Ну что, давайте разберём как это работает, на примере кнопки (ведь кнопка — это CSSный «hello world»):
Начнём с заготовки селектора с базовым кодом для каждой кнопки, используя чуть подправленный синтаксис, похожий на БЭМ. Первая часть его имени будет объектом, на который мы ссылаемся (button or btn), а после двойного дефиса будет модификатор (в данном случае base, т.е. базовый). Заметьте: переменные именуются таким же образом — тип переменной в роли основы (в примере ниже это color).
$color--primary: #b29; $color--secondary: #19d; %btn--base { border: 1px solid currentColor; border-radius: 1.5em; background: none; outline: none; transition-duration: .25s; cursor: pointer; margin: 30px; padding: .5em 1em; &:hover { color: white; background: black; } }
Эта базовая кнопка — наш класс кнопок. К этому мы будем добавлять всё остальное для каждой кнопки, поскольку каждая кнопка — экземпляр этого класса. Каждый элемент-кнопка имеет тип «кнопка», поэтому у них всех есть общие черты (сплошная обводка, прозрачный фон, и т.д.). Каждая кнопка вбирает в себя одни и те же общие свойства и надстраивается над ними. Они не перезаписывают эти свойства, а добавляют к ним новые. Все они — кнопки++, наследующие свойства базовой кнопки. (Надо ли еще как-нибудь повторить эту же мысль?)
%btn--primary { @extend %btn--base; color: $color--primary; font-size: 1.5em; } %btn--secondary { @extend %btn--base; color: $color--secondary; font-size: 1.1em;
А вот их использование:
.hero__btn { @extend %btn--primary; margin: 2em; } .sidebar__btn { @extend %btn--secondary } .global-nav__btn--login { @extend %btn--secondary; margin-right: 1em; }
Иллюстрация прототипного наследования из «Вы не знаете JS: Ключевое слово this и прототипы объектов»
Если вы пишете их «классным» способом, у каждого компонента (hero, sidebar, global-nav) будет его собственный частичный файл .scss, где создаются экземпляры класса для реального использования. Это может выглядеть так:
_hero.scss
.hero { ... &__btn { @extend %btn--primary; } ... }
_sidebar.scss
.sidebar { ... &__btn { @extend %btn--secondary; } ... }
_global-nav.scss
.global-nav { ... &__btn { @extend %btn--secondary; &--login { @extend .global-nav__btn; margin-right: 1em; // в этом месте вы // оформляете .global-nav__btn--login } } }
Так, а что насчёт миксинов?
Вам может быть интересно, почему бы вместо этого не использовать @mixin
? Наш способ разбивки классов по файлам ближе к тому, для чего придуман и предназначен @extend
. Можно создавать миксины в классах-заготовках, над которыми мы надстраиваем стили, а затем использовать @extend
, чтобы создать экземпляры классов с этими стилями.
В защиту @extend
Применение @extend
позволяет уменьшить итоговый файл CSS и сделать код чище, если понимать, что происходит (мы дополняем стили, а не дублируем их). Можно возразить, что это не проблема, есть же gzip — но всегда полагаться на gzip нельзя, ведь доступа к настройкам сервера может не оказаться.
Чтобы понимать и применять @extends
с пользой, нужно как следует разобраться, что именно происходит и в чем коренные различия межу @mixin
и @extend
. Вот наглядный пример:
@mixin
— это как штамповка: он создает дубликат блока со свойствами, с необязательной добавкой в виде переданных аргументов.@extend
добавляет тот элемент, который вы расширяете, к блоку свойств. Это всё равно что сказать «Да, и еще ___».
Это значит, что наш итоговый CSS-файл будет меньше, ведь мы не повторяем блок кода каждый раз, когда он используется. C @extend
мы можем просто ссылаться на свойства. Директивы @extend идеально подходят для этого! Их логическое предназначение вполне разумно. #teamExtend
Недостаток здесь в том, что мы не знаем, где находится блок свойств, поскольку мы не создаем новую копию блока кода в директиве, а лишь ссылаемся на него как на источник стилей (вот почему нельзя использовать @extend
в медиавыражениях). Но это неважно, если мы следуем принципам Классного CSS. Вы расширяете классы-заготовки, превращая их в настоящие, годные к использованию классы, и работаете с экземплярами этих классов, а директивы @mixin
резервируются для другой цели. С их помощью мы выстраиваем в классах-заготовках тот код, который затем расширяем.
В случае Классного CSS, миксины напоминают конструкторы и используются только в заготовках селекторов.
Например, давайте создадим миксин кнопки, чтобы на его основе было легче достраивать основную и дополнительную кнопки:
// двоеточие после аргумента означает значение по умолчанию // создает функцию-конструктор (миксин) @mixin btn-me($color: hotpink, $size: normal) { border: 1px solid $color; border-radius: 1.5em; background: none; outline: none; transition-duration: .25s; cursor: pointer; margin: 30px; padding: .5em 1em; @if $size == 'small' { font-size: .8em; } @else { font-size: 1.2em; } &:hover { color: white; background: $color; } } // создает классы-заготовки как основу для будущего расширения, на которую мы будем ссылаться %btn--primary { @include btn-me; // если аргументов нет, то подставляется значение по умолчанию } %btn--secondary { @include btn-me(blue, small) } // получаем из кода экземпляр с семантическим именем // только здесь мы пишем компилируемый код .hero__btn { @extend %btn--secondary; }
И итоговый файл CSS выглядит так:
.hero__btn { border: 1px solid blue; border-radius: 1.5em; background: none; outline: none; transition-duration: .25s; cursor: pointer; margin: 30px; padding: .5em 1em; font-size: .8em; } .hero__btn:hover { color: white; background: blue; }
Преимущества
Конечно, у этой системы свой характер, и она может показаться сложноватой для непосвящённых, но как только вы начнёте с ней работать, она здорово поможет придумывать имена и решать вопросы архитектуры
В числе преимуществ:
- Гарантирует такую организацию кода, что в нем не случится конфликта специфичностей и ничего не будет переопределяться (см. ITCSS) — а значит, CSS становится проще, эффективнее, а размер итогового CSS-файла — меньше.
- Последовательная система наименования (БЭМ) сохраняет единообразие в команде и уменьшает путаницу благодаря общим правилам.
- Система организована, легко поддерживается и масштабируется
Пишите классный код, и пусть Sass поможет вам воплотить самые дерзкие идеи! 💁
Ну всё, на этом откланиваюсь
P.S. Это тоже может быть интересно:
Я этим подходом уже год пользуясь. Однако надо добавить что екстенд и сасс плохое сочетание тк в сасс екстенд весьма урезан(не цепляет вложенные классы, не дружит с медиа) поэтому использовать стоит или лесс или стайлус.
Можно обойтись без extend если уже используется БЭМ, как раз для этого и существуют модификаторы. Ссылка.