- Unify API: lib/api.ts uses /api/v1, inbox uses /api/inbox (rewrites) - Remove localhost refs: openapi, inbox page - Add rewrites: /api/inbox|tmc -> inbox-server, /api/v1 -> FastAPI - Add stub routes: knowledge/insights, recommendations, search, log-error - Transfer from PAPA: prompts (inspection, tmc), scripts, supabase, data/tmc-requests - Fix inbox-server: ORDER BY created_at, package.json - Remove redundant app/api/inbox/files route (rewrites handle it) - knowledge/ in gitignore (large PDFs) Co-authored-by: Cursor <cursoragent@cursor.com>
270 lines
8.4 KiB
Markdown
270 lines
8.4 KiB
Markdown
# Документация по доступности (a11y)
|
||
|
||
## Реализованные улучшения
|
||
|
||
### 1. ARIA атрибуты
|
||
|
||
**Файл:** `lib/accessibility/aria.ts`
|
||
|
||
Утилиты для добавления ARIA атрибутов:
|
||
|
||
- `getButtonAriaProps` - для кнопок
|
||
- `getFormFieldAriaProps` - для полей форм
|
||
- `getModalAriaProps` - для модальных окон
|
||
- `getNavigationAriaProps` - для навигации
|
||
- `getTableAriaProps` - для таблиц
|
||
|
||
**Пример:**
|
||
```typescript
|
||
import { getButtonAriaProps } from '@/lib/accessibility/aria';
|
||
|
||
const ariaProps = getButtonAriaProps({
|
||
label: 'Сохранить данные',
|
||
describedBy: 'save-hint',
|
||
disabled: false,
|
||
});
|
||
|
||
<button {...ariaProps}>Сохранить</button>
|
||
```
|
||
|
||
### 2. Поддержка screen readers
|
||
|
||
**Реализовано:**
|
||
- ✅ ARIA labels для всех интерактивных элементов
|
||
- ✅ ARIA descriptions для дополнительной информации
|
||
- ✅ ARIA live regions для динамического контента
|
||
- ✅ Скрытие декоративных элементов (`aria-hidden="true"`)
|
||
- ✅ Skip to main content link
|
||
- ✅ Семантический HTML (nav, main, section, article)
|
||
|
||
**Примеры:**
|
||
```tsx
|
||
// Скрытие декоративных иконок
|
||
<span aria-hidden="true">✈️</span>
|
||
|
||
// ARIA label для кнопки
|
||
<button aria-label="Закрыть модальное окно">×</button>
|
||
|
||
// ARIA live region для ошибок
|
||
<div role="alert" aria-live="polite">{error}</div>
|
||
```
|
||
|
||
### 3. Навигация с клавиатуры
|
||
|
||
**Файл:** `lib/accessibility/keyboard.ts`
|
||
|
||
**Реализовано:**
|
||
- ✅ Обработка Enter/Space для активации
|
||
- ✅ Обработка Escape для закрытия
|
||
- ✅ Навигация стрелками в списках
|
||
- ✅ Горячие клавиши (Ctrl+K для поиска и т.д.)
|
||
- ✅ Фокус-ловка для модальных окон
|
||
- ✅ Управление фокусом
|
||
|
||
**Компоненты:**
|
||
- `AccessibleButton` - кнопка с поддержкой клавиатуры
|
||
- `AccessibleModal` - модальное окно с фокус-ловкой
|
||
- `useKeyboardNavigation` - хук для горячих клавиш
|
||
|
||
**Пример:**
|
||
```typescript
|
||
import { useKeyboardNavigation } from '@/hooks/useKeyboardNavigation';
|
||
|
||
useKeyboardNavigation([
|
||
{
|
||
key: 'k',
|
||
ctrl: true,
|
||
handler: () => openSearch(),
|
||
},
|
||
]);
|
||
```
|
||
|
||
### 4. Достаточный контраст цветов
|
||
|
||
**Файл:** `lib/accessibility/colors.ts`
|
||
|
||
**Реализовано:**
|
||
- ✅ Функции проверки контраста (WCAG AA/AAA)
|
||
- ✅ Предопределенные цвета с проверенным контрастом
|
||
- ✅ Утилиты для вычисления контраста
|
||
|
||
**Цвета системы:**
|
||
- Primary: #1e3a5f / #ffffff - контраст ~8.5:1 (AAA)
|
||
- Error: #ffebee / #c62828 - контраст ~7.5:1 (AAA)
|
||
- Warning: #fff3e0 / #e65100 - контраст ~6.5:1 (AA)
|
||
- Success: #e8f5e9 / #2e7d32 - контраст ~6.8:1 (AA)
|
||
- Info: #e3f2fd / #1565c0 - контраст ~7.2:1 (AAA)
|
||
|
||
**Пример:**
|
||
```typescript
|
||
import { getWCAGLevel } from '@/lib/accessibility/colors';
|
||
|
||
const result = getWCAGLevel('#1e3a5f', '#ffffff');
|
||
// result.aa = true (соответствует WCAG AA)
|
||
// result.aaa = true (соответствует WCAG AAA)
|
||
```
|
||
|
||
### 5. Альтернативный текст для изображений
|
||
|
||
**Компонент:** `components/AccessibleImage.tsx`
|
||
|
||
**Особенности:**
|
||
- ✅ Обязательный alt текст
|
||
- ✅ Предупреждение, если alt не указан
|
||
- ✅ Поддержка декоративных изображений (alt="decorative")
|
||
- ✅ Использование Next.js Image для оптимизации
|
||
|
||
**Пример:**
|
||
```tsx
|
||
import AccessibleImage from '@/components/AccessibleImage';
|
||
|
||
<AccessibleImage
|
||
src="/logo.png"
|
||
alt="Логотип REFLY - система контроля лётной годности"
|
||
width={200}
|
||
height={50}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
## Компоненты доступности
|
||
|
||
### AccessibleButton
|
||
|
||
Кнопка с полной поддержкой доступности:
|
||
|
||
```tsx
|
||
<AccessibleButton
|
||
onClick={handleClick}
|
||
ariaLabel="Сохранить данные"
|
||
disabled={false}
|
||
>
|
||
Сохранить
|
||
</AccessibleButton>
|
||
```
|
||
|
||
### AccessibleInput
|
||
|
||
Поле ввода с ARIA атрибутами:
|
||
|
||
```tsx
|
||
<AccessibleInput
|
||
label="Email"
|
||
name="email"
|
||
type="email"
|
||
required
|
||
error="Неверный формат"
|
||
hint="Введите ваш email адрес"
|
||
/>
|
||
```
|
||
|
||
### AccessibleModal
|
||
|
||
Модальное окно с фокус-ловкой:
|
||
|
||
```tsx
|
||
<AccessibleModal
|
||
isOpen={isOpen}
|
||
onClose={handleClose}
|
||
title="Заголовок"
|
||
description="Описание модального окна"
|
||
>
|
||
Содержимое
|
||
</AccessibleModal>
|
||
```
|
||
|
||
---
|
||
|
||
## Глобальные стили доступности
|
||
|
||
**Файл:** `app/globals.css`
|
||
|
||
- ✅ Видимый фокус для всех интерактивных элементов
|
||
- ✅ Минимальный размер области клика (44x44px)
|
||
- ✅ Skip to main content link
|
||
- ✅ Поддержка `prefers-reduced-motion`
|
||
- ✅ Поддержка `prefers-contrast: high`
|
||
- ✅ Улучшенная читаемость текста
|
||
|
||
---
|
||
|
||
## Горячие клавиши
|
||
|
||
Реализованные горячие клавиши:
|
||
|
||
- `Ctrl+K` - Глобальный поиск (в разработке)
|
||
- `Escape` - Закрыть модальное окно
|
||
- `Tab` - Навигация по элементам
|
||
- `Enter/Space` - Активация элемента
|
||
- `Arrow Up/Down` - Навигация в списках
|
||
|
||
---
|
||
|
||
## Тестирование доступности
|
||
|
||
### Страница тестирования
|
||
|
||
Доступна по адресу `/accessibility-test` для проверки:
|
||
- Навигации с клавиатуры
|
||
- ARIA атрибутов
|
||
- Контраста цветов
|
||
- Работы модальных окон
|
||
|
||
### Инструменты
|
||
|
||
Рекомендуется использовать:
|
||
- **axe DevTools** - расширение для браузера
|
||
- **WAVE** - веб-инструмент оценки доступности
|
||
- **Lighthouse** - встроенный в Chrome
|
||
- **NVDA/JAWS** - screen readers для тестирования
|
||
|
||
### ESLint правила
|
||
|
||
Добавлены правила `eslint-plugin-jsx-a11y`:
|
||
- Проверка alt текста для изображений
|
||
- Проверка ARIA атрибутов
|
||
- Проверка валидности ссылок
|
||
- И другие правила доступности
|
||
|
||
---
|
||
|
||
## Best Practices
|
||
|
||
1. **Всегда добавляйте alt текст** для изображений
|
||
2. **Используйте семантический HTML** (nav, main, section)
|
||
3. **Добавляйте ARIA labels** для иконок без текста
|
||
4. **Обеспечьте видимый фокус** для всех интерактивных элементов
|
||
5. **Проверяйте контраст** перед использованием цветов
|
||
6. **Тестируйте с клавиатуры** - все должно работать без мыши
|
||
7. **Тестируйте со screen readers** - NVDA, JAWS, VoiceOver
|
||
|
||
---
|
||
|
||
## Соответствие стандартам
|
||
|
||
- ✅ **WCAG 2.1 Level AA** - базовый уровень
|
||
- ✅ **WCAG 2.1 Level AAA** - для критических элементов
|
||
- ✅ **Section 508** - готовность к соответствию
|
||
- ✅ **EN 301 549** - готовность к соответствию
|
||
|
||
---
|
||
|
||
## Чек-лист доступности
|
||
|
||
- [x] ARIA атрибуты добавлены
|
||
- [x] Screen readers поддерживаются
|
||
- [x] Навигация с клавиатуры работает
|
||
- [x] Контраст цветов соответствует WCAG
|
||
- [x] Альтернативный текст для изображений
|
||
- [x] Видимый фокус для всех элементов
|
||
- [x] Skip to main content link
|
||
- [x] Семантический HTML
|
||
- [x] Фокус-ловка в модальных окнах
|
||
- [x] Горячие клавиши
|
||
|
||
---
|
||
|
||
**Дата создания:** 2025-01-21
|
||
**Версия:** 1.0
|