Создание компонента плавающей кнопки действия (FAB)

Базовый обзор того, как создавать адаптивные к цвету, отзывчивые и доступные компоненты FAB.

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

Если вы предпочитаете видео, вот версия этого поста на YouTube:

Обзор

FAB чаще встречаются на мобильных устройствах, чем на настольных компьютерах, но они присутствуют в обоих случаях. Они позволяют держать основные действия на виду, что делает их удобными и вездесущими. Этот стиль пользовательского опыта стал популярным благодаря Material UI, и его рекомендации по использованию и размещению можно найти здесь .

Элементы и стили

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

FAB-контейнер

Этот элемент может быть обычным <div> , но давайте сделаем одолжение нашим незрячим пользователям и снабдим его некоторыми полезными атрибутами, чтобы объяснить назначение и содержимое этого контейнера.

Разметка FAB

Начните с класса .fabs , к которому CSS будет привязан для стилей, затем добавьте role="group" и aria-label чтобы это был не просто общий контейнер, а именованный и целенаправленный.

<div class="fabs" role="group" aria-label="Floating action buttons">
  <!-- buttons will go here -->
</div>

Стиль FABs

Для удобства FAB-элементы всегда остаются в области просмотра. Это отличный пример использования fixed положения. В этой области просмотра я решил использовать inset-block и inset-inline , чтобы положение соответствовало режиму просмотра документа, например, справа налево или слева направо. Также используются пользовательские свойства, чтобы избежать повторов и обеспечить одинаковое расстояние от нижнего и боковых краев области просмотра:

.fabs {
  --_viewport-margin: 2.5vmin;

  position: fixed;
  z-index: var(--layer-1);

  inset-block: auto var(--_viewport-margin);
  inset-inline: auto var(--_viewport-margin);
}

Затем я присваиваю контейнеру свойство display flex и меняю направление его макета на column-reverse . Это позволяет расположить дочерние элементы друг над другом (в столбце), а также изменить их визуальный порядок. В результате первый фокусируемый элемент становится нижним, а не верхним, как это обычно происходит в HTML-документе. Обратный визуальный порядок объединяет взаимодействие зрячих пользователей и пользователей клавиатуры, поскольку оформление основного действия, большее, чем мини-кнопки, показывает зрячим пользователям, что это основное действие, и пользователи клавиатуры выделят его как первый элемент в исходном коде.

Показаны две потрясающие кнопки с наложенной на них сеткой DevTools. Зазор между ними показан полосатым узором, а также их вычисленные высота и ширина.

.fabs {
  

  display: flex;
  flex-direction: column-reverse;
  place-items: center;
  gap: var(--_viewport-margin);
}

Центрирование выполняется с помощью place-items , а gap добавляет пространство между любыми кнопками FAB, размещенными в контейнере.

FAB-кнопки

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

FAB по умолчанию

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

FAB-разметка

Элемент <button> — правильный выбор. Мы возьмём его за основу, поскольку он обеспечивает отличный пользовательский интерфейс с помощью мыши, сенсорного экрана и клавиатуры. Самый важный аспект этой разметки — скрыть значок от пользователей программ чтения с экрана с помощью aria-hidden="true" и добавить необходимый текст подписи в саму разметку <button> . При добавлении подписей в таких случаях я также предпочитаю добавлять title , чтобы пользователи, управляющие мышью, могли понять, что именно хочет передать значок.

<button data-icon="plus" class="fab" title="Add new action" aria-label="Add new action">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>

Потрясающий стиль

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

.fab {
  --_size: 2rem;

  padding: calc(var(--_size) / 2);
  border-radius: var(--radius-round);
  aspect-ratio: 1;
  box-shadow: var(--shadow-4);
}

Теперь добавим цвет. Мы воспользуемся стратегией, которую уже применяли в GUI Challenges. Создайте набор чётко именованных пользовательских свойств, статически определяющих светлые и тёмные цвета, а затем адаптивное пользовательское свойство, которое будет присвоено переменным «светлый» или «тёмный» в зависимости от системных цветовых предпочтений пользователя:

.fab {
  

  /* light button and button hover */
  --_light-bg: var(--pink-6);
  --_light-bg-hover: var(--pink-7);

  /* dark button and button hover */
  --_dark-bg: var(--pink-4);
  --_dark-bg-hover: var(--pink-3);

  /* adaptive variables set to light by default */
  --_bg: var(--_light-bg);

  /* static icon colors set to the adaptive foreground variable */
  --_light-fg: white;
  --_dark-fg: black;
  --_fg: var(--_light-fg);

  /* use the adaptive properties on some styles */
  background: var(--_bg);
  color: var(--_fg);

  &:is(:active, :hover, :focus-visible) {
    --_bg: var(--_light-bg-hover);

    @media (prefers-color-scheme: dark) {
      --_bg: var(--_dark-bg-hover);
    }
  }

  /* if users prefers dark, set adaptive props to dark */
  @media (prefers-color-scheme: dark) {
    --_bg: var(--_dark-bg);
    --_fg: var(--_dark-fg);
  }
}

Затем добавьте несколько стилей, чтобы иконки SVG вписались в пространство.

.fab {
  

  & > svg {
    inline-size: var(--_size);
    block-size: var(--_size);
    stroke-width: 3px;
  }
}

Наконец, уберите подсветку нажатия с кнопки, так как мы добавили собственную визуальную обратную связь для взаимодействия:

.fab {
  -webkit-tap-highlight-color: transparent;
}

Мини ФАБ

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

Мини-разметка FAB

HTML тот же, что и FAB, но мы добавляем класс «.mini», чтобы привязать CSS к варианту.

<button data-icon="heart" class="fab mini" title="Like action" aria-label="Like action">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>
Мини-стиль FAB

Благодаря использованию пользовательских свойств единственное необходимое изменение — это корректировка переменной --_size .

.fab.mini {
  --_size: 1.25rem;
}

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

Доступность

Самое важное, что следует помнить для обеспечения доступности при использовании FAB, — это расположение в потоке клавиатуры на странице. В этой демонстрации используются только FAB, нет ничего, с чем можно было бы конкурировать в плане порядка и потока клавиатуры, а значит, нет возможности продемонстрировать осмысленный поток клавиатуры. В ситуации, когда есть конкурирующие элементы за фокус, я предлагаю тщательно продумать, в какой части потока пользователь должен попасть в поток кнопок FAB.

Демонстрация взаимодействия с клавиатурой

Как только пользователь наводит фокус на контейнер FAB, мы уже добавляем role="group" и aria-label="floating action buttons" , которые информируют пользователей экранных ридеров о содержимом выбранного элемента. Стратегически я разместил FAB по умолчанию первым, чтобы пользователи могли быстро найти основное действие. Затем я использую flex-direction: column-reverse; чтобы визуально расположить основную кнопку внизу, ближе к пальцам пользователя для удобства доступа. Это приятное решение, поскольку кнопка по умолчанию визуально заметна и также находится первой для пользователей клавиатуры, что обеспечивает им очень похожий интерфейс.

Наконец, не забудьте скрыть значки от пользователей программ чтения с экрана и обязательно добавьте метку для кнопки, чтобы она не была для них загадкой. Это уже сделано в HTML с помощью aria-hidden="true" для <svg> и aria-label="Some action" для <button> .

Анимация

Для улучшения пользовательского опыта можно добавлять различные типы анимации. Как и в других задачах GUI, мы настроим несколько пользовательских свойств для реализации идеи ограниченного и полного движения. По умолчанию стили будут предполагать, что пользователь хочет ограниченное движение, а затем с помощью медиазапроса prefers-reduced-motion заменят значение перехода на полное движение.

Стратегия сокращенного движения с пользовательскими свойствами

В следующем CSS-коде создаются три пользовательских свойства: --_motion-reduced , --_motion-ok и --_transition . Первые два содержат соответствующие переходы в зависимости от предпочтений пользователя, а последняя переменная --_transition будет установлена ​​либо в значение --_motion-reduced либо в значение --_motion-ok соответственно.

.fab {
  /* box-shadow and background-color can safely be transitioned for reduced motion users */
  --_motion-reduced:
    box-shadow .2s var(--ease-3),
    background-color .3s var(--ease-3);

  /* add transform and outline-offset for users ok with motion */
  --_motion-ok:
    var(--_motion-reduced),
    transform .2s var(--ease-3),
    outline-offset 145ms var(--ease-2);

  /* default the transition styles to reduced motion */
  --_transition: var(--_motion-reduced);

  /* set the transition to our adaptive transition custom property*/
  transition: var(--_transition);

  /* if motion is ok, update the adaptive prop to the respective transition prop */
  @media (prefers-reduced-motion: no-preference) {
    --_transition: var(--_motion-ok);
  }
}

При наличии вышеперечисленного можно перенести изменения в box-shadow , background-color , transform и outline-offset , предоставив пользователю удобный интерфейс для отображения того, что его взаимодействие было выполнено.

Далее добавим немного изюминки состоянию :active , немного изменив translateY — это придаст кнопке красивый эффект нажатия:

.fab {
  

  &:active {
    @media (prefers-reduced-motion: no-preference) {
      transform: translateY(2%);
    }
  }
}

Затем, наконец, перенесите все изменения в значки SVG на кнопки:

.fab {
  

  &[data-icon="plus"]:hover > svg {
    transform: rotateZ(.25turn);
  }

  & > svg {
    @media (prefers-reduced-motion: no-preference) {
      will-change: transform;
      transition: transform .5s var(--ease-squish-3);
    }
  }
}

Заключение

Теперь, когда вы знаете, как я это сделал, как бы вы поступили? 🙂

Давайте разнообразим наши подходы и изучим все способы развития в Интернете.

Создайте демо, пришлите мне ссылку в Твиттер , и я добавлю ее в раздел ремиксов сообщества ниже!

Ремиксы сообщества

Пока что здесь нечего смотреть.

Ресурсы