Карточки, обёрнутые в
<a>, достаточно распространённый шаблон в Интернете. Дизайнеры хотят, чтобы нажатие в любое место карточки вызывало переход на соответствующую страницу. Самый простой способ — обернуть всё в <a>.<a href="...">
<article class="card">
<img
src="..."
class="card__image"
alt="Чёрные беспроводные наушники Sony WH‑1000XM5, вид сбоку."
>
<h2 class="card__title">
Беспроводные наушники Sony WH‑1000XM5 (Чёрные)
</h2>
<p class="card__vendor">
Sony
</p>
<p class="card__price">
299 $
</p>
</article>
</a>
Первая проблема заключается в доступности. Весь текстовый контент внутри ссылки будет использован для генерации имени для вспомогательных технологий. Для карточки из примера выше итоговым именем будет:
Чёрные беспроводные наушники Sony WH‑1000XM5, вид сбоку. Беспроводные наушники Sony WH‑1000XM5 (Чёрные) Sony 299 $
Слишком длинное и избыточное имя. Оно будет озвучиваться программами чтения с экрана, отображаться в списке ссылок и на дисплее Брайля, его нужно будет частично или полностью произнести при голосовом управлении интерфейсом.
Быстро исправить проблему можно путём добавления на ссылку атрибута
aria-label или aria-labelledby с привязкой к заголовку. Оба способа переопределяют имя, генерируемое из тестового контента. Всё содержимое не будет зачитываться.<a
href="..."
aria-label="Беспроводные наушники Sony WH‑1000XM5 (Чёрные)"
>
<!-- ... -->
</a>
<a
href="..."
aria-labelledby="heading"
>
<!-- ... -->
<h2
id="heading"
class="card__title"
>
Беспроводные наушники Sony WH‑1000XM5 (Чёрные)
</h2>
<!-- ... -->
</a>
При использовании
aria-label важно, чтобы текст в атрибуте совпадал с видимыми текстом заголовка согласно критерию 2.5.3 Label in Name. С aria-labelledby это гарантируется за счёт связи с заголовком.Вторая проблема — дополнительные интерактивные элементы в карточке: тэги, кнопки галереи, переключатели параметров, кнопка добавления в корзину и так далее. Предположим, что в примере бренд — это ссылка на другую страницу:
<a href="...">
<!-- ... -->
<a
href="..."
class="card__vendor"
>
Sony
</a>
<!-- ... -->
</a>
Тогда получается интерактивный элемент внутри интерактивного элемента, что недопустимо по стандарту. Это приводит к неожиданному для пользователя поведению при нажатии и ухудшает опыт взаимодействия с карточкой.
Исправить первую и вторую проблемы, соблюдая при этом первое правило ARIA, можно перенеся ссылку внутрь заголовка и растянув её псевдо-элемент на всю карточку. Нажатие по псевдо-элементу делегирует нажатие на элемент.
<article class="card">
<!-- ... -->
<h2 class="card__title">
<a
href="..."
clasd="card__link"
>
Беспроводные наушники Sony WH‑1000XM5 (Чёрные)
</a>
</h2>
<a
href="..."
class="card__vendor"
>
Sony
</a>
<!-- ... -->
</article>
.card {
position: relative;
}
.card__link::before {
content: '';
position: absolute;
inset: 0;
z-index: 1;
}
.card__vendor {
position: relative;
z-index: 2;
}Чтобы псевдо-элемент не перекрывал другие интерактивные элементы карточки, препятствуя взаимодействию с помощью мыши и касаний, их нужно приподнять на слой выше. Также побочный эффект — невозможность выделения текста.
Получается несколько более сложная реализация карточки за счёт псевдо-элемента и слоёв. Зато она более надёжная с точки зрения новых интерактивных элементов и удобная с точки зрения вспомогательных технологий.
Более подробно эту технику описывает Хейдон Пикеринг в онлайн книге Inclusive Components. Также тему проблем с карточками затрагивает Адриан Розелли в своей статье Block Links, Cards, Clickable Regions, Rows, Etc.
В будущем, надеюсь, решать эту задачу можно будет с помощью Link Area Delegation API, разрабатываемого в рамках OpenUI. Это механизм делегации кликов с других элементов на ссылку, навеянный проблемами с карточками.
#html #css #a11y