TGViewer
<divelopers> <divelopers> @alexnozer_dev · 1.18K subscribers
Post #354 825
Диалог, форма, список выбора и нарушения WCAG

При аудите столкнулся с компонентом переключения страны/валюты в магазине. Он использует кнопку раскрытия диалога, в котором находится форма со списком выбора (listbox), внутри которого опции в виде кнопок отправки формы:

<button
type="button"
aria-expanded="false"
aria-haspopup="dialog"
aria-controls="popover-id"
aria-label="Change country or currency"
>
United States (USD $)
</button>
<x-popover
role="dialog"
id="popover-id"
>
<form action="...">
<!-- скрытые поля -->
<x-listbox
role="listbox"
aria-activedescendant="option-us"
>
<!-- другие опции -->
<button
type="submit"
id="option-gb"
name="country_code"
value="GB"
role="option"
>
United Kingdom (GBP £)
</button>
<button
type="submit"
id="option-us"
name="country_code"
value="US"
role="option"
aria-selected="true"
>
United States (USD $)
</button>
<!-- другие опции -->
</x-listbox>
</form>
</x-popover>

Достаточно маленький компонент, а нарушает сразу несколько критериев WCAG:

- 2.1.1 Keyboard — в списке выбора не реализована соответствующая механика клавиатурного взаимодействия. Фокус должен попадать на listbox, опции переключаться стрелками/Home/End с фокусом через aria-activedescendant;

- 2.5.3 Label in Name — имя кнопки раскрытия диалога не совпадает с видимой подписью. Видимая подпись — «United States (USD $)», а программно заданное с помощью атрибута aria-label имя — «Change country or currency»;

- 3.2.2 On Input — выбор опции из списка приводит к отправке формы и смене контекста. Из-за реализации опций на основе кнопок с атрибутом type="submit" при нажатии происходит отправка формы с перезагрузкой страницы;

- 3.3.2 Labels or Instructions — нет видимой подписи у кнопки раскрытия диалога и поля выбора. В первом случае подпись задана через aria-label, а во втором случае не задана ни видимым элементом, ни программно;

- 4.1.2 Name, Role, Value — у поля выбора нет ассоциированного имени. У listbox должно быть имя, которое задано одним из способов. В данном случае имя не задано ни одним из способов.

Правильнее было бы не делать внутри формы список выбора и оставить обычный список кнопок отправки формы. Такие кнопки явно дают понять, что ожидается смена контекста. Исправленная версия разметки:

<p id="label-id">
Change country or currency
</p>
<button
type="button"
id="button-id"
aria-expanded="false"
aria-haspopup="dialog"
aria-controls="popover-id"
aria-labelledby="label-id button-id"
>
United States (USD $)
</button>
<x-popover
role="dialog"
id="popover-id"
>
<form action="...">
<!-- скрытые поля -->
<ul>
<!-- другие кнопки -->
<li>
<button
type="submit"
name="country_code"
value="GB"
>
United Kingdom (GBP £)
</button>
</li>
<li>
<button
type="submit"
name="country_code"
value="US"
aria-current="true"
autofocus
>
United States (USD $)
</button>
</li>
<!-- другие кнопки -->
</ul>
</form>
</x-popover>


Убран listbox, кнопка раскрытия получила видимую подпись и составное имя с помощью aria-labelledby, добавлен обычный список, убрана семантика option и состояние aria-selected, вместо него теперь aria-current, добавлен autofocus для установки фокуса при открытии диалога.

На мой взгляд такая реализация работает лучше, не требует более сложного виджета списка выбора со своей механикой клавиатурного взаимодействия, более понятна пользователям и не нарушает упомянутые критерии WCAG.

#html #ui #a11y
  • ❤ 8
  • 👍 5
  • 🤩 2
  • 🌚 1
More from @alexnozer_dev
  1. Sep 21, 2026Реализация пользовательских атрибутов Под конец прошлого года я рассказывал об API пользов…
  2. Sep 14, 2026Подклассы Event вместо CustomEvent Многие библиотеки предоставляют систему событий в качес…
  3. Sep 11, 2026Эволюция стилей в темах Shopify Адам Ватан на днях поделился новостью, что Shopify выкупил…
  4. Sep 9, 2026Persistent Widgets Многие сайты не должны быть SPA. Но иногда эта архитектура продиктована…
  5. Sep 7, 2026Processing Instructions и маркеры В DOM всё представлено в виде узлов (Node) разных типов.…
  6. Sep 1, 2026@scope и потоковая передача HTML Ноам Розенталь поделился интересной техникой, в которой с…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →