В вебе есть много небольших, но полезных доступных паттернов. И один из них — ссылка для пропуска контента или скип линк (skip link). Это гиперссылка, которая ведёт к основному содержанию страницы и помогает пропустить объёмный, часто повторяющийся контент. Её главная цель — экономия времени пользователей.
Какой контент считается объёмным? Навигационное меню с логотипом и кучей ссылок, громоздкая сложная таблица, буквенные указатели, списки с главами или техническими характеристиками. Чаще всего skip link полезна для пропуска навигации по сайту в хедере.
Исключения — меню в футере и небольшая навигация в хедере, которая состоит из пары ссылок и логотипа. В случае футера можно вернуться к началу страницы с помощью клавиш, жестов и других встроенных возможностей браузеров. А небольшая навигация не отнимет много времени у пользователей.
В теории всё просто, но на практике несколько сложнее. Давайте попробуем разобраться со всем по порядку.
Теория
Что говорит про это WCAG 2.1
В руководстве по доступности есть два критерия, связанные со skip link. Первый касается их косвенно, а второй напрямую.
- Критерий 2.1.1. Клавиатура (A). Вся функциональность контента доступна для клавиатуры и не зависит от пауз между нажатиями клавиш.
- Критерий 2.4.1. Пропуск блоков (А). Доступен механизм пропуска блоков контента, которые повторяются на нескольких страницах.
Механизмы для пропуска блоков
Есть два механизма:
- навигация по ориентирам (landmarks);
- skip link.
Первый способ доступен для пользователей скринридеров. Ориентиры добавляются с помощью семантических тегов или благодаря ARIA. У второго механизма аудитория больше. Это не только пользователи с особенностями зрения.
Можно встретить совет о том, что skip link не нужна на сайте с хорошей семантической вёрсткой. Это не совсем верно. Не все пользователи скринридеров знают о шорткатах для открытия меню с ориентирами, а у других пользователей клавиатуры такой возможности нет. К тому же, чем больше вариантов навигации, тем лучше.
Кому нужна skip link
Если коротко, то всем, кто последовательно навигируется по страницам и не может быстро пропустить контент. Если развёрнуто, то четырём категориям пользователей. Это:
- Пользователи скринридеров, которые перемещаются по десктопным сайтам с помощью клавиатуры, а по мобильным — касаниями, тапами и свайпами.
- Пользователи с моторными нарушениями, которые пользуются клавиатурой, выносными компьютерными кнопками и другими переключателями.
- Любые другие пользователи клавиатуры. Они могут быть продвинутого уровня или у них временно сломалась мышка.
- Пользователи экранных луп, которые используют для навигации клавиатуру.
Представьте, что используете для навигации клавиатуру и зашли на сайт интернет-магазина, например, Ozon. Вы нашли нужный товар, перешли к нему и снова оказались в начале страницы. Примерно 40 табов и вот наконец можно узнать больше про понравившийся рюкзак. Со skip link вы бы оказались в нужном месте в одно нажатие и не заснули по пути.
Требования к skip link
- Находится на первом месте в порядке табуляции.
- Ведёт сразу к основному контенту и устанавливает фокус на нём. Она эффективнее, если на странице одна основная область —
<main>
. - Может располагаться в основной области страницы. В этом случаем она пропускает нужный блок и ведёт в начало следующего.
- Понятно называется и хорошо описывает, куда ведёт.
- Может быть всегда видна, а может появляться при фокусе с клавиатуры. В обоих случаях отвечает критериям WCAG.
- Можно добавлять несколько таких ссылок. Например, одна ведёт к основному контенту, вторая — к поисковой строке. Не стоит перебарщивать с количеством, иначе в ссылках нет смысла.
- Не должна мешать пользователям мыши. Это дискуссионное требование, но в нём есть разумное зерно. Если такая ссылка всегда видна, то может запутать пользователей мышки. Они не знакомы с этим паттерном, а ещё один способ прокрутки к основному контенту им не нужен.
Практика
В проекте уже должен поддерживаться фокус с клавиатуры и использоваться семантическая вёрстка. Без этого skip link бесполезна.
Размечаем страницу
Перед тем, как перейти к разметке, пара слов про текст ссылки. На англоязычных сайтах чаще всего используют «Skip to main content» или «Skip to content». Кажется, что самые подходящие эквиваленты в русском — «Перейти к основному контенту» или более краткое «К основному контенту». «Cодержание» — широкое понятие и означает содержимое страницы или оглавление. «Контент» лучше отражает, куда ведёт ссылка.
Ещё несколько вариантов названия:
- пропустить навигацию (Skip navigation);
- пропустить основную навигацию (Skip main navigation);
- пропустить ссылки в навигации (Skip navigation links).
Теперь поговорим о разметке.
Практическая реализация skip link — якорная ссылка. Лучше добавить её в начало <body>
или <header>
, если это первый элемент на странице. В примерах буду добавлять её в начало хедера.
<header>
<a href="#main-content" class="skip-link">Перейти к основному контенту</a>
<!-- Внушительная навигация -->
</header>
А вот куда она должна вести — главная загвоздка. Есть несколько ответов на этот вопрос.
Вариант 1, классический. Ссылка ведёт прямо к <main>
.
<!-- Вариант 1 -->
<header>
<a href="#main-content" class="skip-link">Перейти к основному контенту</a>
<!-- Внушительная навигация -->
</header>
<main id="main-content">
<!-- Контент основного блока -->
</main>
Этот вариант более-менее хорошо работает в современных браузерах, но есть одно «но». Могут возникнуть проблемы во всех мобильных браузерах на старых версиях iOS и Android, в старых Chrome и даже в Safari 14. Баги везде разные.
- iOS + VoiceOver. При переходе по skip link визуально срабатывает прокрутка, но, после свайпа, фокус перемещается на другую область, а не на содержимое основного блока. Другой баг возвращает в начало страницы, когда пытаешься перейти к следующему элементу в основном блоке.
- Android + TalkBack. Скрытые ссылки просто не получают фокус. Это ошибка всей системы, из-за которой на таких элементах не запускается событие фокуса.
- Chrome. Фокус остаётся на skip link и перемещается к следующему элементу после ссылки после нажатия на Tab.
- Safari + VoiceOver. В ишьюс GOV.UK нашла свежий баг с десктопным Safari 14 и VoiceOver. Если нажать на skip link, а потом на клавишу со стрелкой, то фокус переместится на следующий элемент после ссылки.
Баг на iOS исправлен в апреле 2020, на Android — в феврале 2021 и в Chrome — ещё в 2017. Следовательно, они не встречаются с iOS 13+, как минимум с Android 10+ и в старых версиях Chrome. Только часть пользователей скринридеров долго не обновляет браузеры и операционные системы, так что от фикса багов не становится легче.
Вариант 2, в котором ссылка ведёт к <h1>
внутри <main>
.
<!-- Вариант 2 -->
<header>
<a href="#main-content" class="skip-link">Перейти к основному контенту</a>
<!-- Внушительная навигация -->
</header>
<main>
<h1 id="main-content">Основной заголовок</h1>
<!-- Остальной контент основного блока -->
</main>
Отличается от первого варианта тем, что скринридеры объявят текст заголовка, а не всё текстовое содержимое <main>
. Это даст пользователям больше контроля, так как им не нужно прерывать автоматическое объявление вручную.
У такой разметки те же баги, что и у предыдущего варианта с <main>
с атрибутом id
.
Вариант 3, который решает проблему двух предыдущих.
<!-- Вариант 3 -->
<header>
<a href="#main-content" class="skip-link">Перейти к основному контенту</a>
<!-- Внушительная навигация -->
</header>
<main id="main-content" tabindex="-1">
<!-- Контент основного блока -->
</main>
Атрибут tabindex
с отрицательным значением удаляет элемент из последовательной навигации.
Этот хак хорошо работает со старыми версиями Chrome и на iOS. И снова «но». В других браузерах это может привести к новым багам:
- При переходе к основному блоку выделяется вся область с отрицательным
tabindex
. - При клике по странице фокус вернётся в её начало.
Здесь на помощь приходит JavaScript. Нужен скрипт, который, после события клика у skip link, устанавливает фокус на main
и добавляет ему атрибут tabindex="-1"
. При потере фокуса этот атрибут удаляется. Можно подсмотреть реализацию в демке Майка Фоскетта, в том числе для Android. Аника Хенке предлагает более универсальное решение на jQuery, которое исправляет все ссылки и тоже удаляет tabindex
в момент потери фокуса.
Вариант 4, в котором skip link ведёт к другой ссылке перед <main>
.
<!-- Вариант 4 -->
<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
или без него.
<!-- Вариант 5 -->
<header>
<a href="#main-content" class="skip-link">Перейти к основному контенту</a>
<!-- Внушительная навигация -->
</header>
<main>
<a id="main-content" class="visually-hidden-link"></a>
<!-- Остальной контент основного блока -->
</main>
Его используют на сайте Deque. И, кажется, это самый проблемный вариант.
Ссылка без href
считается ссылкой-плейсхолдером. Её можно использовать для некоторых ситуаций, только если она не должна работать как ссылка. Дело в том, что на неё не устанавливается фокус с клавиатуры.
Если это ссылка без названия, но с href
, то скринридеры будут зачитывать содержимое этого атрибута. Например, "#main-content"
. Для NVDA эту проблему исправит aria-hidden="true"
.
Вариант 6 с несколькими ссылками, который подходит для редких кейсов.
<!-- Вариант 6 -->
<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>
В этом примере обе ссылки обёрнуты в дополнительный <nav>
с aria-label
. Скринридеры объявят, что это навигация со ссылками для пропуска меню. Можно дополнительно обернуть их в <ul>
, чтобы пользователям было проще навигироваться.
Без хака с tabindex="-1"
из третьего варианта тоже могут возникнуть проблемы в старых и некоторых новых браузерах.
Скрываем ссылку
Визуально скрывать ссылку и показывать её при фокусе тоже можно разными способами. Есть несколько основных правил:
- Не используйте свойства
display: none
,visibility: hidden
или атрибутhidden
. Нам надо скрыть ссылку только визуально. - Не устанавливайте значение 0 для
width
иheight
. Тогда фокус просто не установится на таком элементе.
Давайте рассмотрим пару конкретных примеров со стилями.
Вариант 1 с position: absolute
и безумным отрицательным значением left
.
Мы просто абсолютно позиционируем элемент, выносим его за видимую область, а при фокусе возвращаем туда, куда нужно.
/* Вариант 1 */
.skip-link {
position: absolute;
top: auto;
left: -999px;
width: 1px;
height: 1px;
overflow: hidden;
}
/* Показываем при фокусе */
.skip-link:focus {
top: 0;
left: 0;
width: auto;
height: auto;
overflow: visible;
}
Вариант 2 с clip
или clip-path
. Старый-добрый visually-hidden способ.
Можно использовать их одновременно для большей совместимости. Ещё встречала вариант с clip-path: inset(50%)
.
/* Вариант 2 */
.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 {
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
в большинстве браузеров поддерживается с префиксом.
Вариант 3 с transform
.
Снова позиционируем ссылку абсолютно и прячем её за пределы видимой области с помощью transform
. Когда на ней фокус, то возвращаем обратно.
/* Вариант 3 */
.skip-link {
position: absolute;
top: 0;
left: 0;
transform: translateY(-100%);
}
/* Показываем при фокусе */
.skip-link:focus {
transform: translateY(0%);
}
Добавляем последние штрихи
Остальная стилизация skip link зависит от вашего дизайнерского видения. Самое главное, чтобы она была хорошо видна при фокусе с клавиатуры.
WebAIM рекомендует не показывать ссылку неожиданно. Это может сбить с толку пользователей клавиатуры, которые видят интерфейс. Это исправит плавная анимация. Тогда ссылка будет выезжать из-за края экрана и уезжать обратно, когда на ней больше нет фокуса.
Размещать ссылки можно в любой верхней части экрана. Очень часто их располагают в левом верхнем углу, но это не железное правило.
Собрала небольшой список сайтов со skip link, где можно посмотреть, как они задизайнены у других. Используйте для навигации Tab на Windows и Tab или Option+Tab на macOS.
Пара слов напоследок
Часто, чем что-то кажется проще, тем оно сложнее на самом деле. Это произошло и со skip link.
Вариантов того, как сделать skip link, довольно много для такого небольшого элемента. У них есть плюсы и минусы. Для себя выбрала бы классическую реализацию со ссылкой, которая ведёт к <main>
или <h1>
с tabindex="-1"
из третьего варианта. И прогрессивно улучшила её с помощью JS. Что касается стилей для скрытия ссылки, то они все рабочие. Можно выбрать любой, который больше подходит для проекта. Я чаще всего пользуюсь абсолютным позиционированием и отрицательным значением left
.
Что почитать
- Bypass Blocks. Understanding SC 2.4.1, W3C.
- Accessibility Skip links, W3C School.
- «Skip Navigation» Links, WebAIM.
- Use skip navigation links, Камерон Кандифф.
- A Deep Dive on Skipping to Content, Пол Рэдклифф.
- Some thoughts on CSS Tricks' «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, Хампус Сетфош.
- Skip links: the 5 most common mistakes, System Concepts.
Спасибо Василию Дудину за помощь с редактированием.