diff --git a/apps/web/src/styles/theme.css b/apps/web/src/styles/theme.css index 4d6f48b..e25495f 100644 --- a/apps/web/src/styles/theme.css +++ b/apps/web/src/styles/theme.css @@ -6,7 +6,7 @@ --surface-control: light-dark(#f3f6f1, #0a1d1f); --text-primary: light-dark(#092226, #edf5ef); --text-secondary: light-dark(#526662, #b8c7c3); - --text-tertiary: light-dark(#647572, #93a7a2); + --text-tertiary: light-dark(#5b6b68, #93a7a2); --text-on-dark: #f5f8f3; --border-strong: light-dark(#cbd6ce, #3a5658); --border-subtle: light-dark(#d6dfd7, #294244); @@ -41,7 +41,7 @@ --source-official: light-dark(#815713, #e3b65b); --source-rf4db: light-dark(#245d78, #74bee3); --source-rf4stat: light-dark(#60499a, #b7a2ed); - --source-rf4map: light-dark(#2f704f, #79c79e); + --source-rf4map: light-dark(#276044, #79c79e); --source-rf4posts: light-dark(#984a33, #ee9a7e); --source-players: light-dark(#4b6419, #add066); --graphic-line: light-dark(#3f7779, #79b5b8); @@ -127,6 +127,11 @@ .timeline-chart { border-color: var(--border-strong); background: repeating-linear-gradient(to top, transparent 0, transparent 35px, var(--border-subtle) 36px); } .timeline-slot strong { color: var(--text-primary); } .timeline-slot small, .activity-legend { color: var(--text-tertiary); } + .spot-hero .eyebrow { color: var(--text-on-dark); } + .activity-legend { color: var(--text-secondary); } + .catch-list .tackle-label > span { color: var(--text-secondary); } + .catch-list span { color: var(--text-secondary); } + .catch-list .source-chip > span { color: var(--chip) !important; } .catalog-hero { border-color: var(--border-strong); } .catalog-hero p, .catalog-results aside a, .catalog-grid span { color: var(--text-secondary); } diff --git a/apps/web/tests/domain-accessibility.spec.ts b/apps/web/tests/domain-accessibility.spec.ts new file mode 100644 index 0000000..b08463b --- /dev/null +++ b/apps/web/tests/domain-accessibility.spec.ts @@ -0,0 +1,15 @@ +import AxeBuilder from "@axe-core/playwright"; +import { expect, test } from "@playwright/test"; + +for (const path of [ + "/waterbodies", + "/waterbodies/р-вьюнок", + "/spots/vyunok-321x654", + "/media", +]) { + test(`${path} has no serious accessibility violations`, async ({ page }) => { + await page.goto(path); + const results = await new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa", "wcag21aa"]).analyze(); + expect(results.violations.filter(item => ["critical", "serious"].includes(item.impact ?? ""))).toEqual([]); + }); +} diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 30f1c23..9cf093d 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -86,7 +86,7 @@ - [x] **D04 · Сохранение и SSR-согласование.** Выбор хранится год в allowlist-cookie `rf4-theme` с `SameSite=Lax` и `Secure` на HTTPS; Astro SSR выставляет `data-theme` до отрисовки, а системный режим оставляет выбор браузеру. Собранный Astro client script переключает тему без inline-кода, localStorage и ослабления CSP. - [ ] **D05 · Темизация компонентов и графики.** На семантические light/dark-токены переведены базовые и admin-поверхности, header, каталог, breadcrumb-леска, ссылки сущностей, таблицы, формы, moderation-карточки, provenance/quality/status badges, паспорта данных, pagination, empty/loading-состояния, радар, timeline, силуэты и глифы; добавлен forced-colors layer. In-app Chromium подтвердил переключение light/dark/system на медиатеке во всех четырёх ширинах; standalone axe проходит `9/9` маршрутов, forced-colors и print regression подтверждены. Полная screenshot-матрица `156` PNG захватывается launcher-ом без document overflow; остаётся вручную проверить тени/градиенты и выбрать сохраняемый reference-набор. Источники и статусы сохраняют текстовые подписи и не различаются только цветом. - [x] **D06 · Метаданные браузера и CSP.** Добавлены парные `theme-color` для системной light/dark схемы; явный cookie-выбор согласуется с SSR и мгновенно переключает активный meta-тег. PWA manifest использует устойчивый тёмный brand chrome и splash background. Production build сохраняет `inlinedScripts: []`; новые inline script/style/attributes не появились, ослабление CSP не потребовалось. -- [ ] **D07 · Визуальная и accessibility-приёмка.** In-app Chromium подтвердил отсутствие горизонтального scroll на публичных `/`, `/waterbodies`, `/records`, `/report`, `/status`, `/media`, `/rules`, waterbody detail `/waterbodies/р-вьюнок` и fish detail `/fish/pike`, а также admin `/admin`, `/admin/moderation`, `/admin/external-sources`, `/admin/media` в 320/390/768/1280 px; исправлены tablet header и media-grid overflow. Автоматизированный visual-matrix regression и `scripts/capture-visual-matrix.mjs` подтверждают 13 маршрутов × 4 ширины × 3 режима (`156` комбинаций): document overflow отсутствует, везде есть `main`/`h1`, SSR-тема совпадает; полная screenshot-матрица захвачена во внешнюю директорию. Для media и двух detail-страниц light/dark/system корректно переключаются, broken images — `0`; axe `9/9`, keyboard/focus, forced-colors и print-regression проходят. Остались ручная проверка shadows/gradients и выбор сохраняемого reference-набора. Для обеих тем обеспечить WCAG AA, отсутствие горизонтального scroll и CLS, reduced motion и переключение без потери введённых данных; сохранить эталонные screenshots и краткий отчёт. +- [ ] **D07 · Визуальная и accessibility-приёмка.** In-app Chromium подтвердил отсутствие горизонтального scroll на публичных `/`, `/waterbodies`, `/records`, `/report`, `/status`, `/media`, `/rules`, waterbody detail `/waterbodies/р-вьюнок` и fish detail `/fish/pike`, а также admin `/admin`, `/admin/moderation`, `/admin/external-sources`, `/admin/media` в 320/390/768/1280 px; исправлены tablet header и media-grid overflow. Автоматизированный visual-matrix regression и `scripts/capture-visual-matrix.mjs` подтверждают 13 маршрутов × 4 ширины × 3 режима (`156` комбинаций): document overflow отсутствует, везде есть `main`/`h1`, SSR-тема совпадает; полная screenshot-матрица захвачена во внешнюю директорию. Для media и двух detail-страниц light/dark/system корректно переключаются, broken images — `0`; axe `9/9`, отдельная domain accessibility-проверка добавлена для `/waterbodies`, waterbody detail, spot detail и `/media`, keyboard/focus, forced-colors и print-regression проходят. Остались ручная проверка shadows/gradients и выбор сохраняемого reference-набора. Для обеих тем обеспечить WCAG AA, отсутствие горизонтального scroll и CLS, reduced motion и переключение без потери введённых данных; сохранить эталонные screenshots и краткий отчёт. - [ ] **Q01 · Документы источников — реестр готов, нужны первичные подтверждения.** Создан единый production-gate с атрибуцией, общим лимитом 30 минут, хранением и процедурой отзыва для RF4DB, RF4-STAT, RF4MAP, RF4 Posts и официального RF4. До открытой публикации приложить устойчивые ссылки/копии первичных разрешений, контакты, даты и отдельно подтвердить право на изображения; пустое поле блокирует соответствующий источник. - [x] **Q02 · Управляемое удаление источника.** Изменение ранее опубликованной записи возвращает её в staging, сбрасывает сопоставление и переводит связанный улов в pending с новой версией решения. Результат проверки хранится как `available`, `missing`, `temporary_error` или `blocked`: только подтверждённый `missing` отзывает публикацию, временная ошибка и блокировка остаются диагностикой. Повторное появление требует ручного подтверждения. Withdrawn-записи исключены из публичной активности, а статус и время проверки доступны в admin provenance и журнале решений. Отдельного сетевого обхода нет: Q03 подключит эту реакцию к разрешённому плановому запросу.