Sass: разрабатываем дизайн-систему

от автора

Привет, друзья!

В данной статье мы разработаем простую, но относительно полноценную дизайн-систему для веб-приложения средствами Sass.

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

При разработке дизайн-системы в части терминологии, названий, значений переменных и т.п. я буду ориентироваться, в основном, на Bootstrap и немного на Tailwind.

Код проекта на GitHub.

Если вам это интересно, прошу под кат.

Для работы с зависимостями будет использоваться Yarn.

Для тестирования дизайн-системы можно воспользоваться одним из готовых шаблонов приложений, предоставляемых Vite, например:

# создаем шаблон приложения # sass-style - название приложения и директории проекта # --template vanilla - используемый шаблон yarn create vite sass-style --template vanilla # переходим в созданную директорию и устанавливаем зависимости cd sass-style && yarn # устанавливаем `sass` в качестве зависимости для разработки yarn add -D sass # запускаем приложение в режиме разработки yarn dev

Создаем в корне проекта директорию styles и в ней — файл index.scss. Это основной файл стилей нашего приложения. Убедитесь, что он импортируется в файле main.js:

import "./styles/index.scss";

Сброс стилей

Реализуем минимальный сброс стилей.

Создаем директорию styles/utils и в ней — файл _reset.scss следующего содержания:

*, *::before, *::after {   box-sizing: border-box; }  body, h1, h2, h3, h4, h5, p, figure, picture {   margin: 0; }  h1, h2, h3, h4, h5, h6, p {   font-weight: 400; }  img, picture {   display: block;   max-width: 100%; }  input, button, textarea, select {   font: inherit; }  @media (prefers-reduced-motion: reduce) {   *,   *::before,   *::after {     animation-duration: 0.01ms !important;     animation-iteration-count: 1 !important;     scroll-behavior: auto !important;     transition-duration: 0.01ms !important;   } }

Импортируем этот файл в index.scss и определяем еще парочку дефолтных стилей для html и body:

@import "./utils/reset";  html {   // о переменных см. ниже   font-size: $fs-base; }  body {   font-family: Helvetica, sans-serif;   color: $dark;   line-height: 1.5; }

Переменные / Variables

Создаем файл styles/_vars.scss и определяем в нем несколько переменных:

// минимальная и максимальная ширина области просмотра $min-w: 375px; $max-w: 1536px;  // отступы $spacer-base: 30px;  $spacer-xxs: 0.25 * $spacer-base; $spacer-xs: 0.5 * $spacer-base; $spacer-s: 0.75 * $spacer-base; $spacer-m: $spacer-base; $spacer-l: 1.5 * $spacer-base; $spacer-xl: 2 * $spacer-base; $spacer-xxl: 2.25 * $spacer-base;  // шрифты $fs-base: 16px;  $fs-xxs: 11px; $fs-xs: 12px; $fs-s: 14px; $fs-m: $fs-base; $fs-l: 22px; $fs-xl: 24px; $fs-xxl: 26px;  // цвета // bootstrap $primary: #0275d8; $success: #5cb85c; $info: #5bc0de; $warning: #f0ad4e; $danger: #d9534f; $light: #f7f7f7; $dark: #292b2c; // дополнительно $gray100: #fefefe; $gray200: #fbfbfb; $gray300: #fafafa; $gray400: #f3f3f3; $gray500: #eeeeee; $gray600: #a3a3a3; $gray700: #8c8c8c; $gray800: #555555; $gray900: #333333; $white: #fff; $black: #000;  // контрольные точки // bootstrap $sm: 576px; $md: 768px; $lg: 992px; $xl: 1200px; $xxl: 1400px;

Импортируем этот файл в начале index.scss:

@import "./vars";

Это делает переменные доступными глобально — переменные будут доступны всем импортируемым в index.scss модулям.

Подробнее о переменных Sass можно почитать здесь.

Отступы

Создаем файл utils/_spacing.scss следующего содержания:

// перечисление отступов $spacers: (   "0": 0,   xxs: $spacer-xxs,   xs: $spacer-xs,   s: $spacer-s,   m: $spacer-m,   l: $spacer-l,   xl: $spacer-xl,   xxl: $spacer-xxl );  // типы отступов $types: (   "m": "margin",   "p": "padding" );  // стороны $sides: (   "": "",   t: "-top",   r: "-right",   b: "-bottom",   l: "-left" );  // перебираем перечисление @each $key-spacer, $factor in $spacers {   // перебираем типы   @each $key-type, $type in $types {     // перебираем стороны     @each $key-side, $side in $sides {       // для всех и каждой стороны       .#{$key-type}#{$key-side}-#{$key-spacer} {         #{$type}#{$side}: $factor;       }     }      // для горизонтального отступа     .#{$key-type}x-#{$key-spacer} {       #{$type}-left: $factor;       #{$type}-right: $factor;     }      // для вертикального отступа     .#{$key-type}y-#{$key-spacer} {       #{$type}-bottom: $factor;       #{$type}-top: $factor;     }   }    // дополнительно   // пространство между гридами или флексами   .gap-#{$key-spacer} {     gap: $factor;   } }

О директиве @each можно почитать здесь.

Это позволяет определять внешние (margin) и внутренние (padding) отступы следующим образом:

  • m или p (в дальнейшем паддинг предполагается) — для отступов по всем сторонам, внешним или внутренним, соответственно. Например, m-xs компилируется в:

margin-top: 15px; margin-right: 15px; margin-bottom: 15px; margin-left: 15px;

  • mx/my — для отступов по горизонтали/вертикали. Например, mx-s компилируется в:

margin-right: 15px; margin-left: 15px;

  • mt/mr/mb/ml — для отступов с соответствующей стороны. Например, mt-s компилируется в:

margin-top: 15px;

Цвета

Создаем файл utils/_colors.scss следующего содержания:

// перечисление цветов $colors: (   'gray100': $gray100,   'gray200': $gray200,   'gray300': $gray300,   'gray400': $gray400,   'gray500': $gray500,   'gray600': $gray600,   'gray700': $gray700,   'gray800': $gray800,   'gray900': $gray900,   'primary': $primary,   'success': $success,   'info': $info,   'warning': $warning,   'danger': $danger,   'light': $light,   'dark': $dark,   'white': $white,   'black': $black );  // перебираем перечисление @each $name, $color in $colors {   // цвет текста   .color-#{$name} {     color: $color;   }    // цвет фона   .bg-#{$name} {     background-color: $color;   }    // заливка `svg`   .fill-#{$name} {     svg {       color: $color;     }   } }

Это позволяет определять цвет текста, фона или заливки для SVG. Например, color-primary компилируется в:

color: #0275d8;

bg-success в:

background-color: #5cb85c;

Размеры шрифтов и блоков

Создаем файл utils/_font.scss следующего содержания:

// перечисление размеров шрифта $font-sizes: (   "0": 0,   xxs: $fs-xxs,   xs: $fs-xs,   s: $fs-s,   m: $fs-m,   l: $fs-l,   xl: $fs-xl,   xxl: $fs-xxl );  // перебираем перечисление @each $size, $factor in $font-sizes {   .fs-#{$size} {     font-size: $factor;   } }  // перебираем возможные значения свойства `font-weight` @each $v in 100, 200, 300, 400, 500, 600, 700, 800, 900 {   .fw-#{$v} {     font-weight: $v;   } }

fs-s компилируется в:

font-size: 14px;

fw-600 в:

font-weight: 600;

Создаем файл utils/_sizing.scss следующего содержания:

@each $v in 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 {   .w-#{$v} {     width: calc(100% / 12 * $v);   }    .h-#{$v} {     height: calc(100% / 12 * $v);   } }  // дополнительно .w-full {   width: 100vw; }  .h-full {   height: 100vh; }

Это позволяет распределять ширину и высоту блоков по сетке, состоящей из 12 равных частей. Об этом можно думать так: сколько колонок/строк должен занимать блок? Например, w-4 компилируется в:

width: 33.3333333333%;

h-2 в:

height: 16.6666666667%;

Пример:

<div style="display: flex" class="w-full h-full">   <div class="w-2 h-4 bg-primary"></div>   <div class="w-6 h-4 bg-success"></div>   <div class="w-4 h-4 bg-danger"></div> </div>

Результат:

Флекс

Создаем файл utils/_flex.scss следующего содержания:

// justify-content @each $v in start, end, center, stretch, between, around, evenly {   .justify-#{$v} {     @if ($v == start) or ($v == end) {       justify-content: flex-#{$v};     } @else if ($v == between) or ($v == around) or ($v == evenly) {       justify-content: space-#{$v};     } @else {       justify-content: $v;     }   } }  // align-items @each $v in start, end, center, stretch {   .items-#{$v} {     @if ($v == start) or ($v == end) {       align-items: flex-#{$v};     } @else {       align-items: $v;     }   } }  // дополнительно .flex-center {   // о миксинах см. ниже   @include flex-center(); }  .flex-center-column {   @include flex-center(column); }  .flex-wrap {   flex-wrap: wrap; }  .flex {   flex: 1; }

При желании, таким же способом можно определить justify-items, align-content, justify-self и align-self.

Утилиты

Создаем файл utils/_utils.scss следующего содержания:

// display @each $v in none, inline, block, inline-block, flex, inline-flex, grid,   inline-grid {   .d-#{$v} {     display: $v;   } }  // position @each $v in relative, absolute, fixed, sticky {   .p-#{$v} {     position: $v;   } }  // контент для читалок // visually-hidden .sr-only {   background: none;   border: none;   color: none;   cursor: none;   height: 0;   margin: 0;   opacity: 0;   outline: none;   overflow: hidden;   padding: 0;   pointer-events: none;   position: absolute;   user-select: none;   visibility: hidden;   white-space: nowrap;   width: 0;   z-index: -1; }  // дополнительно .text-overflow {   overflow: hidden;   text-overflow: ellipsis;   white-space: nowrap; }  .text-center {   text-align: center; }  // ваши классы-утилиты

Перепишем последний пример:

<div class="w-full h-full d-flex gap-xxs">   <div class="w-2 h-4 bg-primary flex-center">     <p class="fs-xl color-success">box#1</p>   </div>   <div class="w-6 h-4 bg-success d-flex justify-end items-end">     <p class="pr-s fs-xl color-primary">box#2</p>   </div>   <div class="w-4 h-4 bg-danger d-flex items-end">     <p class="pl-s fs-xl color-gray300">box#3</p>   </div> </div>

Результат:

Не забываем импортировать все утилиты в index.scss.

На этом с основами дизайн-системы мы закончили. Остальное зависит от потребностей конкретного приложения и личных предпочтений разработчика.

Бонус

Функции / Functions

В числе прочего, Sass позволяет создавать функции для выполнения операций над значениями свойств CSS.

Определим функцию для преобразования px в rem.

Создаем файл styles/_fns.scss следующего содержания:

@function strip-unit($v) {   @return $v / ($v * 0 + 1); }  @function rem($v) {   @return #{strip-unit($v / $fs-base)} + rem; }

Функция strip-unit позволяет игнорировать px в передаваемом функции значении.

Пример использования функции rem:

p {   font-size: rem(16);   // или   font-size: rem(16px);   // или   font-size: rem($fs-m); }  // после компиляции p {   font-size: 0.8888888889rem }

Подробнее о функциях можно почитать здесь.

Миксины / Mixins

Миксин — это блок готовых стилей, которые включаются в другие стили.

Миксин определяется с помощью директивы @mixin и внедряется с помощью директивы @include.

Создаем файл styles/_mixins.scss.

Определяем миксин для выравнивания элемента по центру с помощью display: flex:

@mixin flex-center($d: row) {   display: flex;   justify-content: center;   align-items: center;    @if ($d == column) {     flex-direction: column;   } }

Мы видели пример использования данного миксина в utils/_flex.scss.

О директивах @if и @else можно почитать здесь.

Определяем миксин для стилизации плейсхолдера:

@mixin placeholder {   &.placeholder {     @content;   }   &:-moz-placeholder {     @content;   }   &::-moz-placeholder {     @content;   }   &:-ms-input-placeholder {     @content;   }   &::-webkit-input-placeholder {     @content;   } }

Директива-переменная @content содержит стили, передаваемые миксину.

Пример использования:

input {   @include placeholder {     color: $primary;     font-size: 0.8rem;     opacity: 0.8;   } }

Результат после компиляции:

input.placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; } input:-moz-placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; } input::-moz-placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; } input:-ms-input-placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; } input::-webkit-input-placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; }

Определяем миксин для стилизации прокрутки:

@mixin scrollbar($scrollbar-width, $thumb-color, $track-color) {   &::-webkit-scrollbar {     width: #{$scrollbar-width} + px;   }    &::-webkit-scrollbar-track {     background-color: $track-color;   }    &::-webkit-scrollbar-thumb {     background-color: $thumb-color;   }    // firefox   & {     @if ($scrollbar-width == 0) {       scrollbar-width: none;       // приблизительное значение     } @else if ($scrollbar-width < 19) {       scrollbar-width: thin;     } @else {       scrollbar-width: auto;     }     scrollbar-color: $thumb-color $track-color;   } }

Пример использования:

@use "sass:color";  html {   @include scrollbar(12, lighten($primary, 25), $gray500); }

Функция lighten из встроенного модуля sass:color позволяет осветлять цвета.

Определяем миксины — альтернативу медиа-запросам:

// перечисление контрольных точек $breakpoints: (   "sm": $sm,   "md": $md,   "lg": $lg,   "xl": $xl,   "xxl": $xxl );  // от указанной точки и шире @mixin up($b) {   @if map-has-key($breakpoints, $b) {     $v: map-get($breakpoints, $b);      @media (min-width: $v) {       @content;     }   } }  // от указанной точки и уже @mixin down($b) {   @if map-has-key($breakpoints, $b) {     $v: map-get($breakpoints, $b);      @media (max-width: $v) {       @content;     }   } }

Пример использования up:

h1 {   font-size: rem(18);    // > 768px   @include up(md) {     font-size: rem(24);     font-weight: bold;   }    // > 1200px   @include up(xl) {     font-size: rem(30);   } }

В качестве последнего упражнения — миксин для изменения размера шрифта пропорционально изменению ширины области просмотра:

@mixin fluid-fs($min-w, $max-w, $min-fs, $max-fs) {   $u1: unit($min-w);   $u2: unit($max-w);   $u3: unit($min-fs);   $u4: unit($max-fs);    @if $u1 == $u2 and $u1 == $u3 and $u1 == $u4 {     & {       font-size: $min-fs;        @media (min-width: $min-w) {         font-size: calc(           #{$min-fs} + #{strip-unit($max-fs - $min-fs)} *             ((100vw - #{$min-w}) / #{strip-unit($max-w - $min-w)})         );       }        @media (min-width: $max-w) {         font-size: $max-fs;       }     }   } }

Пример использования:

html {   @include fluid-fs($min-w, $max-w, $fs-xs, $fs-xl); }

При изменении ширины области просмотра размер шрифта элемента html (:root) будет пропорционально меняться от 12 до 24px. В случае применения единиц измерения rem, размер шрифта вычисляется от базового, т.е. размера шрифта html. Таким образом, заранее позаботившись об установке размеров в rem или применении одноименной функции, можно автоматически получить гибкую систему шрифтов во всем приложении.

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

Благодарю за внимание и happy coding!



ссылка на оригинал статьи https://habr.com/ru/company/timeweb/blog/677866/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *