Печатает... Список заметок

Дневники дизайн-системы. Выпуск 5. Ящик и кнопки

Предыдущие выпуски:

  1. Вступление
  2. Цвета и типографика
  3. Система отступов
  4. Структура страниц

В прошлой записи рассказал об устройстве скелета страницы. Но что если нужно организовать элементы внутри блока, при этом описывать новую сущность не хочется? Для этих целей я сделал ящик — .box, блок создающий изолированную структурную единицу внутри других блоков.

Ящик → .box

Ящик повторяет устройство каркаса страниц, в нём есть секция, контейнеры и строки.

  • В роли секции выступает сам блок — .box
  • В секцию вкладываются контейнеры .box__container
  • В контейнерах лежат строки .box__row
Общее устройства блока .box
Рис. 1. Общее устройства блока .box

Важно соблюдать иерархию вложенности, но при этом не обязательно использовать все элементы.

Можно:

  • Сразу расположить контент в ящике, не используя контейнеры и строки
  • Вложить контент в контейнер, не используя строку.
  • Вложить строку в ящик, опустив контейнер.
  • Расположить строку после контейнера.

Нельзя:

  • Вкладывать контейнер в строку.
Хорошие и плохие примеры организации элементов блока .box
Рис. 2. Хорошие и плохие примеры организации элементов блока .box

Ящики и их части — структурные единицы страницы. Поэтому отступы для них подобраны согласно шкале, определённой в третьем выпуске: 8, 16, 24, 32, 52, 104 пикселя. Три больших значения используются для интервалов скелета страницы, три меньших отошли ящику.

Таким образом:

  • ящик от ящика отстоит на 24 пикселя;
  • контейнер от контейнера на 16 пикселей;
  • строка от строки на 8 пикселей.

При этом у соседних элементов интервал задаёт старший элемент, то есть если поставить строку после контейнера то интервал между ними будет равен 16 пикселям.

На экранах мобильных устройств шкала сдвигается влево на шаг: 24, 16 и 8 пикселей превращаются в 20, 12 и 4 пикселя, соответственно.

Система интервалов между элементами блока .box
Рис. 3. Система интервалов между элементами блока .box

Кнопка → .button

Работу над стандартной кнопкой, первым полноценным компонентом дизайн-системы, я начал с определения списка требований.

Моя кнопка будет иметь три стиля:

  • кнопка первого цвета;
  • кнопка второго цвета;
  • светлая, нейтральная кнопка.

Для кнопки каждого из стилей надо предусмотреть четыре состояния:

  • дефолтное;
  • при наведении курсора мыши;
  • кнопка в фокусе;
  • выключенная кнопка.

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

  • кнопка с иконкой и подписью;
  • кнопка с одним текстом;
  • кнопка с иконкой без текста.

Для каждой кнопки в моей системе будет предусмотрено три размера:

  • маленькая,
  • стандартная,
  • большая.

Итого: 3 × 4 × 3 × 3 = 108 вариантов предстоит сделать в фигме.

Кнопка в фигме

Кнопка состоит из двух элементов: текстового лейбла и коробочки с иконкой.

Кнопка в фигме. Внешний вид и организация слоёв.
Рис. 4. Кнопка в фигме. Внешний вид и организация слоёв.

Фон, скругление углов, поля, интервал между элементами определяют параметры контейнера .button. Поля и интервалы заданы при помощи фигмовской функции «авто-лэйаут».

Внутри лэйбла лежит текстовый фрейм, внутри коробочки с иконкой — иконка (пиктограммы отдельными компонентами вынесены на страницу _ icons and other graphic).

Для кнопки и всех её элементов, кроме иконки установлен параметр «авто-лэйаута» hug contents, что позволяет компоненту подстраиваться под ширину надписи.

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

Получил вот такой набор.

Обзорная иллюстрация всех вариантов стилей и состояний кнопки
Рис. 5. Обзорная иллюстрация всех вариантов стилей и состояний кнопки

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

Стили, размеры, состояния теперь переключаются при помощи простого меню.

Настройки компонента кнопки в фигме
Рис. 6. Настройки компонента кнопки в фигме

Вёрстка кнопки

Структура кнопки в ХТМЛ идентична её устройству в фигме.

<button class="button button_l button_standart" aria-label="Label">
    <div class="button__icon">
        <svg fill="none" viewBox="0 0 24 24" preserveAspectRatio="none"><path clip-rule="evenodd" d="..." fill-rule="evenodd"/></svg>
    </div>
    <span class="button__label">Label</span>
</button>

Кнопке присваивается базовый класс .button с двумя модификаторами, определяющими размер и стиль.

.button {
  display: flex;
  justify-content: center;
  align-items: center;
  width: fit-content;
  border: none;
  border-radius: 12px;
  cursor: pointer;
  outline: none;
  text-decoration: none;
}
.button:disabled {
  cursor: default;
}

.button_s — маленькая кнопка.

.button_m — средняя.

.button_l — большая.

// sizes
.button_s {
  padding: 6px 12px;
}
.button_s .button__icon {
  width: 20px;
  height: 20px;
}
.button_s .button__icon + .button__label {
  margin-left: 6px;
}
.button_s .button__label {
  @include text-style__caption-bold;
}
.button_m {
  padding: 8px 16px;
}
.button_m .button__icon {
  width: 24px;
  height: 24px;
}
.button_m .button__icon + .button__label {
  margin-left: 8px;
}
.button_m .button__label {
  @include text-style__text-bold;
}
.button_l {
  padding: 14px 28px;
}
.button_l .button__icon {
  width: 24px;
  height: 24px;
}
.button_l .button__icon + .button__label {
  margin-left: 8px;
}
.button_l .button__label {
  @include text-style__headline;
}

Состояния стилизуются для кнопки каждого цвета отдельно.

// styles
.button_standart {
  background-color: $color__first_standard;
}
.button_standart .button__label {
  color: $color__white;
}
.button_standart .button__icon svg path {
  fill: $color__white;
}
.button_standart:hover {
  background-color: $color__first_medium;
}
.button_standart:focus {
  background-color: $color__first_medium;
  @include outline;
}
.button_standart:disabled {
  background-color: $color__gray_7;
}
.button_standart:disabled:hover {
  background-color: $color__gray_7;
}
.button_standart:disabled .button__label {
  color: $color__gray_5;
}
.button_standart:disabled .button__icon svg path {
  fill: $color__gray_5;
}
.button_signal {
  background-color: $color__second_standard;
}
.button_signal .button__label {
  color: $color__white;
}
.button_signal .button__icon svg path {
  fill: $color__white;
}
.button_signal:hover {
  background-color: $color__second_medium;
}
.button_signal:focus {
  background-color: $color__second_medium;
  @include outline;
}
.button_signal:disabled {
  background-color: $color__gray_8;
}
.button_signal:disabled:hover {
  background-color: $color__gray_8;
}
.button_signal:disabled .button__label {
  color: $color__gray_7;
}
.button_signal:disabled .button__icon svg path {
  fill: $color__gray_7;
}
.button_light {
  background-color: $color__gray_1;
}
.button_light .button__label {
  color: $color__text;
}
.button_light .button__icon svg path {
  fill: $color__text;
}
.button_light:hover {
  background-color: $color__second_ultralight;
}
.button_light:focus {
  background-color: $color__second_ultralight;
  @include outline;
}
.button_light:disabled {
  background-color: $color__gray_1;
}
.button_light:disabled:hover {
  background-color: $color__gray_1;
}
.button_light:disabled .button__label {
  color: $color__gray_7;
}
.button_light:disabled .button__icon svg path {
  fill: $color__gray_7;
}

Обращает на себя внимание обилие переменных, в основном это цвета и текстовые стили, о проработке которых я писал во втором выпуске этой серии постов.

Но появилось что-то новое — это миксин с правилами для элементов в фокусе.

Для удобства навигации с клавиатуры важно наличие отдельного стиля для состояния :focus, обычно оно задаётся дефолтным outline из браузерных стилей, но он мне не нравится, поэтому я стандартный фокус выключил при помощи outline: none, а дальше, используя box-shadow создал свой кастомный стиль для элементов в фокусе.

@mixin outline {
  box-shadow: 0 0 0 3px $color__outline;
}

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

// elements
.button__icon img,
.button__icon svg {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
}
.button__label {
  white-space: nowrap;
}

Пара слов об иконках

Пока я предусмотрел один вариант установки иконки — инлайновый СВГ с разобранным контуром в виде элемента path.

СВГ важно задавать viewbox и устанавливать preserveAspectRatio="none", это позволяет управлять отображением SVG-элемента. Подробнее можно почитать в моей памятке по атрибуту.

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

Ну а img указан так, на всякий случай — вдруг придётся вставлять ПНГшку.


Эта серия постов — описание процесса, который подразумевает эволюцию продукта. Поэтому если вы читаете пост спустя некоторое время после публикации, имейте ввиду, что это не документация, некоторые вещи в актуальной версии работы могут отличаться от описанного. Актуальную версию можно посмотреть:

Полный список записей серии смотрите по тегу #raw-alfa