Приветствую. Представляю вашему вниманию перевод заметок с сайта HTMHell - коллекции плохих примеров HTML-кода, взятых из реальных проектов. Каждая заметка включает сам фрагмент плохого кода, который дополняется объяснениями, в чём именно ошибки и почему так лучше не делать. А в заключение предлагается вариант, который считается более корректным. Так как я объединил в единую статью заметки, которые изначально являются независимыми и самодостаточными, во время перевода позволил себе иногда немного отходить от стилистики изначальных формулировок, пытаясь сделать текст менее "сухим" и более последовательным. Плохой код Ошибки и что следует исправить Пример неправильного использования элемента <button>. Для ссылок на другую страницу или сайт следует использовать элемент <a> . Не пренебрегайте семантикой HTML-тегов, если только в этом нет явной потребности Благодаря элементам <a>, на страницы можно ссылаться и без использования JavaScript Атрибут title описывает содержимое элемента в виде всплывающей подсказки и для элементов <button> указывать его излишне В атрибуте tabindex также нет необходимости, ведь при переключении между элементами с помощью клавиатуры кнопки получают фокус по умолчанию Хороший код Плохой код Ошибки и что следует исправить Нет необходимости пытаться задать семантику <div>-элемента с помощью атрибута role, ведь вместо этого достаточно просто использовать элемент <button> При использовании <button> не понадобится и атрибут tabindex . HTML-кнопки по умолчанию могут получать фокус На <div>-элементах событие клика вызывается только непосредственно кликом мыши. На элементах же <button> это происходит ещё и при нажатии на кнопки Enter или Space на клавиатуре SVG-иконки, расположенной внутри нашей псевдокнопки не хватает текстовой альтернативы на случай, если SVG не отобразится Хороший код Чтобы предназначение кнопки было понятно не только визуально, но и оставалось доступным для пользователей скринридеров, внутрь добавляется описательный текст. Тексту присваивается класс .sr-only с набором свойств, делающим его скрытым только визуально  Плохой код Ошибки и что следует исправить Элемент <img> предназначен отнюдь не для выполнения JavaScript, а для показа изображений Как и на упомянутом ранее <div>, на элементе img>  событие клика вызывается только непосредственно кликом мыши. Если бы вместо него использовался элемент <button> , это происходило бы ещё и при нажатии кнопок Enter или Space на клавиатуре Для самого изображения не задана текстовая альтернатива (атрибут alt ). Из-за этого скринридеры могут озвучивать название самого файла изображения, что далеко не всегда информативно Хороший код Решение №1: Использовать кнопки, а к помещённым внутрь кнопок изображениям добавить атрибут alt Решение №2: Использовать кнопки и вместо добавления атрибута alt к изображениям, добавить описание в текстовые элементы Изображения с пустым атрибутом alt недоступны для пользователей скринридеров, что нам и нужно, ведь название кнопок мы добавили в виде отдельного текста, визуально скрытого с помощью набора свойств упомянутого выше класса .sr-only Плохой код Ошибки и что следует исправить Вкладывая кнопку внутрь ссылки, вы подаёте сразу два сигнала: это кнопка, но также это и ссылка Если вы не уверены, когда нужно использовать элемент <a>, а когда <button>, рекомендую посмотреть видео "The Links vs. Buttons Showdown" от Marcy Sutton Хороший код Плохой код Контекст: это ссылка, стилизованная под кнопку. Ведёт она на форму, расположенную на этой же странице Ошибки и что следует исправить Добавляя к ссылке role="button" , вы сообщаете, что это кнопка, хотя она ведёт себя как ссылка. Не меняйте семантику элементов, только если в этом нет серьезной необходимости Атрибут aria-haspopup="true" призван сообщать вспомогательным устройствам, что данный элемент вызывает попап, но в нашем случае этого не происходит Внутренний отступ padding следует добавлять к элементам через CSS, а не с помощью &nbsp; Хороший код Плохой код Ошибки и что следует исправить Если JavaScript-код не загрузился или не может быть выполнен, использующая его ссылка просто перестанет работать Да и для ссылки на другую страницу нет необходимости использовать JavaScript. Адрес можно указать в атрибуте href, который 100% поддерживается всеми браузерами и будет корректно работать Такая ссылка будет работать только при клике левой кнопкой мыши. Открыть её в новой вкладке/окне щелчком скролла или через конктекстное меню не удастся Хороший код Плохой код Ошибки и что следует исправить Значения атрибутов id должны быть уникальными, независимо от того, к какому тегу они добавляются Данный код использует раскладку, основанную на таблицах (это при том, что дизайн сайта обновлялся в 2016 году). Избегайте разметки страниц с помощью таблиц, потому что эти элементы имеют вполне конкретное семантическое значение и не предназначены для этих целей Текущую разметку следует заменить на семантические HTML5-теги. Это существенно сократит количество тегов и сделает код более понятным При стилизации следует использовать новые технологии Flexbox и CSS Grid, но никак не элементы таблиц Для значений атрибута IDдолжны быть использованы более семантические термины Хороший код Плохой код Ошибки и что следует исправить Элементы <a> следует использовать для ссылки на другие ресурсы: такие как страница или PDF-документ В нашем же случае задача элемента – вызвать JavaScript-действие на текущей странице. Для таких целей лучше подходит элемент <button> с атрибутом type="button" , потому что не имеет поведения по умолчанию и изначально предназначен для вызова действий в ответ на нажатие пользователем Ещё одним серьёзным недочётом данного примера является то, что браузеры и устройства, которые не поддерживают JavaScript, не смогут получить доступ к содержимому модального окна Хороший код Решение №1: Использовать элемент <button> Поскольку целью данного элемента является не навигация, а вызов действия на текущей странице, элемент <button> является семантически более подходящим. Решение №2: Ссылка на отдельную страницу Ещё одно решение - использовать элемент <a> , у которого в атрибуте hrefуказать путь на отдельную полноценную страницу, где пользователь сможет проделать те же действия, что и в модальном окне. А переход по указанному пути, который является поведением ссылки по умолчанию, заблокировать через JavaScript Таким образом, получаем некий фолбэк для браузеров и устройств, которые не поддерживают JavaScript или если он по какой-то причине не сработал Это пример прогрессивного улучшения при разработке Плохой код Ошибки и что следует исправить Окно с запросом разрешения хранить данные Cookie появляется сразу при входе на страницу. Конкретно для этого типа модального окна следует делать исключение и помещать  в самом начале кода, чтобы сразу же после загрузки страницы оно получало фокус и было доступно для взаимодействия. Но в нашем примере оно располагается наоборот в конце Кнопки данного окна, реализованные с помощью <div> элементов , а значит не получат фокус при переключении между элементами с помощью клавиатуры Содержимое внутри <div>-кнопок  семантически является просто текстом. Всё это не позволит вспомогательным технологиям вроде экранных читалок понять, что определённые элементы на самом деле являются кнопками Как уже было указано ранее, в дополнение ко всему, на элементах <div>  событие клика вызывается только непосредственно кликом мыши. Если бы всесто них использовались элементы <button>, это происходило ещё и при нажатии на кнопки Enter или Space на клавиатуре Для иконки нет текстовой альтернативы, а значит её назначение можно определить только визуально Font Awesome советует скрывать иконки от скринридеров, добавляя элементам <i> атрибут aria-hidden="true" Font Awesome добавляет Unicode-содержимое через псевдоэлемент ::before. Некоторые вспомогательные технологии могут озвучивать его. Но в данном примере иконка будет названа "разы" (times), поскольку fa-times - это не "крестик", а "знак умножения". Обратите внимание: Talkback и VoiceOver в данном случае не озвучат вообще ничего В завершение можно также добавить, что крайне полезным было бы иметь возможность закрыть модальное окно нажатием Escape Хороший код Для описательного текста кнопок мы снова используем набор свойств, называемых классом .sr-only , который скрывает его визуально, но оставляет доступным для скринридеров Дополнительные материалы Screen readers and cookie consents Плохой код Ошибки и что следует исправить Элементы секционного содержимого (<article>, <aside>, <nav>, <section>)  –  это разделы, которые потенциально можно как-то озаглавить Секционные элементы вкладывать друг в друга можно, только это имеет смысл в ситуациях, когда содержимое внутренных элементов связано с содержимым родителя В данном конкретном случае секционные элементы используются не для того, чтобы разбить содержимое на разделы, а лишь с целью стилизации. Значит большинство из них должны быть элементами <div>, не передающими семантическое значение Когда пользователь переходит к элементу <section>, скринридеры могут озвучивать роль выбранного элемента – region. Устройства также могут предоставлять возможность быстрой навигации по этим разделам. Использование большого количества элементов <section> (в том числе, вложенных) может сделать интерфейс излишне сложным для пользователей экранных читалок Важный момент, который следует понимать – элементы <section> не являются заменой <div> Ещё одна ошибка в данном примере - неправильное использование элемента <header>. Обычно он содержит вводное содержимое для ближайшего родительского элемента <main> или другого секционного элемента. Если <header> не является вложенным, относится ко всей странице Компонент карусели (слайдера) следует озаглавливать и связывать заголовок с главным элементом с помощью атрибута aria-labelledby чтобы позволить пользователям скринридеров легко его найти Хороший код Дополнительные материалы Accessibility Support - section element (html) Плохой код Ошибки и что следует исправить Проблема данной реализации в том, что текст внутри тегов скринридеры могут озвучить как "триграмма неба меню". Причина кроется в символе "☰", который таким образом называется в unicode Задача иконок - декоративное оформление, поэтому они должны быть скрыты от скринридеров. Рассмотрите добавление декоративных изображений с использованием CSS-свойства background Так же, как на упомянутых ранее <div>, и <img>, на элементе <span>  событие клика вызывается только непосредственно кликом мыши. Если вместо него использовать <button> , клик можно будет вызвать ещё и при нажатии на кнопок Enter и Space на клавиатуре. Элемента <span> касаются и проблемы, связанные с невозможностью получения фокуса при переключении между элементами с помощью клавиатуры. А в нашем случае это особенно важно, ведь главная навигация открывается и скрывается именно с помощью данного элемента Для улучшения доступности при открытии навигации к элементу следует добавлять атрибут aria-expanded для обозначения текущего состояния панели. Значением true – если панель открыта, false – если закрыта Хороший код Плохой код Ошибки и что следует исправить Элемент <form> сам по себе семантически подразумевает форму, поэтому нет необходимости повторно указывать это с помощью атрибута role="form" Форма – это важный для доступности страницы ориентир. Поэтому внутрь формы полезно поместить заголовок, а в элементе <form> с помощью атрибута aria-labeledby, сослаться на него. Это существенно облегчает навигацию в документе с помощью вспомогательных технологий Задавать атрибут role="radiogroup" необязательно, и уж точно не дважды. Если нужно сгруппировать элементы, просто используйте <fieldset> Не используйте aria-labelledby для создания связи между радиокнопкой и вопросом. Данный атрибут предназначен для установки доступного имени. Вместо этого для текста вопроса используйте элемент <legend> Чтобы радиокнопке назначить доступное имя, текст из <span> поместите в элемент <label> и свяжите с радиокнопкой с помощью атрибута for Кнопку также следует поместить внутрь <fieldset> для создания одной логической группы элементов Хороший код Плохой код Ошибки и что следует исправить Вкладывать элементы с запускаемым поведением (таким как клик) считается плохим решением Возможность выбора чекбокса путём нажатия на его название улучшает удобство и доступность (путём увеличения области клика) Но в данном случае пользователи не ожидают, что при нажатии на название чекбокса откроется новая страница Размещайте ссылки за пределами элемента <label> Хороший код Источники 4.10.4. The label element Be Wary of Nesting Roles by Adrian Roselli Плохой код Ошибки и что следует исправить В данном примере с элементом <a> используется атрибут type , хотя он никак не влияет на семантику и можно сказать, что совсем неуместен Если его и добавлять, то лишь со значением, являющимся допустимым MIME-типом. Браузеры могут учитывать его, но исключительно как рекомендацию Если это ссылка, которая в атрибуте href содержит путь на какой-то ресурс (страницу или документ), следует использовать элемент <a>, а не <button>, независимо от того, как данный элемент выглядит в дизайне: как ссылка или как кнопка Отрицательное значение tabindex значит, что элемент не получит фокус при переключении между элементами с помощью клавиатуры. Правда, такой элемент всё же может получить фокус с помощью JavaScript Не меняйте семантику элементов, присущую им по умолчанию, если только в этом нет явной необходимости Если вам нужна кнопка, просто используйте элемент <button> Хороший код Источники 4.8.2. Links created by a and area elements 4.5.1. The a element Контекст: буквы обёрнуты в <div> с целью анимирования каждой буквы через JavaScript Плохой код Ошибки и что следует исправить Если каждая буква обёрнута в отдельный элемент, вспомогательные технологии могут озвучивать текст, произнося каждую букву по отдельности Код примера, продемонстрированного на видео Старайтесь избегать разрастания DOM. Слишком большое количество DOM-узлов или вложенных элементов может плохо отразиться на производительности страницы Большое DOM-дерево ведёт к большому дереву доступности, которое также может плохо сказываться и на производительности вспомогательных технологий Рекомендуется отделять представление от содержимого. Стили, которые не изменяются динамично, поместите в CSS-файл Хороший код Решение №1 Решение №2 Если в этом действительно есть необходимость, добавьте версию текста, доступную для скринридеров, а текст, предназначеный для визуального отображения, скройте с помощью aria-hidden="true" К сожалению, не существует встроенного способа скрывать содержимое только визуально. Для этого следует использовать CSS-правило, называемое классом .sr-only которое гарантирует, что контент будет скрыт визуально, но останется доступным для пользователей скринридеров. Источники Uses An Excessive DOM Size Performance and the Accessibility Tree Контекст: список изображений, которые ссылаются на страницы с товаром Плохой код Ошибки и что следует исправить   Если у элемента <a> атрибут href имеет пустое значение или данного атрибута нет вообще, он представляет собой заполнитель для того места, где могла быть ссылка (HTML спецификация) Если вы добавляете обработчик клика к ссылке-заглушке, то, по всей видимости, хотите, чтобы она была не заглушкой, а полноценной ссылкой с атрибутом href или кнопкой <button>, в зависимости от того, что должно происходить при клике Ссылки без href не получают фокус при переключении между элементами с помощью клавиатуры. tabindex исправляет эту ситуацию, но необходимость использовать данный атрибут лишь подтверждает тот факт, что корректно оформленная ссылка была бы в данном случае лучшим выбором Атрибут alt не используется с элементами div и никак не влияет на их семантическое значение Не злоупотребляйте ARIA. Атрибут aria-label лишний для элемента div, потому что img уже имеет доступное имя (значение атрибута alt) Хороший код Полноценная ссылка с заполненным атрибутом href и текстовая альтернатива alt для изображения Источники The a element The accessibility of placeholder links Контекст: список карточек со ссылками, каждая из которых имеет заголовок, изображение и краткое описание Плохой код Ошибки и что следует исправить Вероятнее всего, в подобных ситуациях необходимости в таком количестве элементов <section> нет. Чтобы лучше понять почему, рекомендую прочитать статью "Why You Should Choose HTML5 <article> Over <section>" автора Bruce Lawson Не следует недооценивать важность соблюдения иерархии заголовков. Например, пользователи скринридеров при навигации по сайту нередко ориентируются в структуре документа именно по его заголовкам Согласно спецификации, HTML5-элемент <figure> представляет собой самодостаточный элемент, который под основным содержимым опционально может содержать подпись. Но в этом примере нет содержимого, есть только подпись Изображнеие карточки не является декоративным, оно несёт какую-то информацию и должно быть частью HTML-кода документа, а не добавляться через CSS-свойство background . Фоновые изображения доступны пользователям не всех устройств В приведённом примере обработка клика на карточку происходит только через JavaScript. Если нет элемента ссылки с указанием пути (<a href="path/to/page">), для пользователей скринридеров переход на страницу карточки становится недоступным. Также элемент карточки не получает фокус при навигации с помощью клавиатуры Элементы <h1> - <h6> представляют собой вводный заголовок для родительского элемента <section>. <h4> является потоковым содержимым и, как следствие, технически может быть потомком <figcaption>, но лучше сделать его заголовком всей карточки Элемент <article> представляет собой самодостаточную композицию на странице. Это может быть газетная статья , эссе или отчёт, публикация в блоге или социальной сети. Для обычного абзаца текста лучше использовать элемент <p> Сделать доступной карточку, вся область которой является кликабельной, непросто. Дополнительную информацию можно найти в разделе "источники" ниже Хороший код Источники Semantic Structure Block Links, Cards, Clickable Regions, Etc. Inclusive Components: Cards Teaser with multiple links Block Links Are a Pain (and Maybe Just a Bad Idea) Контекст: главная панель навигации  Плохой код Ошибки и что следует исправить <div> - это элемент, используемый в крайнем случае - когда никакие другие элементы не подходят. Использование <div> вместо более подходящих по семантике элементов ухудшает доступность Для главной панели навигации лучше использовать самантический элемент <nav>. Он является важным для доступности страницы ориентиром, содержащим ссылки на внешние и внутренние страницы. Пользователи скринридеров могут сразу получить доступ к навигации или наоборот пропустить её Используйте элементы <ul> или <ol> для структурирования ссылок, связанных семантически и визуально. Скринридеры обычно объявляют номер элемента в списке, что помогает ориентироваться Если в навигации имеет значение последовательность элементов, используйте <ol> вместо <ul> Если вместо <a> для ссылок использовать элемент <div>, событие клика будет вызываться только непосредственно кликом мыши. Это сделает их недоступными для пользователей скринридеров и тех, кто переключается между элементами с помощью клавиатуры Хороший код Источники 4.3.4. The nav element Menu Structure Menus & Menu Buttons Контекст: простая страница, которая отображает наличие товара Плохой код Ошибки и что следует исправить Элементы <h1> - <h6> не должны использоваться для разметки подзаголовков, альтернативных заголовков и слоганов, если только они не озаглавливают новый раздел или подраздел Все элементы <div> в данном примере излишни. Скорее всего, они присутствуют только потому, что фронтенд-фреймворк добавляет их по умолчанию. Используйте Fragments in React или подобные техники в других фреймворка, чтобы избежать этого Старайтесь избегать разрастания DOM. Слишком большое количество DOM-узлов и вложенных элементов может отрицательно повлиять на производительность страницы Большое DOM-дерево ведёт к большому дереву доступности, что может отрицательно влиять и на скорость работы вспомогательных технологий Потомками <h1> - <h6> могут быть только элементы фразового содержимого. <h3> и <div> к ним не относятся Элемент <i> представляет собой фрагмент, который скринридеры произносят изменённой интонацией, чтобы обозначить его отличие от остального текста. Если вам нужен просто курсивный текст, используйте CSS-свойство font-style: italic Если у элемента <a> нет атрибута href, он представляет собой заглушку в том месте, где в другой ситуации может быть ссылка Если вы добавляете обработчик клика к ссылке-заглушке, то, по всей видимости, хотите, чтобы она была полноценной ссылкой с атрибутом href или кнопкой <button>, в зависимости от того, что должно происходить при клике Хороший код Источники 4.13.1. Subheadings, subtitles, alternative titles and taglines 4.5.22. The i element 4.5.1 The a element В данном спецвыпуске рассматривается один из наиболее сложных и наиболее спорных шаблонов во фронтенд-разработке – кнопка "Закрыть". В модальных окнах, рекламных баннерах и других всплывающих элементах интерфейса часто можно встретить кнопку с символом закрытия, позволяющую пользователям, или, по крайней мере, части из них, закрыть его. Часто функциональность таких кнопок ограничена лишь пользователями мыши, потому что большинство реализаций кнопки закрытия - полный отстой. После недолгого исследования, занявшего менее 2 часов, HTMHell представляет 11 примеров плохой реализации данного элемента Ошибки и что следует исправить <div> - это элемент для крайнего случая, когда никакие другие элементы не подходят. Использование <div> вместо более подходящих по семантике элементов ухудшает доступность На <div>  событие клика вызывается только непосредственно кликом мыши. На элементах <button> это происходит ещё и при нажатии на кнопки Enter или Space на клавиатуре <div> не получает фокус при переключении между элементами с помощью клавиатуры Для фонового изображения невозможно задать текстовую альтернативу Данный элемент скринридеры озвучат: Никак Ошибки и что следует исправить Символ "✕" не является чем-то вроде "Закрыть" или "Перечёркнуто", это знак умножения. Например, 2 ✕ 2 (два умножить на два). В кнопках "Закрыть" использовать его неуместно В первом примере подробно описаны проблемы, связанные с использованием элемента <div> Данный элемент скринридеры могут озвучить: как-то вроде "умножить на" или "разы" (times) Ошибки и что следует исправить Скринридеры могут озвучивать содержимое, которое генерируется через CSS Font Awesome рекомендуют скрывать иконки семантически с помощью атрибута aria-hidden="true" для элемента <i> Font Awesome добавляет Unicode-содержимое через псевдоэлемент ::before. Вспомогательные технологии могут озвучивать эту Unicode-альтернативу, которая в этом конкретном примере будет звучать как "разы" (times), поскольку fa-times - это не крестик, а знак умножения. Обратите внимание: Talkback и VoiceOver в данном примере не озвучат вообще ничего Элемент <i> представляет собой фрагмент, который скринридеры произносят другой интонацией, что обозначить его отличие от остального текста. Если вам нужен просто курсивный текст, используйте CSS-свойство font-style: italic В первом примере подробно описаны проблемы, связанные с использованием элемента <div> Данный элемент скринридеры могут озвучить: "разы" (times) Ошибки и что следует исправить Если элемент <a> содержит атрибут href, он представляет собой ссылку на другой ресурс: такой как страница или PDF-документ Задача элемента в нашем примере – вызвать JavaScript-действие на текущей странице. Элемент <button> с атрибутом type="button" подходит лучше, потому что не имеет поведения по умолчанию и разработан для вызова действий в ответ на нажатие пользователем Если вы не уверены, когда нужно использовать элемент <a>, а когда <button>, посмотрите видео "The Links vs. Buttons Showdown" от Marcy Sutton Скринридеры могут озвучивать содержимое, которое генерирует CSS  Символ "✕" не является чем-то вроде "Закрыть" или "Перечёркнуто", это знак умножения. Например, 2 ✕ 2 (два умножить на два). Не используейте его в кнопках "Закрыть" Данный элемент скринридеры могут озвучить: "ссылка, разы" (link, times) Ошибки и что следует исправить Хорошая попытка, но это всё ещё ссылка, а не кнопка В предыдущем примере подробно написано про использование элемента <a> и генерируемое CSS содержимое Данный элемент скринридеры могут озвучить: "ссылка, разы закрыть" (link, times close) Ошибки и что следует исправить Ещё одна хорошая попытка, но ссылка без атрибута href всё еще не является кнопкой Если у элемента <a> нет атрибута href, он представляет собой заглушку в том месте, где в другой ситуации может быть ссылка Если вы добавляете обработчик клика к ссылке-заглушке, то, по всей видимости, хотите, чтобы она была не заглушкой, а полноценной ссылкой с атрибутом href или кнопкой <button>, в зависимости от того, что должно происходить при клике Ссылки-заглушки не получают фокус при переключении между элементами с помощью клавиатуры Если вы не уверены, когда нужно использовать элемент <a>, а когда <button>, посмотрите видео "The Links vs. Buttons Showdown" от Marcy Sutton Данный элемент скринридеры могут озвучить: "разы, кликабельно" (times, clickable) Ошибки и что следует исправить Для изображения не задана текстовая альтернатива. Скринридеры могут озвучить название файла В 6 примере подробно написано про использование ссылок-заглушек Скринридеры могут озвучить данный элемент: "close.png, изображение" (close.png, image) Ошибки и что следует исправить Когда сторонники доступности говорят "Просто используйте кнопку", они имеют в виду элемент <button>, а не радио-кнопки Радио-кнопки используются в группах, описывающих набор связанных вариантов (опций) У SVG нет текстовой альтернативы. Чтобы больше узнать о доступности SVG, рекомендую почитать статью "Creating Accessible SVGs" автора Carie Fisher Также, display: none на элементе <input>  делает недоступным <label> Данный элемент скринридеры озвучат: Никак Ошибки и что следует исправить Символ "✕" не является чем-то вроде "Закрыть" или "Перечёркнуто", это знак умножения. Например, 2 ✕ 2 (два умножить на два). Не используейте его в кнопках "Закрыть" Данный элемент скринридеры могут озвучить: "разы, кнопка" (times, button) Ошибки и что следует исправить У SVG нет текстовой альтернативы. Чтобы больше узнать о доступности SVG, рекомендую почитать статью "Creating Accessible SVGs" автора Carie Fisher Данный элемент скринридеры могут озвучить: "кнопка" (button) Ошибки и что следует исправить Нет необходимости с помощью атрибута role явно задавать семантику элемента, вместо этого стоит просто использовать элемент <button При использовании <button> атрибут tabindex не нужен. HTML-кнопки получают фокус по умолчанию В 1 примере подробно описаны проблемы, связанные с использованием элемента <div> Буква "X" не является иконкой для кнопки "Закрыть" Данный элемент скринридеры могут озвучить: "икс, кнопка" (X, button) "Использовать букву "X" для кнопок "Закрыть" – это то же, что добавлять в кофе соль вместо сахара, потому что выглядит она так же" Max Böck Решение 1: Кнопка с видимым текстом без иконки Только текст: легко в реализации и понятно для пользователей Скринридеры могут озвучить данный элемент: "Закрыть, кнопка" (Close, button) Решение 2: Кнопка с видимым текстом и только визуально доступной иконкой Если вы вынуждены использовать иконку "умножить", скройте её от скринритеров, обернув в элемент <span> с атрибутом aria-hidden="true" Скринридеры могут озвучить данный элемент: "Закрыть, кнопка" (Close, button) Решение 3: Кнопка со скрытым текстом и только визуально доступной иконкой К сожалению, не существует встроенного способа скрывать содержимое только визуально. Для этого следует использовать CSS-правило, называемое классом .sr-only которое гарантирует, что контент будет скрыт визуально, но останется доступным для пользователей скринридеров. Скринридеры могут озвучить данный элемент: "Закрыть, кнопка" (Close, button) Решение 4: Кнопка со скрытым текстом и только визуально доступной иконкой Если вы не хотите показывать текст на экране, обеспечьте для иконки или SVG текстовую альтернативу, добавив к кнопке атрибут aria-label Скринридеры могут озвучить данный элемент: "Закрыть, кнопка" (Close, button) Решение 5: Font Awesome Для полноты картины, закрывающая кнопка с иконкой Font Awesome Общие примечания Порой может иметь смысл использование более описательных названий "Закрыть окно", "Закрыть галереию", "Закрыть рекламу". Если вы используете сторонние решения для модальных, диалоговых окон, проверяйте, как они были реализованы перед добавлением на сайт. Не полагайтесь на других в вопросах качества и доступности кода. Источники Accessibility support for CSS generated content The a element The accessibility of placeholder links Контекст: кнопка, которая разворачивает и сворачивается блок текста Плохой код Ошибки и что следует исправить <legend> не разрешается помещать в какой-нибудь другой элемент, кроме <fieldset> (HTML спецификация для legend) При использовании <button> атрибут tabindex не нужен. HTML-кнопки получают фокус по умолчанию. Атрибут href не может использоваться с элементом <button> (HTML спецификация для button) Хороший код Источники The legend element The button element Контекст: ссылка на другую страницу Плохой код ... или любой другой вариант этого шаблона, где для ссылки на другую страницу используется отличный от <a> элемент Ошибки и что следует исправить   <div> - это элемент для крайнего случая, когда никакие другие элементы не подходят. Использование <div> вместо более подходящих по семантике элементов ухудшает доступность На <div>  событие клика вызывается только непосредственно кликом мыши. На элементах <a> это происходит ещё и при нажатии на кнопку Enter на клавиатуре. <div> не получает фокус при переключении между элементами с помощью клавиатуры При нажатии правой кнопкой мыши в контекстном меню не будет пунктов "Открыть в новой вкладке/окне" или "Добавить ссылку в закладки" По умолчанию скринридеры просто озвучивают текст внутри <div> (например, "О нас"). В случае использования ссылки <a> скринридеры текст и роль элемента (например, "О нас, ссылка") Атрибуты наподобие aria-label у элементов <div> могут работать неправильно Пользователи скринридеров могут использовать раздел со списком ссылок страницы. <div>-ссылок в этом разделе не будет, если только к элементу не будет добавлен атрибут role="link" Хороший код Плохой код Ошибки и что следует исправить В примере выше используется <article>.  Этот элемент предназначен для самодостаточного содержимого, которое может быть повторно использовано на странице. Если здесь и есть что-то повторно используемое, то вся карточка. Более подходящим является элемент <section> В первом <div> присутствует доступный только для скринридеров текст "Image". Ощущение, что это своего рода определение роли следующего за ним элемента <img>. Правильно подобранные HTML-элементы сами сообщают о своей семантике. Необходимость в дополнительных "уточнениях" отпадает Далее расположен <span>, который, кажется, является заголовком. Вспомогательные технологии могут использовать указанные в коде заголовки для быстрой навигации. Следовательно, более корректным будет использовать элемент заголовка корректного уровня. В данном случае – <h4> Основной текст внутри карточки обёрнут в <div>. Использование <p> лучше передало бы его предназначение "Read more" – не самый лучший текст для ссылки. Это особенно заметно пользователям скринридеров, которые используют навигацию по ссылкам. Из названия непонятно, куда именно ведёт эта ссылка <svg>  внутри ссылки не предоставляет дополнительную информацию и должен быть скрыт от скринридеров Хороший код Источники The Generic Section element Плохой код Ошибки и что следует исправить Каждому элементу <input> нужен <label>. Когда пользователи скринридера переходят к полям формы, озвучивается содержимое <label> и тип поля (например, "имя, поле ввода"). Если этот текст пропущен, пользователи могут не знать, какую информацию они должны указать в этом поле Некоторые скринридеры всё же берут текст из атрибута placeholder, но не стоит полагаться на это По умолчанию, текст placeholder  отображается светло-серым цветом с плохой контрастностью. Людям со слабым зрением может быть трудно его прочитать при ярком солнечном свете С помощью псевдоэлемента ::placeholder можно улучшить контрастность цвета. Но тут тоже нужно не переборщить, поскольку текст с очень контрастным цветом пользователи могут принять за текстовое содержимое поля Использование <label> увеличивает область выбора нужного поля, что может быть очень полезным, особенно на устройствах с сенсорным экраном Если placeholder  является единственным местом, содержащим название поля, при вводе текста он скрывается. Это ухудшает удобство заполнения форм, особенно если содержат много полей.  Пользователи не могут проверить, правильно ли они заполнили форму, потому что видят только значения, но не названия полей. Если браузер заполняет поле автоматически, пользователь вынужден вырезать содержимое поля, чтобы убедиться, что оно заполнилось корректно Текст placeholder обрезается, если он выходит за рамки поля Инструменты подобные Google Translate могут не переводить значение данного атрибута при переводе всей страницы Названия полей лучше воспринимаются, когда расположены над соответствующим текстовым полем, а не внутри него Хороший код Источники Placeholders in Form Fields Are Harmful 11 reasons why placeholders are problematic by Adam Silver Don’t Use The Placeholder Attribute by Eric W. Bailey Form Design Quick Fix: Group Form Elements Effectively Using White Space placeholder - the piss-take label by Steve Faulkner Floated Labels Still Suck Примечание: чтобы облегчить чтение кода, большинство классов были удалены Плохой код Ошибки и что следует исправить В тегах <a> атрибут tabindex не нужен. HTML-гиперссылки получают фокус по умолчанию В элементах <a> атрибут type применяется, чтобы с помощью MIME-типа указать формат ресурса, на который ведёт ссылка. Например, type="image/svg+xml Использование атрибута role="link" в тегах <a> не имеет смысла, поскольку вы и так получаете необходимое поведение используя стандартный элемент гиперссылки. Пример: <a href=""> Отрицательное значение атрибута tabindex означает, что элемент  не получает фокус при навигации с помощью клавиатуры Нет необходимости в добавлении дополнительного <span> для получения фокуса. Элемент <a> и сам на это способен Элемент <i> представляет собой фрагмент текста, который нужно озвучить с другой интонацией. Если вам нужен курсив, используйте CSS-свойство font-style: italic Если в теге задаётся атрибут aria-hidden, бессмысленно добавлять ещё и role, потому элемент становится недоступным для скринрридеров. Старайтесь избегать разрастания DOM. Слишком большое количество DOM-узлов или вложенных элементов может плохо отразиться на производительности страницы Большое DOM-дерево ведёт к большому дереву доступности, которое также может плохо сказываться и на производительности вспомогательных технологий Вариант иконки 1: Хотя aria-hidden может быть полезным для скрытия содержимого от скринридеров (в нашем случае изображение иконки), порой вместо него полезно добавить aria-label, если это самое содержимое полезно пользователям любых устройств. Например, чтобы указать, что ссылка откроется в отдельном окне Вариант иконки 2: Иконка может быть удалена, потому что в исходном примере ссылка откроет страницу того же сайта в той же вкладке. Значок внешней ссылки полезен в ситуации, если ссылка ведёт на другой сайт Хороший код или Источники Using the link role Semantically identifying a font icon with role="img"