Олег Марков
Атрибут aria-hidden - как управлять видимостью элементов для скринридеров
Введение
ARIA-hidden — это один из самых часто неправильно используемых ARIA-атрибутов. Он кажется простым: поставить aria-hidden="true", чтобы скрыть что-то от скринридера. Но на практике атрибут влияет на дерево доступности, может неожиданно «ломать» навигацию, дублировать контент и вызывать путаницу у пользователей ассистивных технологий.
В этой статье я покажу вам, как aria-hidden действительно работает, как он взаимодействует с другими атрибутами и когда его использование оправдано. Мы разберем реальные примеры, разложим типичные ошибки и посмотрим, как безопасно применять aria-hidden в современных интерфейсах.
Что такое aria-hidden и как оно работает
Краткое определение
Атрибут aria-hidden управляет тем, будет ли элемент и все его потомки доступны ассистивным технологиям, например скринридерам.
- aria-hidden="true" — элемент и его содержимое исключаются из дерева доступности.
- aria-hidden="false" — элемент явно включается в дерево доступности (если другие факторы это не запрещают).
- Отсутствие атрибута — поведение по умолчанию, зависящее от семантики HTML и других ARIA-атрибутов.
Важно: aria-hidden никак не влияет на визуальное отображение. Элемент все так же виден на экране и интерактивен с точки зрения мыши и клавиатуры (если вы не добавите другие атрибуты или стили).
Дерево доступности и скрытие элементов
Когда браузер рендерит страницу, он строит не только DOM-дерево, но и дерево доступности. Именно его видит скринридер. Атрибут aria-hidden управляет включением или исключением узлов в это дерево.
Смотрите, я покажу вам, как это работает на простом примере.
Простой пример без aria-hidden
<div>
<h1>Мой сайт</h1>
<p>Добро пожаловать на сайт</p>
</div>В дереве доступности здесь будет:
- заголовок уровня 1 с текстом «Мой сайт»
- текстовый абзац «Добро пожаловать на сайт»
Скринридер последовательно прочитает оба элемента.
Тот же пример с aria-hidden="true"
<div aria-hidden="true">
<h1>Мой сайт</h1>
<p>Добро пожаловать на сайт</p>
</div>Комментарии:
<div aria-hidden="true">
<!-- Весь контент внутри этого div будет скрыт для скринридера -->
<h1>Мой сайт</h1>
<!-- Этот заголовок не появится в дереве доступности -->
<p>Добро пожаловать на сайт</p>
<!-- Этот текст также не будет озвучен скринридером -->
</div>Теперь в дереве доступности эти элементы просто не существуют. Пользователь скринридера не узнает, что там что-то есть, даже если визуально блок заметен.
Синтаксис aria-hidden и варианты значений
Допустимые значения
Формально aria-hidden принимает строковые значения:
- "true"
- "false"
- "undefined" (редко используется вручную)
На практике чаще всего вы увидите:
<div aria-hidden="true">...</div>
<div aria-hidden="false">...</div>
<div>...</div> <!-- aria-hidden не задан -->Несколько важных моментов:
- aria-hidden без значения (например aria-hidden) — это некорректный вариант с точки зрения спецификации, его лучше избегать.
- Пустая строка aria-hidden="" трактуется неодинаково разными браузерами и скринридерами. Такое использовать нельзя.
- Значения вроде "0", "1", "yes", "no" не поддерживаются и могут вести себя непредсказуемо.
Наследование и влияние на потомков
Aria-hidden работает рекурсивно: если вы задаете aria-hidden="true" на родителе, все потомки тоже скрываются из дерева доступности, даже если у них стоит aria-hidden="false".
Посмотрите на пример:
<div aria-hidden="true">
<h2>Заголовок раздела</h2>
<button aria-hidden="false">Видимая кнопка</button>
</div>Комментарии:
<div aria-hidden="true">
<!-- Этот контейнер скрывает все свое дерево для ассистивных технологий -->
<h2>Заголовок раздела</h2>
<!-- Несмотря на корректный заголовок, он не попадет в дерево доступности -->
<button aria-hidden="false">Видимая кнопка</button>
<!-- aria-hidden="false" здесь бесполезен - предок уже скрыл весь блок -->
</div>Даже с aria-hidden="false" на кнопке, пользователь скринридера ее не увидит. Это одна из частых ловушек.
Когда aria-hidden действительно нужен
Типичные сценарии использования
Давайте разберемся, в каких случаях aria-hidden — правильное решение.
1. Дублированный визуальный контент
Иногда текст отображается и как обычный текст, и как часть SVG-иконки, и как псевдоэлемент. Чтобы скринридер не повторял одну и ту же информацию дважды, вы можете скрыть декоративную часть.
<button type="button">
<span aria-hidden="true">+</span>
<!-- Этот плюсик чисто декоративный, скрываем его от скринридера -->
<span>Добавить</span>
<!-- Основной текст кнопки, его и должен прочитать скринридер -->
</button>Здесь пользователь зрительно видит значок плюс и текст, а пользователь скринридера слышит только «Добавить», что логично.
2. Декоративные иконки и графика
Все, что несет чисто визуальный смысл (фоновые иллюстрации, разделители, декоративные SVG-иконки), должно быть скрыто от ассистивных технологий.
<span class="icon icon-alert" aria-hidden="true"></span>
<!-- Иконка предупреждения - визуальное усиление, скринридер о ней не сообщает -->
<span>Внимание - доступ к разделу ограничен</span>
<!-- Весь смысл уже есть в тексте -->Если информация в иконке не дублируется в тексте, вместо aria-hidden лучше использовать role="img" и aria-label или alt для . Тогда иконка станет доступной.
3. Переключение между двумя состояниями текста
В интерфейсах часто используют два варианта текста, где один виден визуально, а второй предназначен только для скринридеров.
<button type="button">
<span aria-hidden="true">Открыть меню</span>
<!-- Визуальный текст, который мы будем менять анимацией -->
<span class="sr-only">Открыть основное меню навигации</span>
<!-- Более подробный текст только для скринридера -->
</button>Здесь aria-hidden помогает разделить визуальное представление и доступное описание.
4. Сложные визуальные компоненты
В современных UI много сложных визуальных паттернов, которые легко описать словами, но тяжело представить в виде семантической структуры. В таких случаях:
- визуальную часть можно полностью скрыть с aria-hidden="true"
- а для скринридера создать упрощенный, но понятный аналог
Покажу вам, как это реализовано на практике:
<div class="rating-stars" aria-hidden="true">
<!-- Сложный визуальный компонент рейтинга -->
★★★★☆
</div>
<p class="sr-only">Рейтинг товара - 4 из 5</p>
<!-- Простое текстовое описание для скринридера -->Так вы избегаете путаницы в дереве доступности, но сохраняете смысл.
Ситуации, когда aria-hidden опасен
Теперь давайте рассмотрим случаи, когда aria-hidden наносит вред доступности.
Скрытие интерактивных элементов
Самая серьезная ошибка — ставить aria-hidden="true" на:
- кнопки
- ссылки
- поля форм
- элементы, управляемые с клавиатуры
<button type="button" aria-hidden="true">
Отправить
</button>Комментарии:
<button type="button" aria-hidden="true">
<!-- Кнопка будет видима и кликабельна мышью -->
<!-- Но для пользователей скринридера она просто не существует -->
Отправить
</button>Визуально кнопка есть, но пользователь скринридера:
- не сможет к ней переместиться
- не услышит о ее существовании
- не сможет отправить форму
Такие ошибки напрямую нарушают доступность и могут привести к юридическим проблемам на проектах, где есть требования WCAG.
Скрытие фокусируемых элементов
Если элемент может получать фокус (через tabindex, по умолчанию или через JavaScript), скрывать его aria-hidden опасно. У пользователя получится такая ситуация:
- фокус переходит на элемент
- скринридер молчит, потому что элемент скрыт из доступности
- пользователь не понимает, где он находится
Пример:
<div aria-hidden="true">
<a href="#details">Перейти к деталям</a>
</div>Комментарии:
<div aria-hidden="true">
<!-- Весь блок скрыт для скринридера -->
<a href="#details">Перейти к деталям</a>
<!-- Но ссылка все еще фокусируема с клавиатуры -->
</div>Это создает «немые» фокусы — сильный анти-паттерн в доступности.
Скрытие контента модальных окон и диалогов
Частая логика: открыть модалку и скрыть от скринридера фон. Это правильно. Но ошибки возникают в деталях:
- нельзя ставить aria-hidden="true" на саму модалку
- нельзя забывать вернуть aria-hidden="false" (или удалить атрибут) после закрытия
Вот корректный шаблон:
<main id="main-content">
<!-- Основной контент страницы -->
</main>
<div id="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">Подтверждение</h2>
<p>Вы уверены, что хотите продолжить</p>
<button type="button">Да</button>
<button type="button">Нет</button>
</div>Комментарии к логике JavaScript:
// Открываем модалку
function openModal() {
const main = document.getElementById('main-content')
const modal = document.getElementById('modal')
// Скрываем основной контент от скринридера
main.setAttribute('aria-hidden', 'true')
// Убеждаемся, что модалка доступна
modal.setAttribute('aria-hidden', 'false')
// Перемещаем фокус внутрь модального окна
modal.querySelector('button').focus()
}
// Закрываем модалку
function closeModal() {
const main = document.getElementById('main-content')
const modal = document.getElementById('modal')
// Возвращаем основной контент в дерево доступности
main.removeAttribute('aria-hidden')
// Можно скрыть модалку визуально и из доступности
modal.setAttribute('aria-hidden', 'true')
}Теперь вы видите, как правильно чередовать состояния, чтобы пользователь скринридера не оказался в «пустом» интерфейсе.
Взаимодействие aria-hidden с другими атрибутами и ролями
Aria-hidden и aria-live
Зона aria-live используется для чтения динамических изменений контента (уведомлений, ошибок, системных сообщений). Если вы добавите aria-hidden="true" к aria-live-области, сообщения не будут озвучены.
Неверно:
<div aria-live="polite" aria-hidden="true">
<!-- Сообщения об ошибках формы, но скрыты от скринридера -->
</div>Даже если JavaScript будет обновлять текст, скринридер его не прочитает из-за aria-hidden.
Правильно:
<div aria-live="polite">
<!-- Здесь мы не используем aria-hidden -->
</div>Если вам нужно скрыть визуальное отображение, используйте CSS (например, visually hidden), но не aria-hidden.
Aria-hidden и role
Сами по себе роли (role="button", role="dialog", role="alert" и т. д.) описывают тип элемента. Aria-hidden важнее роли: если элемент скрыт aria-hidden="true", роль игнорируется.
Посмотрите, как это выглядит:
<div role="alert" aria-hidden="true">
Файл не удалось загрузить
</div>Комментарии:
<div role="alert" aria-hidden="true">
<!-- Здесь role="alert" пытается сделать блок важным сообщением -->
<!-- Но aria-hidden="true" полностью исключает его из дерева доступности -->
<!-- В результате скринридер не прочитает это предупреждение -->
Файл не удалось загрузить
</div>Если элемент должен быть озвучен, роль и aria-hidden должны работать согласованно: роль включаем, aria-hidden не используем или явно ставим "false".
Aria-hidden и tabindex
Tabindex управляет доступностью элемента для фокуса с клавиатуры. Aria-hidden управляет доступностью для ассистивных технологий. Эти две сущности должны быть согласованы.
Неверная комбинация:
<button type="button" tabindex="0" aria-hidden="true">
Следующий шаг
</button>Комментарии:
<button type="button" tabindex="0" aria-hidden="true">
<!-- tabindex="0" делает кнопку доступной для фокуса с клавиатуры -->
<!-- aria-hidden="true" скрывает ее от скринридера -->
<!-- В результате фокус попадает на кнопку, но скринридер молчит -->
Следующий шаг
</button>Если элемент скрыт от ассистивных технологий, он не должен получать фокус. Варианты решения:
- удалить tabindex
- или визуально и логически убрать элемент из интерфейса
- или убрать aria-hidden, если элемент действительно нужен пользователю скринридера
Практические паттерны использования aria-hidden
Паттерн: «визуально скрытый текст, доступный для скринридера»
Иногда нужно показать один текст визуально, а другой — только скринридеру. В таких случаях aria-hidden применяется к визуальной части, а вспомогательный класс — к доступной.
Пример:
<button type="button">
<span aria-hidden="true">✉</span>
<!-- Иконка письма - чисто визуальная -->
<span class="sr-only">Открыть почту</span>
<!-- Текст, который прочитает только скринридер -->
</button>Комментарии:
/* Пример стиля sr-only для скрытия текста визуально */
.sr-only {
/* Скрываем элемент с экрана */
position: absolute; /* Убираем из потока документа */
width: 1px; /* Минимальный размер */
height: 1px;
padding: 0;
margin: -1px; /* Смещаем за пределы viewport */
overflow: hidden; /* Скрываем содержимое */
clip: rect(0, 0, 0, 0); /* Отрезаем область отображения */
border: 0;
}Здесь aria-hidden и sr-only работают в паре: один скрывает от скринридера, другой — от зрения.
Паттерн: «переключение состояния кнопки»
Давайте посмотрим, как можно реализовать кнопку «Показать/Скрыть пароль» так, чтобы она была корректной с точки зрения доступности.
<label for="password">Пароль</label>
<div class="password-field">
<input id="password" type="password" />
<button type="button" aria-pressed="false" aria-label="Показать пароль">
<span aria-hidden="true" class="icon icon-eye-open"></span>
<!-- Иконка глаза - скрыта от скринридера -->
</button>
</div>Комментарии к логике JavaScript:
const button = document.querySelector('.password-field button')
const input = document.getElementById('password')
button.addEventListener('click', () => {
const isVisible = input.type === 'text'
if (isVisible) {
// Возвращаем скрытие пароля
input.type = 'password'
button.setAttribute('aria-pressed', 'false')
button.setAttribute('aria-label', 'Показать пароль')
} else {
// Делаем пароль видимым
input.type = 'text'
button.setAttribute('aria-pressed', 'true')
button.setAttribute('aria-label', 'Скрыть пароль')
}
})Здесь иконка чисто визуальна, поэтому aria-hidden="true" на ней оправдан. А само состояние доступно через aria-pressed и aria-label.
Паттерн: «скрытие фона при открытой модалке»
Я уже немного показывал этот пример выше, но теперь разберем его более целостно.
HTML:
<main id="main-content">
<h1>Товары</h1>
<button type="button" id="buy-button">Купить</button>
</main>
<div id="overlay" class="hidden" aria-hidden="true"></div>
<!-- Визуальный оверлей, полностью скрытый от скринридера -->
<div id="modal" class="hidden" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">Оформление заказа</h2>
<p>Подтвердите покупку товара</p>
<button type="button" id="confirm-button">Подтвердить</button>
<button type="button" id="cancel-button">Отмена</button>
</div>CSS:
.hidden {
display: none;
/* Элемент не отображается и недоступен для фокуса */
}
#overlay {
/* Оверлей занимает весь экран */
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
}JavaScript:
const main = document.getElementById('main-content')
const modal = document.getElementById('modal')
const overlay = document.getElementById('overlay')
const buyButton = document.getElementById('buy-button')
const cancelButton = document.getElementById('cancel-button')
function openModal() {
// Скрываем основной контент от скринридера
main.setAttribute('aria-hidden', 'true')
// Делаем модалку и оверлей видимыми
modal.classList.remove('hidden')
overlay.classList.remove('hidden')
// Убеждаемся, что модалка доступна
modal.removeAttribute('aria-hidden')
// Переносим фокус в модальное окно
document.getElementById('confirm-button').focus()
}
function closeModal() {
// Возвращаем основной контент в доступность
main.removeAttribute('aria-hidden')
// Скрываем модалку и оверлей
modal.classList.add('hidden')
overlay.classList.add('hidden')
// Скрываем модалку от ассистивных технологий
modal.setAttribute('aria-hidden', 'true')
// Возвращаем фокус на исходную кнопку
buyButton.focus()
}
buyButton.addEventListener('click', openModal)
cancelButton.addEventListener('click', closeModal)Обратите внимание, как этот фрагмент кода решает задачу:
- aria-hidden применен к фону, чтобы пользователь скринридера не мог «уйти» из модалки
- сама модалка всегда остается видимой в дереве доступности, когда она открыта
- визуальное скрытие и доступность синхронизированы
Частые ошибки при использовании aria-hidden
Ошибка 1. Использовать aria-hidden вместо CSS для скрытия
Иногда встречается такой код:
<div aria-hidden="true">
Этот текст не должен быть виден
</div>И нет никаких стилей. В итоге:
- зрячий пользователь видит текст
- пользователь скринридера текст не видит
Это приводит к расхождению в опыте. Если вы хотите скрыть элемент полностью (и визуально, и для скринридера), правильнее:
<div class="hidden">
Этот текст не должен быть ни виден, ни прочитан
</div>.hidden {
display: none;
/* Элемент не попадает ни в визуальное представление, ни в дерево доступности */
}Aria-hidden — не замена display: none или visibility: hidden.
Ошибка 2. Aria-hidden на корневых контейнерах
Еще одна проблема — ставить aria-hidden="true" на большие и важные контейнеры, например:
<body aria-hidden="true">
<!-- Вся страница скрыта от ассистивных технологий -->
</body>Или:
<div id="app" aria-hidden="true">
<!-- Все, что отрендерил фреймворк, недоступно -->
</div>Такой код полностью ломает доступность. Если нужно временно скрыть фоновый контент, делайте это только при открытой модалке или подобном оверлее, и только на период его существования.
Ошибка 3. Aria-hidden и динамический контент
При динамическом обновлении содержимого блоков важно следить, что aria-hidden не остался в «неправильном» состоянии.
Например, вы сначала загружаете спиннер:
<div id="content" aria-busy="true" aria-hidden="true">
Загрузка...
</div>Затем подгружаете текст, но забываете убрать aria-hidden:
const content = document.getElementById('content')
content.textContent = 'Данные успешно загружены'
// content.removeAttribute('aria-hidden') // забыли это сделать
content.setAttribute('aria-busy', 'false')В итоге:
- пользователь зрительно видит текст
- скринридер его не читает, потому что блок все еще скрыт aria-hidden
Здесь важно помнить: если вы ставите aria-hidden динамически, всегда закладывайте в код путь его снятия.
Лучшие практики и рекомендации
Общие правила
Подведем несколько практических правил, которыми вы можете руководствоваться:
- Не используйте aria-hidden для элементов, с которыми нужно взаимодействовать (кнопки, ссылки, поля форм).
- Не ставьте aria-hidden на фокусируемые элементы или их контейнеры.
- Не скрывайте с aria-hidden зоны aria-live, если они должны зачитываться.
- Для полного скрытия элемента (и визуально, и логически) используйте CSS (display: none) вместо aria-hidden.
- Используйте aria-hidden для:
- декоративных иконок
- дублирующего визуального контента
- сложных визуальных компонентов, для которых есть отдельное, простое текстовое описание.
Тестирование с реальными скринридерами
Только кода недостаточно. Чтобы проверить, как aria-hidden влияет на доступность, стоит тестировать:
- NVDA + Firefox (Windows)
- JAWS + Chrome/Edge (Windows)
- VoiceOver + Safari (macOS, iOS)
- TalkBack (Android)
Минимальный сценарий проверки:
- Пройдитесь по странице с клавиатуры (Tab, Shift+Tab).
- Слушайте, что произносит скринридер.
- Сравните: все ли видимые кнопки/ссылки/поля озвучены.
- Убедитесь, что нет «тихих» фокусов (когда фокус есть, а скринридер молчит).
- Проверьте диалоги и модальные окна: при открытии фокус должен переходить внутрь, фон — становиться недоступным скринридеру.
Если вы видите элемент, но не слышите его — проверьте, не стоит ли на нем или его предке aria-hidden="true".
Интеграция с фреймворками
Во фреймворках вроде React, Vue, Angular aria-hidden часто управляется из кода. Пара замечаний:
- Вместо true/false как булевых значений задавайте строки "true"/"false":
- в React: aria-hidden={isHidden ? 'true' : 'false'}
- во Vue: :aria-hidden="isHidden ? 'true' : 'false'"
- Удобно вынести паттерны в компоненты:
- компонент Icon с aria-hidden="true" по умолчанию
- компонент VisuallyHiddenText для sr-only-текста
- При работе с порталами (рендер модалок вне основного DOM-контейнера) важно не забывать корректно управлять aria-hidden у основной области.
Заключение
Aria-hidden — мощный, но потенциально опасный атрибут. Он не скрывает элементы визуально, но полностью меняет восприятие интерфейса пользователями скринридеров. Если применять его аккуратно и осознанно, можно:
- убрать лишние повторы информации
- скрыть декоративные элементы
- упростить сложные визуальные компоненты для ассистивных технологий
Но если злоупотреблять aria-hidden, легко:
- сделать интерактивные элементы недоступными
- создать «немые» фокусы
- спрятать важные сообщения и ошибки
Чтобы использовать aria-hidden правильно, полезно постоянно держать в голове дерево доступности, а не только DOM. И регулярно проверять интерфейсы с реальными скринридерами, а не полагаться только на визуальный результат.
Частозадаваемые технические вопросы по aria-hidden
Вопрос 1. Как корректно скрыть элемент и визуально и от скринридера
Если элемент не нужен ни пользователям, ни ассистивным технологиям, достаточно CSS:
<div class="hidden-block">Вспомогательный текст</div>.hidden-block {
display: none; /* Элемент не участвует ни в визуальном рендеринге, ни в дереве доступности */
}Aria-hidden в таком случае не нужен. Главное — не оставлять элемент фокусируемым.
Вопрос 2. Как скрыть только часть текста внутри элемента
Оборачивайте скрываемый фрагмент в контейнер и применяйте aria-hidden к нему:
<p>
Основной текст
<span aria-hidden="true"> (служебная пометка)</span>
</p>Так основной текст будет прочитан, а пометка — нет. Важно, чтобы пометка не содержала критически важную информацию.
Вопрос 3. Как временно скрыть элемент от скринридера при анимации
Во время анимаций (например, смена слайдов) можно временно скрыть старый контент aria-hidden="true", а новый — сделать доступным:
oldSlide.setAttribute('aria-hidden', 'true')
newSlide.setAttribute('aria-hidden', 'false')Параллельно управляйте видимостью через CSS (opacity, transform). Следите, чтобы скрытый aria-hidden элемент не получал фокус.
Вопрос 4. Как использовать aria-hidden в компонентах иконок
Создайте компонент Icon, который по умолчанию скрыт от скринридера:
<span class="icon icon-search" aria-hidden="true"></span>Если иконка несет смысл и должна быть доступна, вместо aria-hidden используйте:
<span class="icon icon-error" role="img" aria-label="Ошибка"></span>То есть aria-hidden для чисто декоративных иконок, а role="img" + aria-label для содержательных.
Вопрос 5. Что делать если дизайнер требует текст только для зрячих пользователей
Используйте aria-hidden на визуальном тексте и добавляйте альтернативу для скринридера:
<p>
<span aria-hidden="true">-50 процентов на все товары</span>
<span class="sr-only">Скидка пятьдесят процентов на все товары</span>
</p>Так вы сохраните визуальное оформление и обеспечите корректное произношение для ассистивных технологий.
Постройте личный план изучения Html до уровня Middle — бесплатно!
Html — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Все гайды по Html
Лучшие курсы по теме

HTML и CSS
Антон Ларичев
TypeScript с нуля
Антон Ларичев