Skip to main content

Як приховати елементи візуально, але зберегти їх доступними для екранних читалок

Візуально прихований елемент - елемент, видалений із видимого потоку, але присутній у DOM і дереві доступності, тому екранні читалки (screen readers) на кшталт NVDA чи VoiceOver його озвучують.

Теорія

TL;DR

  • Уявіть шепіт у галасливій кімнаті: зрячі нічого не бачать, екранні читалки чують кожне слово.
  • .sr-only стискає елемент до 1px і виносить його за видимий потік; aria-hidden="true" навпаки — блокує екранні читалки, але залишає елемент видимим.
  • .sr-only — для підказок, міток і skip-посилань. aria-hidden — для іконок та декоративних елементів.
  • display: none ховає від усіх, включно з читалками. Це найпоширеніша помилка.

Швидкий приклад

html
<style> .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } </style> <button>Надіслати</button> <!-- Екранна читалка оголошує це після підпису кнопки --> <span class="sr-only">Натисніть Enter, щоб відправити форму</span>

Зрячі бачать тільки «Надіслати». NVDA і VoiceOver зачитують підказку одразу після кнопки.

Ключова різниця: .sr-only проти aria-hidden

.sr-only через position: absolute виводить елемент із нормального потоку, а clip: rect(0,0,0,0) обрізає намальовану область до 1px. DOM і дерево доступності залишаються незмінними — читалки проходять по ньому звично. aria-hidden="true" працює навпаки: позначає вузол як невидимий для допоміжних технологій, але елемент залишається на екрані. Bootstrap називає це .visually-hidden, Tailwind — sr-only. Реалізація однакова.

Коли що використовувати

  • Підказка або правило валідації тільки для екранних читалок: .sr-only на <span> з прив'язкою через aria-describedby
  • Кнопка-іконка, де іконка потребує текстового підпису: aria-hidden="true" на іконку, aria-label на кнопку
  • Skip-посилання (перехід до основного вмісту): .sr-only плюс :focus для відображення при навігації клавіатурою
  • Декоративний градієнт або фонове зображення: aria-hidden="true" або видалити з DOM
  • Оголошення стану у динамічному тоглі: .sr-only-текст у поєднанні з aria-expanded

Як це працює у браузері

position: absolute виводить елемент із нормального потоку. clip: rect(0,0,0,0) застарілий, але досі підтримується у Chrome, Firefox і Safari. Сучасна альтернатива — clip-path: inset(100%), яка краще працює з трансформованими елементами. Екранні читалки звертаються до дерева доступності через DOM-методи на кшталт computedRole і computedLabel. CSS-розмітка на це дерево не впливає. aria-hidden впливає, бо це прямий сигнал для API доступності.

JavaScript тут не потрібен. Приховування відбувається під час парсингу, лише через CSS.

Типові помилки

Використання display: none в надії, що читалка все одно прочитає елемент

html
<!-- Неправильно: елемент видалено з дерева доступності (W3C UAAG) --> <span style="display: none;">Текст підказки</span>

Рішення: використовуйте .sr-only.

Припущення, що visibility: hidden доступний для читалок

html
<!-- Неправильно: NVDA і VoiceOver пропускають, як і display: none --> <span style="visibility: hidden;">Текст підказки</span>

Рішення: .sr-only.

aria-hidden="true" на інтерактивному елементі

html
<!-- Неправильно: користувачі клавіатури не можуть активувати кнопку --> <button aria-hidden="true">Надіслати</button>

Chrome 89+ блокує фокус на елементах з aria-hidden. Якщо кнопка інтерактивна — цей атрибут на ній неприпустимий.

aria-hidden на обгортці, яка містить фокусовані елементи

jsx
// Неправильно: кнопка недосяжна для читалки, але залишається в tab-порядку <div aria-hidden="true"> <button>Закрити</button> </div>

Використовуйте натомість атрибут inert (Chrome 102+). Він одночасно блокує фокус і дерево доступності без пастки піддерева.

Помилка з display: none трапляється в аудитах доступності набагато частіше, ніж здається. Зазвичай це означає, що CSS-клас скопіювали без реального тестування з читалкою.

Де зустрічається у реальних проектах

  • Bootstrap 5: .visually-hidden для підписів іконок у навбарах
  • Tailwind CSS 3: утиліта sr-only в описах форм і радіо-групах
  • WordPress Gutenberg: skip-посилання через .screen-reader-text
  • React Aria (Adobe): компонент HiddenSelect використовує off-screen spans для нативних select-опцій
  • Material-UI v5: вбудовані sx-стилі з position: absolute; width: 1px у компонентах Drawer

Питання для поглибленого розуміння

Q: Яка різниця між clip: rect(0,0,0,0) і clip-path: inset(100%)?
A: clip застарілий, Firefox 112 прибрав його підтримку. clip-path: inset(100%) краще обробляє трансформовані елементи. Для широкої сумісності тримайте обидва правила в одному класі.

Q: Чи працює .sr-only всередині flex або grid контейнера?
A: Так. position: absolute виводить елемент із flex або grid потоку. Перевіряйте порядок читання через NVDA у Windows Chrome.

Q: Коли краще aria-label, а коли .sr-only span?
A: aria-label підходить для короткого тексту на одному інтерактивному елементі. .sr-only span — для багаторядкових описів або коли текст потрібно прив'язати через aria-describedby.

Q: Як правильно використовувати aria-hidden у фоні модального вікна?
A: Фон отримує aria-hidden="true". Панель самого модального вікна — ні. Якщо aria-hidden потрапляє на обгортку з кнопкою закриття, краще використовуйте inert. Він блокує і фокус, і дерево доступності одночасно.

Q: Чи є різниця між VoiceOver на macOS і NVDA на Windows при зчитуванні .sr-only?
A: Обидва беруть текст з DOM через власні API. VoiceOver пріоритизує зв'язки через aria-describedby. NVDA повертається до innerText, коли ARIA-зв'язків немає. Тестуйте на обох платформах, якщо порядок оголошень важливий.

Приклади

Skip-посилання з відображенням при фокусі

html
<style> .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } /* Відображається при навігації клавіатурою (Tab) */ .sr-only:focus { position: static; width: auto; height: auto; background: white; color: black; padding: 0.5rem; clip: auto; } </style> <a href="#main" class="sr-only">Перейти до основного вмісту</a> <main id="main">Вміст сторінки</main>

При натисканні Tab посилання з'являється вгорі сторінки для користувачів клавіатури. На сенсорних пристроях стиль :focus може не спрацювати — тестуйте на різних платформах перед випуском.

React-форма з прихованою підказкою для читалки

jsx
// Форма входу: видима мітка, правила пароля приховані від зрячих function LoginForm() { return ( <form> <label htmlFor="password">Пароль</label> <input id="password" type="password" aria-describedby="password-rules" /> {/* Зрячим: невидимий. Читалка: зачитує після фокусу на полі */} <span id="password-rules" className="sr-only"> Використовуйте 8 або більше символів з цифрою та спеціальним знаком </span> <button type="submit">Увійти</button> </form> ); }

NVDA оголошує: «Пароль, редагування, Використовуйте 8 або більше символів з цифрою та спеціальним знаком». Зрячим форма виглядає чисто.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?