Как сгенерировать все ваши служебные классы с помощью Sass Maps
Одна из возможностей служебных классов заключается в предоставлении вам доступа к каждой небольшой концепции вашей дизайн-системы во множестве контекстов. Если ваш основной цвет - королевский синий, вы можете применить его как цвет текста к чему угодно с классом .text-royal-blue
, как цвет фона с классом .bg-royal-blue
и так далее.
Но как написать их эффективным, последовательным и масштабируемым способом?
TL; DR: в этом посте подробно рассказывается о том, как делать. Если вы хотите понять весь мыслительный процесс, читайте дальше. В противном случае вы можете загрузить код на GitHub или протестировать его на SassMeister.
$royal-blue: #0007ff; .text-royal-blue { color: $royal-blue; } .bg-royal-blue { background: $royal-blue; } ...
Это повторяется. Вы не только вручную набираете название и значение цвета каждый раз, но и создаете неподдерживаемую систему. Что произойдет, если у вас есть десять таких утилит для работы с цветом, и вам нужно добавить в схему еще один цвет?
Не следует тратить время на бессмысленные и утомительные дела. Вот для чего нужны языки сценариев. Если вы уже используете Sass, вам нужно использовать его мощь и позволить ему помочь вам.
Что такое карты Sass?
Карты - это тип данных Sass, который представляет связь между ключами и значениями. Если вы знакомы с другими языками сценариев, вы могли бы увидеть это как ассоциативный массив. Он позволяет хранить данные и иметь имя для ссылки на каждую часть.
Списки и карты немного похожи тем, что они хранят набор данных, и их можно итерировать в @each
цикле. Но в отличие от списков, карты позволяют легко ссылаться на любую часть информации, называя ее по имени. Это делает его идеальным для группировки логически связанной информации.
$colors: (
mako-grey: #404145,
fuel-yellow: #ecaf2d,
pastel-green: #5ad864
);
Добавим логики
Теперь, когда наши цвета аккуратно хранятся внутри карты, нам нужно выполнить итерацию для создания наших служебных классов. Для этого мы будем использовать директиву @each
внутри @mixin
, которую мы позже включим в наш базовый класс утилиты.
@mixin color-modifiers {
// do stuff
}
Теперь давайте воспользуемся директивой @each
, чтобы просмотреть $colors
и получить нужные данные.
@mixin color-modifiers {
@each $name, $hex in $colors {
// do stuff
}
}
Мы повторяем $colors
, и в каждом цикле ссылка на текущий ключ будет в $name
, а шестнадцатеричный код цвета будет в $hex
. Мы можем приступить к созданию нашего набора правил.
@mixin color-modifiers {
@each $name, $hex in $colors {
&-#{$name} {
color: $hex;
}
}
}
Теперь для каждой пары на карте @each
будет генерировать набор правил, который ссылается на родительский селектор с символом &
, добавляет дефис и имя цвета и устанавливает атрибут color
на текущее шестнадцатеричное значение.
Другими словами, делаем это:
.text {
@include color-modifiers;
}
Сгенерирует это:
.text-mako-grey {
color: #404145;
}
.text-fuel-yellow {
color: #ecaf2d;
}
.text-pastel-green {
color: #5ad864;
}
Довольно аккуратно, а? Собственно, мы почти не поцарапали поверхность. На данный момент наш миксин может выводить правила только с атрибутом color
. Что, если мы хотим создать несколько служебных классов для цветов фона?
К счастью, Sass позволяет нам передавать аргументы миксинам.
@mixin color-modifiers($attribute: 'color') {
@each $name, $hex in $colors {
&-#{$name} {
#{$attribute}: $hex;
}
}
}
Теперь мы можем точно указать, какой атрибут нам нужен.
Давайте еще немного улучшим наш миксин: сейчас префикс модификатора - это жестко запрограммированный дефис. Это означает, что ваши классы всегда будут иметь форму .base-modifier
. Что делать, если вам нужно это изменить? Что, если вы также хотите сгенерировать модификаторы со вкусом БЭМ (два дефиса)? Опять же, этого можно добиться, используя аргументы.
@mixin color-modifiers($attribute: 'color', $prefix: '-') {
@each $name, $hex in $colors {
&#{$prefix}#{$name} {
#{$attribute}: $hex;
}
}
}
Теперь мы можем генерировать классы модификаторов с любым желаемым префиксом. Итак, делаем это:
.text {
@include color-modifiers($prefix: '--');
}
Сгенерирует это:
.text--mako-grey {
color: #404145;
}
.text--fuel-yellow {
color: #ecaf2d;
}
.text--pastel-green {
color: #5ad864;
}
Совет от профессионалов: в Sass вы можете явно указывать аргументы при вызове миксина или функции (как в примере выше). Это позволяет избежать их упорядочивания.
Карты на картах
Мне нравится использовать немного другую цветовую систему, чтобы управлять тональными вариациями. Вкладывая карты в карты, я получаю чистый и удобочитаемый способ группировки оттенков.
$colors: (
grey: (
base: #404145,
light: #c7c7cd
),
yellow: (
base: #ecaf2d
),
green: (
base: #5ad864
)
);
Если мы хотим работать с такой цветовой системой, нам нужно адаптировать наш миксин, чтобы он повторял более глубокий уровень.
@mixin color-modifiers($attribute: 'color', $prefix: '-', $separator: '-') {
@each $name, $color in $colors {
&#{$prefix}#{$name} {
@each $tone, $hex in $color {
&#{$separator}#{$tone} {
#{$attribute}: $hex;
}
}
}
}
}
Мы добавили новый аргумент $separator
, чтобы связать название цвета и тон. Мы могли бы использовать $prefix
, но он не имеет той же цели. Использование выделенной переменной со значением по умолчанию - лучший выбор, поскольку это дает нам полную свободу при использовании миксина.
Теперь сделаем это:
.text {
@include color-modifiers;
}
Сгенерирует это:
.text-grey-base {
color: #404145;
}
.text-grey-light {
color: #c7c7cd;
}
.text-yellow-base {
color: #ecaf2d;
}
.text-green-base {
color: #5ad864;
}
Большой! Теперь у нас есть помощники, состоящие из базового класса, цвета и тона. Одна вещь, которую нам нужно улучшить, - это то, как выводятся модификаторы базового цвета. На самом деле нам не нужен этот -base
суффикс - достаточно базового класса и цвета.
Что мы должны сделать, так это проверить тон во вложенном @each
цикле и выводить его и $separator
только тогда, когда он не является «базовым». К счастью для нас, в Sass уже есть все, что нам нужно.
@if, @else, если ()
Нашим первым побуждением могло бы стать использование директив @if/@else
. Проблема в том, что это заставит нас повторить код и приведет к сложному коду. Вместо этого мы собираемся использовать одно из секретных орудий Sass: if()
.
if()
- это условный (тернарный) оператор Sass. Он принимает три аргумента: условие и два оператора возврата. Если условие выполнено, if()
вернет первый оператор. В противном случае вернет второй. Вы можете видеть это как @if/@else
сокращение.
@mixin color-modifiers($attribute: 'color', $prefix: '-', $separator: '-', $base: 'base') {
@each $name, $color in $colors {
&#{$prefix}#{$name} {
@each $tone, $hex in $color {
&#{if($tone != $base, #{$separator}#{$tone}, '')} {
#{$attribute}: $hex;
}
}
}
}
}
Каждый раз, когда вложенный цикл @each
будет анализировать $tone
, отличный от «base», он будет возвращать $separator
и $tone
в качестве суффикса класса. В противном случае он ничего не вернет, оставив класс как есть.
.text-grey {
color: #404145;
}
.text-grey-light {
color: #c7c7cd;
}
.text-yellow {
color: #ecaf2d;
}
.text-green {
color: #5ad864;
}
СУХАЯ все это
В реальном проекте вы, скорее всего, захотите использовать различные структуры карт. Например, у вас могут быть одноуровневые глубокие карты для размеров шрифтов и двухуровневые глубокие карты для цветов. Вы не собираетесь писать разные миксины для каждого уровня глубины. Это было бы повторяющимся и недостижимым. Чтобы справиться с этим, вам нужно полагаться на один миксин.
Нам нужен универсальный миксин для генерации всех модификаторов, способный обрабатывать многомерные карты. Если вы сравните два миксина, которые мы придумали в этом руководстве, вы заметите, что они очень похожи. Единственное отличие состоит в том, что перед печатью вычисленного объявления CSS выполняется дополнительный цикл. Это типичная работа для рекурсивного микширования.
Он начнется с директивы @each
, в которой мы можем начать создавать наш селектор. Здесь мы проверим, совпадает ли текущий $key
с «базовым», чтобы мы могли решить, выводить его или нет. Затем мы проверим, является ли текущий $value
картой: если да, нам нужно снова запустить миксин с того места, где мы находимся, и передать ему вложенную карту. В противном случае мы можем распечатать объявление CSS.
@mixin modifiers($map, $attribute, $prefix: '-', $separator: '-', $base: 'base') {
@each $key, $value in $map {
&#{if($key != $base, #{$prefix}#{$key}, '')} {
@if type-of($value) == 'map' {
@include modifiers($value, $attribute, $separator);
}
@else {
#{$attribute}: $value;
}
}
}
}
И вуаля! Этот миксин будет работать с картами любой глубины. Не стесняйтесь использовать его в своих проектах! Если он вам нравится, вы можете проявить немного любви, поставив его на GitHub. Кроме того, если вы хотите улучшить его, оставьте, пожалуйста, комментарий по сути, чтобы я мог его обновить 👍
Первоначально опубликовано на frontstuff.io.