Доступность компонентов и ручная проверка
Библиотека получила несколько состояний и две темы. Теперь определим, как рассматривать доступность этих компонентов, сохраняя честный статус: проверки не запускаются. Подготовим addon-a11y в ручном режиме и обсудим конкретные клавиатурные маршруты. Цель урока — понять предмет анализа и возможности инструментов, а не получить выдуманную отметку «всё доступно».
Доступность включает смысл элементов, управление, восприятие содержания и понятность ошибок. Разные механизмы помогают разным свойствам. Нативная кнопка выражает действие, видимая подпись связывает поле с названием, dialog поддерживает модальность, текст Notice объясняет результат. Даже если эти решения разумны, их совместная работа в конкретном браузере и сценарии требует отдельного подтверждения. Проверять нужно обещание интерфейса, а не только наличие знакомых атрибутов.
Ручной режим инструмента
В main добавлен @storybook/addon-a11y выбранной версии. В preview задан initialGlobals.a11y.manual = true, а поведение автоматических проверок отключено через соответствующий параметр. Это подготовка интерфейса для будущего самостоятельного анализа. Автор не устанавливал addon, не открывал его панель и не получал отчёт. Наличие зависимости и конфигурации не следует описывать как выполненную проверку.
Полный файл .storybook/main.ts для этого состояния:
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(ts|tsx)'],
addons: ['@storybook/addon-docs', '@storybook/addon-a11y'],
framework: { name: '@storybook/react-vite', options: {} },
};
export default config;
Полный файл .storybook/preview.tsx для этого состояния:
import type { Preview } from '@storybook/react-vite';
import '../src/styles.css';
const preview: Preview = {
parameters: { a11y: { test: 'off' } },
initialGlobals: { theme: 'light', a11y: { manual: true } },
globalTypes: { theme: { description: 'Тема компонентов', toolbar: { title: 'Тема', icon: 'circlehollow', items: [{value:'light',title:'Светлая'},{value:'dark',title:'Тёмная'}] } } },
decorators: [(Story,context) => <div className="nabor-theme" data-theme={context.globals.theme === 'dark' ? 'dark' : 'light'} style={{ padding:'1rem',minHeight:'100vh' }}><Story /></div>],
};
export default preview;
Автоматический инструмент может обнаружить часть проблем, например недостаточные доступные имена или определённые нарушения отношений DOM. Он не знает, понятен ли пользователю текст «Попробуйте ещё раз» после операции с неизвестным результатом. Он также не заменяет наблюдение реального пути фокуса при открытии и закрытии окна. Поэтому будущий отчёт рассматривается как набор конкретных находок, а не сертификат правильности всей библиотечной архитектуры.
Ручной режим удобен для учебного ограничения: анализ запускается отдельным сознательным действием читателя, когда для этого разрешена работа. В текущей авторской задаче никаких таких действий нет. Если позднее команда добавит автоматизацию, ей потребуется определить среду, критерии и реакцию на находки. Просто изменить флаг на строгий режим не означает, что все компоненты теперь проверены в нужных сценариях и с нужным содержимым.
Клавиатурный маршрут кнопки и поля
В обычной истории кнопка должна быть достижима последовательным клавиатурным перемещением и иметь различимое состояние фокуса. При disabled поведение меняется по нативному контракту. Нельзя сравнивать эти состояния только по цвету: важны доступность действия и понятное объяснение причины. Если отключённая кнопка единственная сообщает, что форма не готова, пользователь может не получить нужной инструкции.
Для TextField рассматривают видимую подпись, переход по ней, ввод, чтение подсказки и связи сообщения ошибки. aria-describedby указывает существующие идентификаторы, а не любую строку текста. Два поля с одинаковым внешним id нарушат смысл связи даже при наличии правильных атрибутов. Поэтому будущий ручной анализ должен включать композицию нескольких экземпляров, если такая форма является реальным сценарием библиотеки.
При изменении масштаба текста поле и подпись должны оставаться понятными. Наличие rem в отступах не гарантирует, что внешний контейнер не обрежет содержание. Длинные названия организации и сообщения об ограничении помогают исследовать этот случай. Не выбирайте исключительно короткий текст ради аккуратного скриншота. Содержимое является частью интерфейса и способно обнаружить проблемы, которые не видны на одном удобном примере.
Маршрут модального окна
Для dialog последовательность начинается с кнопки открытия. Затем рассматривают начальный фокус, перемещение внутри, действие Escape и возврат к исходному элементу. Сама функция showModal даёт нативную основу, но приложение может удалить источник или перестроить дерево. Поэтому составной сценарий важнее isolated-проверки одного DOM-тега. В нашем примере кнопка открытия сохраняется весь цикл, что делает ожидаемую цель возврата ясной.
Если содержимое окна высокое, пользователь должен иметь доступ к его элементам без потери контекста. Это касается не только ширины, но и ограниченного вертикального пространства. CSS нашего короткого примера не претендует на решение всех длинных форм. При расширении содержимого надо выбрать политику прокрутки и положение действий, а затем отдельно подтвердить её. Библиотечный контракт должен назвать такие ограничения, вместо универсального обещания любых детей ReactNode.
Сообщения и время
Notice использует status или alert, но смысл объявления зависит от динамики. Сообщение о сохранении после изменения поля может стать устаревшим. Ошибка, исчезнувшая через слишком короткое время, может остаться непрочитанной. Ни роль, ни цвет не исправят неправильную модель состояния. Поэтому доступность связана с тем, когда приложение создаёт сообщение и как поддерживает его актуальность. Это будет видно в составной форме следующего урока.
Не добавляйте aria-label поверх существующего видимого текста без причины. Альтернативное имя, существенно отличающееся от подписи, способно усложнить управление речью и понимание интерфейса. Нативная разметка уже предоставляет многие нужные отношения. Дополнительные атрибуты нужны, когда они выражают отсутствующий смысл, а не как украшение исходника. Начинайте с вопроса о конкретном пользователе и конкретном механизме, которому требуется информация.
Запись наблюдения без ложной гарантии
В ACCESSIBILITY.md сохранён перечень будущих предметов анализа и статус неисполнения. После самостоятельной работы полезно записывать браузер, состояние истории, содержимое, способ управления и полученный результат. Фраза «кнопка прошла» слишком общая: какая кнопка, в какой теме и с какой подписью? Конкретная запись помогает повторить наблюдение и понять практическую границу. При изменении компонента сравнивают тот же договор, а не случайную новую картинку.
К концу урока библиотека имеет подготовленный ручной инструмент и понятную карту пользовательских маршрутов. Ни автоматические проверки, ни тесты, ни браузерные сценарии автором не выполнялись. Следующий шаг превратит технические решения в документацию API и составной пример формы, чтобы потребитель понимал не только какие параметры существуют, но и почему их надо связывать определённым образом.
Конкретность будущего отчёта
Найдённая проблема должна описывать состояние и последствие. «В dark-теме поле ошибки имеет нечитаемый текст на поверхности» помогает выбрать исправление. «Надо улучшить доступность» не объясняет, какой договор нарушен. При записи также называйте способ воспроизведения и границу результата. Один сценарий dialog не подтверждает все возможные формы с длинным содержимым и динамическим удалением элементов.
Если инструмент не сообщил находок, это тоже результат с ограниченной областью. Он означает отсутствие выявленных нарушений по выбранным правилам и DOM в этот момент. Нельзя автоматически расширять его до полного соответствия всем требованиям. Пользовательские маршруты, понятность текста и время объявления продолжают требовать самостоятельного анализа. Именно поэтому в текущем учебном плане инструмент подготовлен рядом с объяснением, а не вместо объяснения.
Оглавление серии, общая подборка и полный архив исходников. Для этого урока выберите lesson-10.
Первичные источники: Storybook CSF, React Vite integration, Аргументы историй, Темы и globals, Декораторы, Accessibility addon, Автоматическая документация, React useId, HTML dialog.
Редакционный статус: локальный черновик. Учебные команды, приложение и проверки не запускались; описанные результаты ожидаемые. Профиль версий закреплён, разрешённый lockfile не создавался.