Aria-describedот не работает с role="listitem" в Safari с VoiceOver

Я пытаюсь сделать список доступным, используя role = "list" в качестве родительского элемента и role = "listitem" для элементов. Я хочу, чтобы каждый элемент списка имел описание с использованием aria-describedby в дополнение к текстовому содержимому.

Вот упрощенная версия моего кода:

<div class = "wrapper">
  <div id = "rbd-hidden-text-11-hidden-text-132" style = {{ display: "none" }}>
    This text should be read by screen reader
  </div>
  <div role = "list" style = {listStyle}>
    {items.map((item, index) => (
      <div
        key = {index}
        // NOTE: using listitem, you can focus item's focusable
        // children like buttons but aria-describedby is not read on
        // macOS
        role = "listitem"
        // Button role makes aria-describedby readable by macOS VO
        // but you cannot focus its focusable children :(
        // role = "button"
        style = {listItemStyle}
        tabIndex = {0}
        // macOS reads this only when role = "button"
        aria-describedby = "rbd-hidden-text-11-hidden-text-132"
        // aria-labelledby = "rbd-hidden-text-11-hidden-text-132"
        // aria-description = "Is this read by a screen reader?"
      >
        {item}
        &nbsp;
        <button>Action1</button>
        &nbsp;
        <button>Action2</button>
      </div>
    ))}
  </div>
</div>

При тестировании на macOS Chrome все работает нормально. Когда VoiceOver фокусирует элемент списка, описание читается как положено. Однако это не работает в Safari.

Изменение role на button делает aria-describedby доступным для чтения VoiceOver на macOS, но создает еще одну проблему: элементы с role = "button" не позволяют VoiceOver сосредоточиться на интерактивных детях.

Мои вопросы:

  1. Как мне заставить aria-describedby работать с role = "listitem" в Safari с помощью VoiceOver?
  2. Есть ли обходной путь, позволяющий aria-describedby работать, не меняя роль на button и при этом позволяя интерактивным детям сосредоточиться?

Любая помощь или предложения будут очень признательны!

Демо-версия CodeSandbox

Возможно, взгляните на очень похожий вопрос, заданный недавно о aria-describedby и NVDA: stackoverflow.com/questions/78484713/… . В заключение: aria-describedby ненадежно. См. также [короткую заметку Леони Уотсон о […] aria-describedby]( tpgi.com/…)

Andy 29.05.2024 10:11
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
1
88
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

У меня нет прямых ответов на ваши вопросы, но некоторые элементы вашего кода меня насторожили:

  1. Ваша aria-describedby относится к скрытому элементу с display:none. Это посылает программе чтения с экрана противоречивый сигнал: следует ли читать, потому что это описание чего-то другого, или не следует читать из-за display:none ? Хотя это может работать с некоторыми комбинациями ОС + браузер + программа чтения с экрана, вы будете в большей безопасности, если учтете, что display:none побеждает все (и, следовательно, он не будет прочитан). То же самое и с видимостью: скрытый или скрытый атрибут HTML5. Вместо этого вам следует попробовать .sr_only/.visually-hidden text.
  2. У вас проблема с вложенным фокусом: у вас есть родительский элемент с tabindex=0 и несколько кнопок внутри него. Это антишаблон доступности, я уже объяснил, почему в нескольких других ответах SO. Вероятно, вам следует удалить tabindex=0 для родительского элемента и оставить фокусируемыми только дочерние элементы.
  3. Атрибуты aria-label, aria-labeledby, aria-description и aria-describedby не гарантированно будут работать, если они не используются с элементами, которые являются интерактивными/фокусируемыми или ориентирами, и если эти элементы не имеют роли, соответствующей такого рода функции.
  4. Роли list и listitem не должны применяться к фокусируемым элементам. Их естественными соответствующими элементами являются <ul>, <ol> и <li>. Например, вы можете попробовать список ролей/опцию, панель инструментов/кнопку или меню/пункт меню. Напоминаем, что постарайтесь взять соответствующий элемент с самого начала, вообще не используя атрибут роли, если можете.

Это дает четыре веские причины не читать описания так, как вы ожидаете.

Не могли бы вы поделиться ссылкой, где вы объяснили «проблему вложенного фокуса»?

tprieboj 06.06.2024 22:39

Подводя итог короткой заметке Леони Уотсон к […] арии, описанной:

Если вы используете […] aria-describedby с любыми элементами, отличными от интерактивных или ориентировочных (например, div, span, p, blockquote или Strong и т. д.), они, как правило, не будут работать во всех комбинациях браузера и вспомогательных технологий.

Поскольку текст описания скрыт, правильно использовать aria-description вместо ссылки.

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

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

/* ie9+ */
/* by Scott O’Hara
   https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html
*/
.visually-hidden:not(:focus):not(:active) {
  clip: rect(0 0 0 0); 
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap; 
  width: 1px;
}
<ul>
  <li>
    {item}
    <span class = "visually-hidden">This text should be read by screen reader</span> …
  </li>
</ul>

Другие вопросы по теме