Разбираемся со skip link

  • Паттерны
  • HTML
  • CSS
Обновлено:

В вебе есть много небольших, но полезных доступных паттернов. И один из них — ссылка для пропуска контента или скип линк (skip link). Это гиперссылка, которая ведёт к основному содержанию страницы и помогает пропустить объёмный, часто повторяющийся контент. Её главная цель — экономия времени пользователей.

Какой контент считается объёмным? Навигационное меню с логотипом и кучей ссылок, громоздкая сложная таблица, буквенные указатели, списки с главами или техническими характеристиками. Чаще всего skip link полезна для пропуска навигации по сайту в хедере.

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

В теории всё просто, но на практике несколько сложнее. Давайте попробуем разобраться со всем по порядку.

Теория

Послушаем WCAG 2.2

В руководстве по доступности есть два критерия, связанные со skip link. Первый касается их косвенно, а второй напрямую.

Механизмы пропуска блоков

Есть два механизма:

  • навигация по ориентирам (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.

Что почитать


Спасибо Василию Дудину за помощь с редактированием ❤️

Другие статьи