# Pa11y Live Baseline — 2026-05-14 > First live-Vue baseline после Pa11y scope migration (Audit #3 sole P1 > `F-A11Y-PA11Y-SCOPE-01` closure). До этой даты `pa11y.config.json` указывал > на HTML-прототипы из `liderra_v8_handoff/concepts/*.html` (3 URL) — не на > работающее Vue-приложение. Historical handoff baseline сохранён в > [`pa11y-handoff.config.json`](../pa11y-handoff.config.json) и доступен через > `npm run a11y:handoff`. ## URLs scanned (live Vue, guest pages) | # | URL | Pa11y exit | Violations | Status | |---|---|---|---|---| | 1 | `/login` | 0 errors | 0 | ✅ clean | | 2 | `/register` | 0 errors | 0 | ✅ clean | | 3 | `/forgot` | 0 errors | 0 | ✅ clean | | 4 | `/2fa` | 0 errors* | 0 | ✅ clean (см. note) | | 5 | `/recovery` | 0 errors (после fix) | 0 | ✅ clean | | 6 | `/403` | 0 errors | 0 | ✅ clean | | 7 | `/500` | 0 errors | 0 | ✅ clean | **Итог:** 7/7 URLs passed. \* **Note on `/2fa`:** Vue Router редиректит guest без `pending_user_id` в сессии на `/login`. Pa11y видит `/login` (после редиректа), на котором уже 0 violations. Это ожидаемый guard-flow, не баг. ## Сделанные фиксы при первой baseline | URL | Violation (initial) | Fix | |---|---|---| | `/recovery` | `.v-alert--variant-tonal` warning alert content имел `color-contrast` 2.03:1 (требуется ≥4.5:1) на тексте «После закрытия страницы коды нельзя посмотреть снова». Vuetify tonal variant tints text тем же оттенком что фон. | `app/resources/js/views/auth/RecoveryCodesView.vue` — добавлен scoped `:deep(.v-alert__content)` override с `color: #0a0700` (Pa11y recommendation). После fix: 0 violations. | ## Ignored selectors (rationale) Глобально через `hideElements` в `pa11y.config.json`: | Selector | Why ignored | |---|---| | `.js-skip-a11y, [data-a11y-skip]` | Authoring hook: разработчик может вручную пометить элементы вне scope (например, third-party widget'ы). | | `.dev-index-badge`, `.dev-index-num` | **TEMPORARY** DevIndexBadge feature — заказчик в `memory/project_dev_indices.md`: «уберём в конечном релизе». Production tree-shake уже не включает её. Color-contrast 3.43:1 — известный issue, но без production impact. | | `.v-overlay-container` | Vuetify portal container. Рендерится напрямую в `` вне семантических landmarks. Пустой когда overlays/menus закрыты. Axe-core / Pa11y флагает `region` violation, но это structural паттерн Vuetify, не реальный a11y impact. | ## ~~Authenticated pages — out of scope для первой baseline~~ > **SUPERSEDED 2026-05-14** — см. секцию «Authenticated rescan» ниже. Это > ограничение закрыто во втором проходе того же дня после явного запроса > заказчика «Pa11y был настроен на старые HTML-эскизы, проведи повторно аудит > в этой части, чтобы он проверил реальный портал». ## Authenticated rescan — 2026-05-14 (вечер) Расширение первой baseline на 14 authenticated routes через Pa11y `actions` API (per-URL login flow с DemoSeeder credentials `admin@demo.local:password`). Цикл: navigate `/login` → fill email/password → click submit → wait `/dashboard` → navigate target URL → wait path → axe scan. ### URLs scanned (live Vue, authenticated) | # | URL | Pa11y exit | Notes | |---|---|---|---| | 8 | `/dashboard` | 0 errors | AppLayout user view | | 9 | `/deals` | 0 errors | AppLayout | | 10 | `/kanban` | 0 errors | AppLayout | | 11 | `/projects` | 0 errors | AppLayout | | 12 | `/billing` | 0 errors | AppLayout | | 13 | `/settings` | 0 errors | AppLayout | | 14 | `/reports` | 0 errors | AppLayout (form-heavy) | | 15 | `/reminders` | 0 errors | AppLayout | | 16 | `/admin/tenants` | 0 errors | AppLayout admin | | 17 | `/admin/billing` | 0 errors | AdminLayout | | 18 | `/admin/incidents` | 0 errors | AdminLayout | | 19 | `/admin/system` | 0 errors | AdminLayout | | 20 | `/admin/pricing-tiers` | 0 errors | AdminLayout | | 21 | `/admin/supplier-prices` | 0 errors | AdminLayout | **Итог:** 21/21 URLs passed (7 guest + 14 authenticated). ### Fixes (commit-level) при authenticated rescan | # | Pattern | URLs было | Fix file(s) | |---|---|---|---| | 1 | Mobile nav-icon `` без accessible name | 9 (AppLayout views) | `app/resources/js/components/layout/AppTopbar.vue` — `aria-label="Открыть меню навигации"` | | 2 | `.sep` точки-разделители contrast 2.92:1 на ivory | 3 (dashboard/billing/reports) | 8 файлов с scoped `.sep { color: #6b6356 }` (было `#92907b`); 5.33:1 | | 3 | Vuetify `.v-alert--variant-tonal .v-alert__content` contrast 4.18:1 | 2 (billing/admin-system) | `app/resources/css/app.css` — глобальный override на content text → `#0a0700` | | 4 | Vuetify `.v-chip--variant-tonal.bg-success/warning .v-chip__content` contrast 4.25:1 / 2.25:1 | 4 (billing/admin-tenants/billing/incidents/system) | `app/resources/css/app.css` — success → `#1f5e3a`, warning → `#6a4504` | | 5 | `.text-warning` utility (count badges «5» / «0» / «1») contrast 2.03:1 | 2 (admin/billing + admin/incidents) | `app/resources/css/app.css` — matched specificity `.v-theme--liderraForest .text-warning, .text-warning { color: #6a4504 !important }` (Vuetify selector 0,2,0 + !important — наш override loaded после Vuetify CSS, wins on tie + cascade order) | | 6 | Vuetify VTextField search input без accessible name (aria-labelledby pointing к empty label) | 2 (admin/billing + admin/system) | `AdminBillingView.vue` + `AdminSystemView.vue` — `` теперь имеет `label="Поиск"` prop, Vuetify рендерит floating label с правильным accessible name | ### Ignored selectors (added в этом проходе) | Selector | Why ignored | |---|---| | `select[hidden]` | Vuetify VSelect рендерит hidden native `