Создание компонента хлебных крошек

Базовый обзор того, как создать адаптивный и доступный компонент хлебных крошек, позволяющий пользователям перемещаться по вашему сайту.

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

Демо

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

Обзор

Компонент «хлебные крошки» показывает, в каком месте иерархии сайта находится пользователь. Название происходит от Гензеля и Гретель , которые бросали за собой хлебные крошки в темном лесу и смогли найти дорогу домой, прослеживая крошки задом наперед.

Хлебные крошки в этом посте не являются стандартными , они похожи на хлебные крошки. Они предлагают дополнительную функциональность, помещая однородные страницы прямо в навигацию с помощью <select> , что делает возможным многоуровневый доступ.

Фоновый UX

В приведенном выше демонстрационном видеоролике компонента категории-заполнители представляют собой жанры видеоигр. Этот след создается путем перехода по следующему пути: home » rpg » indie » on sale , как показано ниже.

Этот компонент навигации должен позволять пользователям перемещаться по этой информационной иерархии; прыгая по ветвям и выбирая страницы быстро и точно.

Информационная архитектура

Я считаю, что полезно думать о коллекциях и предметах.

Коллекции

Коллекция представляет собой массив вариантов на выбор. На домашней странице прототипа хлебных крошек этого поста представлены коллекции FPS, RPG, Brawler, Dungeon Crawler, Sports и Puzzle.

Предметы

Видеоигра — это предмет, конкретная коллекция также может быть предметом, если она представляет другую коллекцию. Например, RPG — это предмет и действительная коллекция. Если это элемент, пользователь находится на этой странице коллекции. Например, они находятся на странице RPG, где отображается список ролевых игр, включая дополнительные подкатегории AAA, Indie и Self Published.

С точки зрения информатики, этот компонент хлебных крошек представляет собой многомерный массив :

const rawBreadcrumbData = {
 
"FPS": {...},
 
"RPG": {
   
"AAA": {...},
   
"indie": {
     
"new": {...},
     
"on sale": {...},
     
"under 5": {...},
   
},
   
"self published": {...},
 
},
 
"brawler": {...},
 
"dungeon crawler": {...},
 
"sports": {...},
 
"puzzle": {...},
}

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

Макеты

Разметка

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

Темная и светлая схема

<meta name="color-scheme" content="dark light">

Метатег color-scheme в приведенном выше фрагменте сообщает браузеру, что этой странице нужны светлый и темный стили браузера. Примеры хлебных крошек не содержат CSS для этих цветовых схем, поэтому в хлебных крошках будут использоваться цвета по умолчанию, предоставленные браузером.

<nav class="breadcrumbs" role="navigation"></nav>

Для навигации по сайту уместно использовать элемент <nav> , который имеет неявную роль навигации ARIA. При тестировании я заметил, что атрибут role изменил способ взаимодействия средства чтения с экрана с элементом; на самом деле он был объявлен как навигация, и поэтому я решил добавить его.

Иконки

Когда значок повторяется на странице, элемент SVG <use> означает, что вы можете определить path один раз и использовать его для всех экземпляров значка. Это предотвращает повторение одной и той же информации о пути, что приводит к увеличению размера документов и потенциальной несогласованности пути.

Чтобы использовать этот метод, добавьте на страницу скрытый элемент SVG и оберните значки элементом <symbol> с уникальным идентификатором:

<svg style="display: none;">

 
<symbol id="icon-home">
   
<title>A home icon</title>
   
<path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
 
</symbol>

 
<symbol id="icon-dropdown-arrow">
   
<title>A down arrow</title>
   
<path d="M19 9l-7 7-7-7"/>
 
</symbol>

</svg>

Браузер считывает SVG HTML, помещает информацию о значке в память и продолжает работу с остальной частью страницы, ссылаясь на идентификатор для дополнительного использования значка, например:

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
 
<use href="#icon-home" />
</svg>

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
 
<use href="#icon-dropdown-arrow" />
</svg>

DevTools, показывающий визуализированный элемент использования SVG.

Определите один раз, используйте столько раз, сколько захотите, с минимальным влиянием на производительность страницы и гибким стилем. Обратите внимание, что к элементу SVG добавлено aria-hidden="true" . Значки бесполезны для тех, кто просматривает сайт и слышит только контент. Скрытие их от этих пользователей не позволяет им создавать ненужный шум.

Здесь традиционная «хлебная крошка» и те, что входят в этот компонент, расходятся. Обычно это будет только ссылка <a> , но я добавил UX обхода с замаскированным выбором. Класс .crumb отвечает за размещение ссылки и значка, а .crumbicon отвечает за объединение значка и элемента выбора. Я назвал ее разделенной ссылкой, потому что ее функции очень похожи на разделенную кнопку , но для навигации по страницам.

<span class="crumb">
 
<a href="#sub-collection-b">Category B</a>
 
<span class="crumbicon">
   
<svg>...</svg>
   
<select class="disguised-select" title="Navigate to another category">
     
<option>Category A</option>
     
<option selected>Category B</option>
     
<option>Category C</option>
   
</select>
 
</span>
</span>

Ссылка и некоторые параметры не представляют собой ничего особенного, но добавляют больше функциональности простой навигационной цепочке. Добавление title к элементу <select> полезно для пользователей программ чтения с экрана, предоставляя им информацию о действии кнопки. Однако он оказывает такую ​​же помощь и всем остальным, вы увидите, что на iPad он находится спереди и в центре. Один атрибут предоставляет контекст кнопки многим пользователям.

Снимок экрана с наведенным невидимым элементом выбора и его отображение контекстной подсказки.

Разделительные украшения

<span class="crumb-separator" aria-hidden="true"></span>

Разделители не являются обязательными, добавление хотя бы одного тоже отлично подойдет (см. третий пример в видео выше). Затем я присваиваю каждому aria-hidden="true" поскольку они носят декоративный характер и не требуют объявления программой чтения с экрана.

Свойство gap , о котором речь пойдет далее, упрощает их размещение.

Стили

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

Направление и поток макета

DevTools показывает выравнивание навигационной цепочки с помощью наложения flexbox особенность.

Основной элемент навигации nav.breadcrumbs устанавливает настраиваемое свойство с ограниченной областью действия для использования дочерними элементами или, в противном случае, устанавливает горизонтальный вертикально выровненный макет. Это гарантирует выравнивание крошек, разделителей и значков.

.breadcrumbs {
 
--nav-gap: 2ch;

 
display: flex;
 
align-items: center;
 
gap: var(--nav-gap);
 
padding: calc(var(--nav-gap) / 2);
}

Одна навигационная цепочка показана вертикально с наложением флексбоксов.

Каждый .crumb также устанавливает горизонтальный вертикально выровненный макет с некоторым зазором, но специально нацелен на дочерние элементы ссылки и определяет стиль white-space: nowrap . Это очень важно для хлебных крошек, состоящих из нескольких слов, поскольку мы не хотим, чтобы они были многострочными. Позже в этом посте мы добавим стили для обработки горизонтального переполнения, вызванного этим свойством white-space .

.crumb {
 
display: inline-flex;
 
align-items: center;
 
gap: calc(var(--nav-gap) / 4);

 
& > a {
   
white-space: nowrap;

   
&[aria-current="page"] {
     
font-weight: bold;
   
}
 
}
}

aria-current="page" добавляется, чтобы помочь текущей ссылке выделиться среди остальных. Пользователи программ чтения с экрана не только будут иметь четкий индикатор того, что ссылка ведет на текущую страницу, но и визуально стилизовали элемент, чтобы помочь зрячим пользователям получить аналогичный пользовательский опыт.

Компонент .crumbicon использует сетку для объединения значка SVG с «почти невидимым» элементом <select> .

Grid DevTools показан поверх кнопки, где строка и столбец являются одновременно строкой и столбцом. именованный стек.

.crumbicon {
 
--crumbicon-size: 3ch;

  display
: grid;
  grid
: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
  place
-items: center;

 
& > * {
    grid
-area: stack;
 
}
}

Элемент <select> находится последним в DOM, поэтому он находится на вершине стека и является интерактивным. Добавьте стиль opacity: .01 , чтобы элемент можно было использовать, и в результате получится поле выбора, которое идеально соответствует форме значка. Это хороший способ настроить внешний вид элемента <select> , сохранив при этом встроенную функциональность.

.disguised-select {
 
inline-size: 100%;
 
block-size: 100%;
 
opacity: .01;
 
font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}

Переполнение

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

.breadcrumbs {
 
overflow-x: auto;
 
overscroll-behavior-x: contain;
 
scroll-snap-type: x proximity;
 
scroll-padding-inline: calc(var(--nav-gap) / 2);

 
& > .crumb:last-of-type {
   
scroll-snap-align: end;
 
}

 
@supports (-webkit-hyphens:none) { & {
   
scroll-snap-type: none;
 
}}
}

Стили переполнения настраивают следующий UX:

  • Горизонтальная прокрутка с ограничением чрезмерной прокрутки.
  • Горизонтальная прокрутка.
  • Одна точка защелкивания на последней крошке. Это означает, что при загрузке страницы первые крошки загружаются и видны.
  • Удаляет точку привязки из Safari, который не справляется с комбинациями горизонтальной прокрутки и эффектов привязки.

Медиа-запросы

Одна тонкая настройка для небольших видовых экранов — скрыть метку «Домой», оставив только значок:

@media (width <= 480px) {
 
.breadcrumbs .home-label {
   
display: none;
 
}
}

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

Доступность

Движение

В этом компоненте не так много движения, но, обернув переход проверкой prefers-reduced-motion , мы можем предотвратить нежелательное движение.

@media (prefers-reduced-motion: no-preference) {
 
.crumbicon {
   
transition: box-shadow .2s ease;
 
}
}

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

JavaScript

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

Две критически важные меры пользовательского опыта, которые должны обрабатываться с помощью JavaScript: выбор изменен и предотвращение срабатывания события <select> .

Активное предотвращение событий необходимо из-за использования элемента <select> . В Windows Edge и, возможно, в других браузерах событие выбора changed срабатывает, когда пользователь просматривает параметры с помощью клавиатуры. Вот почему я назвал это нетерпеливым, поскольку пользователь только псевдовыбрал опцию, например, наведение курсора или фокус, но не подтвердил выбор с помощью enter или click . Событие нетерпеливости делает эту функцию изменения категории компонента недоступной, поскольку открытие поля выбора и простой просмотр элемента вызовет событие и изменит страницу до того, как пользователь будет готов.

Лучшее событие изменения <select>

const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])

// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs
.forEach(nav => {
  let ignoreChange
= false

  nav
.addEventListener('change', e => {
   
if (ignoreChange) return
   
// it's actually changed!
 
})

  nav
.addEventListener('keydown', ({ key }) => {
   
if (preventedKeys.has(key))
      ignoreChange
= true
   
else if (allowedKeys.has(key))
      ignoreChange
= false
 
})
})

Стратегия заключается в том, чтобы отслеживать события нажатия клавиатуры на каждом элементе <select> и определять, была ли нажатая клавиша подтверждением навигации ( Tab или Enter ) или пространственной навигацией ( ArrowUp или ArrowDown ). При таком определении компонент может решить подождать или уйти, когда сработает событие для элемента <select> .

Заключение

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

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

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