Хлебные крошки
Описание паттерна
Этот паттерн позволяет создавать хлебные крошки для сайта. Хлебные крошки — это навигация, которая отображает текущее местоположение в иерархической структуре сайта.
Пример
Код паттерна
<nav class="breadcrumb" aria-label="Хлебные крошки">
<ol class="breadcrumb__list">
<li class="breadcrumb__item">
<a class="breadcrumb__link" href="../../../..">Главная</a>
</li>
<li class="breadcrumb__item">
<a class="breadcrumb__link" href="../../../">Веб-паттерны</a>
</li>
<li class="breadcrumb__item">
<a class="breadcrumb__link" href="../../">Хлебные крошки</a>
</li>
<li class="breadcrumb__item">
<a class="breadcrumb__link" href="" aria-current="page">Пример использования хлебных крошек</a>
</li>
</ol>
</nav>
.breadcrumb {
--indent: 8px;
--peek: 28px;
font-size: 20px;
overflow-x: auto;
}
.breadcrumb__list {
list-style: none;
margin: 0;
padding: 0;
display: inline-flex;
align-items: center;
gap: var(--indent);
width: max-content;
}
.breadcrumb__item:not(:last-child)::after {
content: "/";
margin-left: var(--indent);
color: #888;
}
.breadcrumb__link {
color: #0077cc;
text-decoration: none;
}
.breadcrumb__link:hover {
text-decoration: underline;
}
.breadcrumb__link[aria-current="page"] {
cursor: default;
pointer-events: none;
appearance: none;
color: #5b6470;
}
.breadcrumb__link[aria-current="page"]:hover {
text-decoration: none;
}
Правила по использованию
Хлебные крошки:
- Не должны заменять глобальную или локальную навигацию внутри раздела и всегда являются дополнительной навигацией.
- Желательно располагать рядом с основной навигацией.
- Должны отображать текущее местоположение в иерархической структуре сайта, а не историю сеансов.
- Должны включать только страницы сайта, а не логические категории.
- Не нужны для сайтов с плоской иерархией (1–2 уровня глубины) или линейной структурой.
- Должны начинаться со ссылки на домашнюю страницу.
- Не должны разбиваться на несколько строк.
- Последний элемент не должен быть интерактивным, так как является указателем на текущую страницу.
Постановка задачи
Пошаговый разбор выражения
1. Хлебные крошки - это дополнительная навигация
<nav class="breadcrumb" aria-label="Хлебные крошки">
<ol class="breadcrumb__list">
<li class="breadcrumb__item">
<a class="breadcrumb__link" href="../">Главная</a>
</li>
<li class="breadcrumb__item">
<a class="breadcrumb__link" href="" aria-current="page">Пример использования хлебных крошек</a>
</li>
</ol>
</nav>
-
Я навигация
Сразу добавляем секцию **
<nav>
. Это помогает асситивным технологиям понять за что отвечает секция - за навигацию. Также плюсом является то, что в асситивных технологиях можно очень легко и быстро перейти к навигации. Да это дополнительная навигация, но это навигация. -
Я говорю, что я дополнительная навигация
Чтобы было понятно, что это дополнительная навигация, добавляем атрибут
aria-label="Хлебные крошки"
. -
Я список
Добавляем список
<ol>
. Почему именно<ol>
, а не<ul>
? Потому что хлебные крошки это последовательность элементов, категория вложенна в категорию, а не группа элементов. -
Не все мои ссылки будут активны
Последний элемент не должен быть интерактивным, так как является указателем на текущую страницу. Такое поведение можно реализиовать несколькими способами, которые рассматриваются ниже. Однозначного ответа на вопрос “какой вариант лучше” на данный момент нет.
2. Стилизация: хлебные крошки не должны разбиваться на несколько строк.
.breadcrumb {
--indent: 8px;
overflow-x: auto; /* горизонтальный скролл на мобильных устройствах */
}
.breadcrumb__list {
display: inline-flex; /* в строку */
align-items: center; /* по вертикали по центру */
gap: var(--indent); /* расстояние между элементами */
width: max-content;
}
/* типовой разделитель */
.breadcrumb__item:not(:last-child)::after {
content: "/";
margin-left: var(--indent);
color: #888;
}
- Я должен быть в тонусе и не разваливаться на несколько строк
Чтобы текст шёл в всегда в одну строку добавим display: inline-flex
, отцентрируем элементы по вертикали align-items: center
и добавим расстояние между элементами gap: var(--indent)
. --indent
также понадобиться для разделителя.
- Мой разделитель не будет у последнего элемента
:not(:last-child)
- это селектор, который выбирает все элементы списка, кроме последнего. У последнего элемента не будет разделителя.
- На мобильных устройствах я должен быть скроллом
Так как хлебные крошки могут быть очень длинными, то на мобильных устройствах нужно добавить скролл. Для этого добавим overflow-x: auto
, а чтобы он точно сработал добавим width: max-content
. width: max-content
- это свойство, которое позволяет элементу растянуться на всю свою ширину контента.
⚠️ На данный момент не решён следующий вопрос: что делать, если элементы показываются ровно в размер экрана, без обрезки текста в следующем пункте. В этот момент пользователю будет сложно понять, что есть ещё элементы, которые не влезают в экран и надо поскролить. Обычно добавляют полутени на край блока, но чаще всего они работают плохо, особенно на неоднородном фоне. Приходите обсудить как улучшить этот момент
Последний элемент хлебных крошек
Последний пункт хлебных крошек вызывает больше всего вопросов и сомнений в этом паттерне. На данный момент нет единого мнения по поводу того, как правильно обрабатывать последний элемент и каждый разработчик выбирает решения под свои внутренний убеждения. Тем не менее, давайте рассмотрим примеры и убедимся в неидеальности ситуации.
Ссылка, которая ведёт сама на себя по спецификации
<nav class="breadcrumb" aria-label="Хлебные крошки">
<ol class="breadcrumb__list">
...
<li class="breadcrumb__item">
<a class="breadcrumb__link" aria-current="page">Пример использования хлебных крошек</a>
</li>
</ol>
</nav>
По HTML-спецификации, ссылка может не иметь атрибута href
. В этом случае, ссылка является заполнителем.
Если у тега
<a>
нет атрибутаhref
, то он не считается ссылкой. В этом случае он просто выступает как «заглушка» — место, где могла бы быть ссылка, но её нет. По сути остаётся только его содержимое (текст или вложенные элементы).
Любой браузер идеально отрабатывает этот случай: элемент-заглушка становится неактивным для взаимодействия и не отображается в фокусе. По сути то, что нужно, но есть один нюанс. Lighthouse в SEO категории помечает ссылку без href
как недоступную для сканирования: почитать подробнее в документации Google Developer.
Это правило говорит, что робот нашёл ссылку и не можете перейти по ней. Он не в состоянии понять, что <a>
не является ссылкой, поэтому он может её не сканировать, так как уже находится на этой странице. Казалось бы можно было бы игнорированить это утверждение, но Lighthouse решает, что это ошибка и может снять 10 балов в разделе SEO. Если интересно, то можно почитать обсуждение в issue.
Ссылка, которая ведёт никуда
<nav class="breadcrumb" aria-label="Хлебные крошки">
<ol class="breadcrumb__list">
...
<li class="breadcrumb__item">
<a class="breadcrumb__link" href="" aria-current="page">Пример использования хлебных крошек</a>
</li>
</ol>
</nav>
Одним из вариантов решения проблемы с Lighthouse является указание пустого href=""
. В этом случае добавляет много работы, так как ссылка становится активной и перезагружает страницу, а также отображается в фокусе. Поэтому нужно не забывать выключить взаимодействие и убрать всю стилизацию.
.breadcrumb__link[aria-current="page"] {
cursor: default; /* убираем курсор */
pointer-events: none; /* убираем взаимодействие */
appearance: none; /* убираем стилизацию */
color: #5b6470; /* восставливаем цвет для неактивного элемента, обычно серого цвета */
}
Этот пример не идеален, но хорош тем, что у него есть пометка
Я больше не ссылка, а простой <span>
Это самый простой, популярный и безопасный вариант, когда последний элемент не является ссылкой, а просто текстом. Но он также, как и предыдущий вариант, требует дополнительных действий в CSS.
<nav class="breadcrumb" aria-label="Хлебные крошки">
<ol class="breadcrumb__list">
...
<li class="breadcrumb__item">
<span class="breadcrumb__link">Пример использования хлебных крошек</span>
</li>
</ol>
</nav>
Обсудить паттерн
Этот паттерн можно обсудить на GitHub — задавайте вопросы, делитесь идеями и опытом.
Если у вас есть мысль для нового паттерна, пример из практики или предложение по улучшению — расскажите об этом в обсуждении: любой вклад помогает развивать проект.