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

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

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

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

Обзор

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

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

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

ФАБ контейнер

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

FAB-разметка

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

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

стиль FAB

Чтобы 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);
}

Затем я придаю контейнеру 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-разметка

Элемент <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);
}

Далее добавим цвет. Мы будем использовать стратегию, которую уже использовали в испытаниях с графическим интерфейсом. Создайте четко названный набор пользовательских свойств, которые статически сохраняют светлые и темные цвета, а затем адаптивное пользовательское свойство, которому будут присвоены светлые или темные переменные в зависимости от системных предпочтений пользователя в отношении цветов:

.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> .

Анимация

Для улучшения пользовательского опыта можно добавить различные типы анимации. Как и в других задачах с графическим пользовательским интерфейсом, мы настроим несколько пользовательских свойств, которые будут обеспечивать уменьшенное и полное движение. По умолчанию стили предполагают, что пользователь хочет уменьшить движение, а затем с помощью медиа-запроса 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);
    }
  }
}

Заключение

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

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

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

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

Здесь пока смотреть нечего.

Ресурсы