Всё, что можно автоматизировать, должно быть автоматизировано. Даже aria-label

Я написала свой ESLint-плагин для доступности. Вот как и зачем.
Я люблю автоматизацию: если что-то можно доверить инструменту, это стоит делать. Особенно то, что повторяется из проекта в проект: aria-label, alt, tabIndex.
Линтер — это как фоновый напарник: один раз настроил — и он работает. Не устает, не отвлекается, не забывает. А в контексте доступности, где многое завязано на деталях, это особенно важно.
Почему мне захотелось написать свой плагин?
На самом деле, всё началось не с бага. Скорее — с исследовательского интереса: могу ли я в одиночку сделать полезный open source-проект, с тестами, публикацией и реальной практической ценностью?
Я изучила существующие решения и увидела, что eslint-plugin-jsx-a11y, несмотря на свою силу, оставляет важные вещи вне поля зрения:
Он хорошо работает с нативными HTML-элементами, но теряется на кастомных компонентах.
Он требует alt у , но не различает контентные и декоративные изображения. Так легко забыть добавить role=«presentation» и не заметить.
Он не понимает, где важна локализация. Для него aria-label=«Close» и aria-label={t ('close')} — одинаковы. А на многоязычном сайте это большая разница: скринридер может читать страницу на русском, а кнопку — на английском.
Он почти не проверяет поведенческую доступность. Например, div с role=«button» без tabIndex и onKeyDown визуально выглядит интерактивно, но не работает с клавиатурой: ни фокус, ни нажатие Enter/Space не срабатывают.
Такие вещи легко пропустить: они не видны на глаз и часто проходят код-ревью. Мне хотелось создать линтер, который будет ловить именно такие «невидимые» проблемы.
Как устроен мой плагин
Технологии: TypeScript, ESLint RuleTester, Jest.
Каждое правило — это обход абстрактного синтаксического дерева (AST). Он позволяет проанализировать JSX-разметку: какие компоненты используются, какие у них атрибуты и как они связаны.
Структура проекта:
src/rules/ — каждое правило в отдельном файле
tests/ — автотесты на правильные и ошибочные примеры
src/index.ts — единая точка входа для подключения всех правил
Я сразу писала как npm-пакет с поддержкой ESLint v9, потому что:
с 9-й версии используется новый формат конфигурации (eslint.config.js);
плагин перешёл на ES Modules;
это избавит пользователей от лишней миграции в будущем.
Описание правил
require-aria-label
Что проверяет:
Находит
//❌ Плохо: не озвучивается скринридером
//✅Хорошо:
no-missing-aria-labelledby-target
Что проверяет:
Если в aria-labelledby=«some-id» указана ссылка, то должен существовать элемент с id=«some-id».
Без этого атрибут не работает, а скринридер озвучит «unlabeled».
//❌ Плохо:
//✅ Хорошо:
Dialog
interactive-supports-focus-and-keys
Что проверяет:
Если элемент с role=«button» — он должен быть фокусируемым (tabIndex ≥ 0) и реагировать на клавиатуру (onKeyDown или onKeyPress).
Это особенно важно для div, span и кастомных интерактивов.
//❌ Плохо:
//✅ Хорошо:
no-hardcoded-accessibility-text
Что проверяет:
Если используется aria-label, alt или title, и внутри строка вроде «Close» — будет предупреждение.
Ожидается вызов i18n-функции (t ('close')), чтобы не нарушать локализацию.
// ❌ Плохо:
// ✅ Хорошо:
img-requires-alt-or-role
Что проверяет:
Каждое должно иметь или alt=»…», или быть помечено как декоративное (role=«presentation»).
Это базовый принцип доступности, особенно важен для логотипов, иконок, аватаров.
// ❌ Плохо:
// ✅ Хорошо:


Кому может быть полезен этот плагин:
Командам с кастомной дизайн-системой (
Проектам с мультиязычным интерфейсом
Тем, кто внедряет доступность поэтапно и хочет начать с базовых проверок
Разработчикам, которые подключают линтер к CI или используют pre-push хуки
Всем, кому важно, чтобы интерфейсы были не только красивыми, но и доступными
Как использовать?
npm install eslint-plugin-a11y-extended --save-dev
И подключить в eslint.config.js:
import plugin from 'eslint-plugin-a11y-extended';
export default [
{
plugins: { 'a11y-extended': plugin },
rules: {
'a11y-extended/require-aria-label': 'warn',
// и другие
},
},
];
Работа над плагином
Сейчас в плагине 5 правил. Каждое из них закрывает важный, но часто пропускаемый случай: от aria-label и alt, до локализации и поведения интерактивных элементов.
Плагин уже сейчас помогает находить ошибки, которые раньше могли попасть в продакшен незаметно. И в этом его главная задача — помочь, а не заменить разработчика.
Зачем все это?
Я люблю автоматизацию. Если можно один раз настроить инструмент, чтобы он отслеживал важные детали — я за.
Мне хотелось попробовать: можно ли в одиночку сделать что-то полезное? Можно.
И теперь у меня есть плагин, который проверяет именно те вещи, что раньше легко проходили мимо — aria-label, alt, tabIndex, локализацию и поведение.
Если вам это близко — пробуйте. А если захочется предложить идею или новое правило — пишите, буду рада.
GitHub — mariaparfenyuk/eslint-plugin-a11y-extended
npm — eslint-plugin-a11y-extended