Разбираемся со skip link
- Паттерны
- HTML
- CSS
В вебе есть много небольших, но полезных доступных паттернов. И один из них — ссылка для пропуска контента или скип линк (skip link). Это гиперссылка, которая ведёт к основному содержанию страницы и помогает пропустить объёмный, часто повторяющийся контент. Её главная цель — экономия времени пользователей.
Какой контент считается объёмным? Навигационное меню с логотипом и кучей ссылок, громоздкая сложная таблица, буквенные указатели, списки с главами или техническими характеристиками. Чаще всего skip link полезна для пропуска навигации по сайту в хедере.
Исключения — меню в футере и небольшая навигация в хедере, которая состоит из пары ссылок и логотипа. В случае футера можно вернуться к началу страницы с помощью клавиш, жестов и других встроенных возможностей браузеров. А небольшая навигация не отнимет много времени у пользователей.
В теории всё просто, но на практике несколько сложнее. Давайте попробуем разобраться со всем по порядку.
Теория
Послушаем WCAG 2.2
В руководстве по доступности есть два критерия, связанные со skip link. Первый касается их косвенно, а второй напрямую.
- Критерий 2.1.1. Клавиатура (A). Вся функциональность контента доступна для клавиатуры и не зависит от пауз между нажатиями клавиш.
- Критерий 2.4.1. Пропуск блоков (А). Доступен механизм пропуска блоков контента, которые повторяются на нескольких страницах.
Механизмы пропуска блоков
Есть два механизма:
- навигация по ориентирам (landmarks);
- skip link.
Первый способ доступен для пользователей скринридеров. Ориентиры добавляются с помощью семантических тегов или благодаря ARIA. У второго механизма аудитория больше. Это не только пользователи с особенностями зрения.
Можно встретить совет о том, что skip link не нужна на сайте с хорошей семантической вёрсткой. Это не совсем верно. Не все пользователи скринридеров знают о шорткатах для открытия меню с ориентирами, а у других пользователей клавиатуры такой возможности нет. К тому же, чем больше вариантов навигации, тем лучше.
Кому это нужно
Если коротко, то всем, кто последовательно навигируется по страницам и не может быстро пропустить контент. Если развёрнуто, то четырём категориям пользователей. Это:
- Пользователи скринридеров, которые перемещаются по десктопным сайтам с помощью клавиатуры, а по мобильным — касаниями, тапами и свайпами.
- Пользователи с моторными нарушениями, которые пользуются клавиатурой, выносными компьютерными кнопками и другими переключателями.
- Любые другие пользователи клавиатуры. Они могут быть продвинутого уровня или у них временно сломалась мышка.
- Пользователи экранных луп, которые используют для навигации клавиатуру.
Представьте, что используете для навигации клавиатуру и зашли на сайт интернет-магазина, например, Ozon. Вы нашли нужный товар, перешли к нему и снова оказались в начале страницы. Примерно 40 табов и вот наконец можно узнать больше про понравившийся рюкзак. Со skip link вы бы оказались в нужном месте в одно нажатие и не заснули по пути.
Требования к skip link
- Находится на первом месте в порядке табуляции.
- Ведёт сразу к основному контенту и устанавливает фокус на нём. Она эффективнее, если на странице одна основная область —
<main>
. - Может располагаться в основной области страницы. В этом случаем она пропускает нужный блок и ведёт в начало следующего.
- Понятно называется и хорошо описывает, куда ведёт.
- Может быть всегда видна, а может появляться при фокусе с клавиатуры. В обоих случаях отвечает критериям WCAG.
- Можно добавлять несколько таких ссылок. Например, одна ведёт к основному контенту, вторая — к поисковой строке. Не стоит перебарщивать с количеством, иначе в ссылках нет смысла.
- Не должна мешать пользователям мыши. Это дискуссионное требование, но в нём есть разумное зерно. Если такая ссылка всегда видна, то может запутать пользователей мышки. Они не знакомы с этим паттерном, а ещё один способ прокрутки к основному контенту им не нужен.
Практика
В проекте уже должен поддерживаться фокус с клавиатуры и использоваться семантическая вёрстка. Без этого skip link бесполезна.
Размечаем страницу
Перед тем, как перейти к разметке, пара слов про текст ссылки. На англоязычных сайтах чаще всего используют «Skip to main content» или «Skip to content». Кажется, что самые подходящие эквиваленты в русском — «Перейти к основному контенту» или более краткое «К основному контенту». «Содержание» — широкое понятие и означает содержимое страницы или оглавление. «Контент» лучше отражает, куда ведёт ссылка.
Ещё несколько вариантов названия:
- пропустить навигацию (Skip navigation);
- пропустить основную навигацию (Skip main navigation);
- пропустить ссылки в навигации (Skip navigation links).
Теперь поговорим о разметке.
Практическая реализация skip link — якорная ссылка. Лучше добавить её в начало <body>
или <header>
, если это первый элемент на странице. В примерах буду добавлять её в начало хедера.
<header>
<a href="#main-content" class="skip-link">
К основному контенту
</a>
<!-- Внушительная навигация -->
</header>
А вот куда она должна вести — главная загвоздка. Есть несколько ответов на этот вопрос.
Вариант 1, классический. Ссылка ведёт прямо к <main>
.
<header>
<a href="#main-content" class="skip-link">
К основному контенту
</a>
<!-- Внушительная навигация -->
</header>
<main id="main-content">
<!-- Контент основного блока -->
</main>
Этот вариант более-менее хорошо работает в современных браузерах, но есть одно «но». Могут возникнуть проблемы во всех мобильных браузерах на старых версиях iOS и Android, в старых Chrome и даже в Safari 14. Баги везде разные.
iOS и VoiceOver. При переходе по skip link визуально срабатывает прокрутка, но, после свайпа, фокус перемещается на другую область, а не на содержимое основного блока. Другой баг возвращает в начало страницы, когда пытаешься перейти к следующему элементу в основном блоке.
Safari и VoiceOver. В ишьюс GOV.UK найдёте баг с десктопным Safari 14 и VoiceOver. Если нажать на skip link, а потом на клавишу со стрелкой, то фокус переместится на следующий элемент после ссылки.
Android и TalkBack. Скрытые ссылки просто не получают фокус. Это ошибка всей операционной системы, из-за которой на таких элементах не запускается событие фокуса.
Chrome. Фокус остаётся на skip link и перемещается к следующему элементу после ссылки после нажатия на Tab.
Баг на iOS исправлен в апреле 2020, а баг в Chrome — ещё в 2017. Баг в Android воспроизводился в 2022, и пока нет обновлений. Итого, с этими багами столкнётесь в iOS старше 13 версии и в старых версиях Chrome. К сожалению, часть пользователей скринридеров не часто обновляет браузеры и операционные системы.
Вариант 2, в котором ссылка ведёт к <h1>
в <main>
.
<header>
<a href="#main-content" class="skip-link">
К основному контенту
</a>
<!-- Внушительная навигация -->
</header>
<main>
<h1 id="main-content">
Основной заголовок
</h1>
<!-- Остальной контент -->
</main>
Отличается от первого варианта тем, что скринридеры объявят текст заголовка, а не всё текстовое содержимое <main>
. Это даст пользователям больше контроля, так как им не нужно прерывать автоматическое объявление вручную.
У такой разметки те же баги, что и у предыдущего варианта с <main>
с атрибутом id
.
Вариант 3, который решает проблему двух предыдущих.
Атрибут tabindex
с отрицательным значением удаляет элемент из последовательной навигации.
<header>
<a href="#main-content" class="skip-link">
К основному контенту
</a>
<!-- Внушительная навигация -->
</header>
<main id="main-content" tabindex="-1">
<!-- Контент основного блока -->
</main>
Этот хак хорошо работает со старыми версиями Chrome и на iOS. И снова «но». В других браузерах это может привести к новым багам:
- При переходе к основному блоку выделяется вся область с отрицательным
tabindex
. - При клике по странице фокус вернётся в её начало.
Здесь на помощь приходит JavaScript. Нужен скрипт, который, после события клика у skip link, устанавливает фокус на main
и добавляет ему атрибут tabindex="-1"
. При потере фокуса этот атрибут удаляется. Можно подсмотреть реализацию в демке Майка Фоскетта, в том числе для Android. Аника Хенке предлагает более универсальное решение на jQuery, которое исправляет все ссылки и тоже удаляет tabindex
в момент потери фокуса.
Вариант 4, в котором skip link ведёт к другой ссылке перед <main>
.
<header>
<a href="#main-content" class="skip-link">
К основному контенту
</a>
<!-- Внушительная навигация -->
</header>
<a
href="#main-content"
id="main-content"
class="skip-link skip-link-target"
>
Начало основного контента
</a>
<main>
<!-- Контент основного блока -->
</main>
Решение предложил Пол Рэдклифф в «A Deep Dive on Skipping to Content». Он сделал демо для большей наглядности.
В этом случае скринридер объявит, что мы перешли к ссылке «Начало основного контента».
Разметка решает проблему пользователей, которые не понимают, сработала ссылка или нет. Также она помогает избежать багов с некорректным поведением фокуса с клавиатуры в некоторых браузерах.
Этот способ новый и интересный, но вижу несколько проблем.
- Вторая ссылка может сбить с толку пользователей скринридеров. Она никуда не ведёт, а так и хочется нажать на неё.
- Если пользователь без особенностей зрения протабает всю навигацию, то увидит непонятную ссылку «Начало основного контента».
- Точно поймаешь баг на Android, ведь это всё ещё визуально скрытые элементы.
Вариант 5, когда вторая ссылка без текста внутри <main>
, с href
или без него.
<header>
<a href="#main-content" class="skip-link">
К основному контенту
</a>
<!-- Внушительная навигация -->
</header>
<main>
<a id="main-content" class="visually-hidden-link"></a>
<!-- Остальной контент -->
</main>
Ссылка без href
— ссылка-плейсхолдер. Её можно использовать для некоторых ситуаций, только если она не должна работать как ссылка. Дело в том, что на неё не устанавливается фокус с клавиатуры.
Если это ссылка без названия, но с атрибутом href
, то скринридеры будут зачитывать содержимое этого атрибута. Например, "#main-content"
. Для NVDA эту проблему исправит aria-hidden="true"
.
Вариант 6 с несколькими ссылками, который подходит для редких случаев.
В этом примере обе ссылки обёрнуты в дополнительный <nav>
с aria-label
. Скринридеры объявят, что это навигация со ссылками для пропуска меню. Можно дополнительно обернуть их в <ul>
, чтобы пользователям было проще навигироваться.
<header>
<nav aria-label="Ссылки для пропуска меню">
<a href="#search" class="skip-link">
Перейти к поиску по сайту
</a>
<a href="#main-content" class="skip-link">
Перейти к основному контенту
</a>
</nav>
<!-- Внушительная навигация -->
<form id="search">
<!-- Поиск по сайту -->
</form>
</header>
<main id="main-content">
<!-- Контент основного блока -->
</main>
Без хака с tabindex="-1"
из третьего варианта тоже могут возникнуть проблемы в старых и некоторых новых браузерах.
Скрываем ссылку
Визуально скрывать ссылку и показывать её при фокусе тоже можно разными способами. Есть несколько основных правил:
- Не используйте свойства
display: none
,visibility: hidden
или атрибутhidden
. Нам надо скрыть ссылку только визуально. - Не устанавливайте значение
0
дляwidth
иheight
. Такой элемент просто не попадёт в фокус.
Давайте посмотрим на пару примеров со стилями.
Вариант 1 с position: absolute
и безумным отрицательным значением у left
.
Мы просто абсолютно позиционируем элемент, выносим его за видимую область, а при фокусе возвращаем туда, куда нужно.
.skip-link {
position: absolute;
top: auto;
left: -999px;
width: 1px;
height: 1px;
overflow: hidden;
}
/* Показываем при фокусе */
.skip-link:focus-visible {
top: 0;
left: 0;
width: auto;
height: auto;
overflow: visible;
}
Вариант 2 с clip
или clip-path
. Старый добрый visually-hidden способ.
Можно использовать их одновременно для большей совместимости. Ещё встречала вариант с clip-path: inset(50%)
.
.skip-link {
position: absolute;
margin: 0;
padding: 0;
/* Для всех браузеров */
clip: rect(0 0 0 0);
/* Современный способ */
-webkit-clip-path: polygon(0 0, 0 0, 0 0, 0 0);
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
}
/* Показываем при фокусе */
.skip-link:focus-visible {
top: 0;
left: 0;
width: auto;
height: auto;
/* Если используете clip */
clip: auto;
/* Если используете clip-path */
-webkit-clip-path: none;
clip-path: none;
}
Свойство clip
устарело, и ему на смену должно прийти clip-path
. Пока будущее не наступило окончательно, поэтому clip-path
поддерживается с префиксом или частично (-webkit-clip-path
).
Вариант 3 с transform
.
Снова позиционируем ссылку абсолютно и прячем её за пределы видимой области с помощью transform
. Когда на ней фокус, то возвращаем обратно.
.skip-link {
position: absolute;
top: 0;
left: 0;
transform: translateY(-100%);
}
/* Показываем при фокусе */
.skip-link:focus-visible {
transform: translateY(0%);
}
Добавляем последние штрихи
Остальная стилизация skip link зависит от вашего дизайнерского видения. Самое главное, чтобы она была хорошо видна при фокусе с клавиатуры.
WebAIM рекомендует не показывать ссылку неожиданно. Это может сбить с толку пользователей клавиатуры, которые видят интерфейс. Это исправит плавная анимация. Тогда ссылка будет выезжать из-за края экрана и уезжать обратно, когда на ней больше нет фокуса.
Размещать ссылки можно в любой верхней части экрана. Очень часто их располагают в левом верхнем углу, но это не железное правило.
Собрала небольшой список сайтов со skip link, где можно посмотреть, как они задизайнены у других. Используйте для навигации Tab на Windows, Tab или Option Tab на macOS.
Пара финальных слов
Часто, чем что-то кажется проще, тем оно сложнее на самом деле. Это произошло и со skip link.
Вариантов того, как сделать skip link, довольно много для такого небольшого элемента. У них есть плюсы и минусы. Для себя выбрала бы классическую реализацию со ссылкой, которая ведёт к <main>
или <h1>
с tabindex="-1"
из третьего варианта. И прогрессивно улучшила её с помощью JS. Что касается стилей для скрытия ссылки, то они все рабочие. Можно выбрать любой, который больше подходит для проекта. Я чаще всего пользуюсь абсолютным позиционированием и отрицательным значением left
.
Что почитать
- «Skip Navigation» Links
- Use skip navigation links, Камерон Кандифф
- A Deep Dive on Skipping to Content, Пол Рэдклифф
- Deep Dive on Skipping to Content, Сюзанна Чельсо
- How to Create a «Skip to Content» Link, Пол Райан
- Implement a Skip Link for Navigation-Heavy Sites, Бен Майерс
- Your skip links are broken, Хампус Сетфош
Спасибо Василию Дудину за помощь с редактированием ❤️