Appearance
02. Реестр подтверждённых находок
Сводка
| Приоритет | Open | Смысл |
|---|---|---|
P0 | 0 | Crash/data loss/system-wide block не обнаружены |
P1 | 4 | Два mobile collision и два modal/layering keyboard defect |
P2 | 9 | A11y, viewport, motion, stacking, theme и test coverage |
P3 | 1 пакет | Code hygiene и неподтверждённые риски для отдельной triage |
Release gate остаётся красным, пока открыт хотя бы один P1. P2 с нормативным a11y impact желательно закрыть до публичного релиза, но они не должны задерживать исправление геометрических и modal blockers.
P1 — release blockers
MAP-LAYOUT-001 — Top toolbar закрывает MapLibre controls на narrow viewport
- Статус:
Open / Confirmed - Приоритет:
P1 - Классификация: responsive interaction + focus/hit-area occlusion
- Среда:
320×844,360×844,375×844,390×844, частично412×844; Chromium; обе темы должны считаться затронутыми до retest - Затронутые состояния: как минимум
M01 IDLEиM03 DRAW; top zone общая для других режимов - Фактическое поведение: первый ряд toolbar начинается с
x=12и имеет ширину376.7px. Zoom In MapLibre находится справа сверху. Площадь их пересечения —1108.4 px²на ширинах320–390и707.4 px²на412. Element hit-test в центре Zoom возвращает toolbar child, а не Zoom. - Дополнительная проблема: на ширинах
320/360/375top row выходит за viewport;Colorзаканчивается наx=385.7. - Ожидание: primary toolbar и MapLibre navigation одновременно видимы, focus-visible и pointer-reachable; ни один обязательный control не лежит под другим.
- Вероятный источник:
FloatingToolbar.module.css, позиция MapLibre controls вMapCanvas, отсутствие общего layout contract для top-left/top-right зон. - Не принимать за корень: координата отдельной кнопки внутри горизонтально scrollable tools row. Page-level
scrollWidthравен viewport из-за intentional overflow containment. - Минимальное направление fix: зарезервировать явную top-right safe zone либо переместить MapLibre control group на mobile; уменьшить/перестроить первый ряд так, чтобы он помещался в
320pxбез скрытия обязательных controls. - Regression guard: geometry assertion для
320/360/375/390/412проверяет viewport containment иintersectionArea(toolbar, zoom) === 0; hit-test центра Zoom возвращает сам control/его child. - Exit criteria: PASS в portrait и
844×390landscape, dark/light, fine/coarse pointer; keyboard focus ring не скрыт; toolbar функции остаются доступны.
MAP-LAYOUT-002 — Changelog перекрывает действия mobile drawers
- Статус:
Fixed - Приоритет:
P1 - Классификация: responsive collision + hit target occlusion
- Среда:
320×568,390×844,412×915,844×390(Chromium Playwright) - Затронутые состояния: Loop drawer, Route Builder/Search drawer, POI drawer
- Решение: Кнопка Changelog скрывается на мобильных viewports при открытии любой drawer-панели (Search, Loop, POI). На десктопе поведение не изменилось.
- Verification: Успешно протестировано в E2E-тесте
tests/mobile-drawers-changelog.spec.ts. Проверено отсутствие пересечений/коллизий Changelog с элементами панелей и кликабельность основных действий (Close, Generate Loop, Reverse route direction) по центру их кнопок на всех целевых viewports. - Ограничения: Физические устройства iPhone/Android не проверялись (валидация проводилась в эмулированных viewports Playwright).
MAP-DIALOG-001 — Фокус выходит из SaveModal
- Статус:
Open / Confirmed - Приоритет:
P1 - Классификация: modal keyboard accessibility
- Среда: все viewports/themes; keyboard
- Что уже корректно: контейнер имеет
role="dialog",aria-modal="true", имяSave Track; initial focus попадает в#track-name. - Фактическое поведение: последовательный
Tabвыводит focus за пределы dialog после 14 переходов. Background toolbar/map controls снова становятся keyboard-actionable. - Ожидание: пока modal открыт,
Tab/Shift+Tabциклически перемещаются только по его tabbable elements; фон недоступен assistive/keyboard interaction. - Вероятный источник:
SaveModal.tsxне реализует containment/inert и не использует общий Dialog primitive. - Минимальное направление fix: общий focus-management primitive предпочтительнее локального keydown hack: containment, inert/aria-hidden strategy, initial focus, close, restore focus.
- Regression guard: прямой
Tabи обратныйShift+Tabcycle; assertion, что каждый activeElement находится внутри dialog; отдельно pointer test фона. - Exit criteria: focus не выходит, Escape закрывает только верхний dialog, после закрытия focus возвращается на Save opener.
MAP-DIALOG-002 — Один Escape закрывает dialog и нижний panel
- Статус:
Open / Confirmed - Приоритет:
P1 - Классификация: layer management + predictable keyboard recovery
- Среда: Route Builder открыт, поверх него SaveModal; keyboard
- Фактическое поведение: после одного Escape
SaveModalневидим и Route Builder также закрыт. Общий document handler вMapPage.tsxсбрасывает несколько open-state одновременно; ниже также остаётся дублирующий reset code. - Ожидание: Escape обрабатывается только topmost dismissible surface; нижний panel сохраняет state; focus возвращается к opener.
- Риск: пользователь теряет контекст редактирования, хотя данные маршрута не доказано потеряны. Поэтому это
P1, неP0. - Минимальное направление fix: единый topmost-layer contract с ранним
return; dialog должен остановить дальнейшее закрытие; затем убрать недостижимый/дублирующий reset. - Regression guard: открыть Route Builder → Save → Escape; dialog закрыт, builder открыт, focus на Save. Второй Escape может закрыть builder согласно его contract.
- Exit criteria: одна клавиша закрывает ровно один слой во всех комбинациях Save/Export/route/loop/poi, без двойной обработки React/document handlers.
P2 — значимые follow-ups
MAP-A11Y-001 — Gradient нарушает label-in-name
- Статус:
Open / Confirmed - Приоритет:
P2 - Стандарт: WCAG 2.5.3 Label in Name
- Evidence: visible text —
Gradient; accessible name —Color route by slope percentage. СловоGradientв accessible name отсутствует. - Fix contract: имя должно включать visible label, например
Gradient — color route by slope percentage, либо visible text становится самим именем, а дополнительное объяснение уходит в description. - Regression guard: accessible name regex содержит
Gradient; pressed-state остаётся корректным.
MAP-A11Y-002 — Недостаточный контраст Logout
- Статус:
Open / Confirmed - Приоритет:
P2 - Evidence: foreground
#939ab7, active background#585b70, ratio2.40:1; для normal text требуется4.5:1. - Scope caution: требуется проверить default/hover/focus/disabled и icon/non-text variant, а не заменить один цвет вслепую.
- Fix contract: semantic token или state-specific color с подтверждённым contrast во всех темах; визуально не путать destructive Logout с disabled action.
- Regression guard: programmatic contrast probe для dark/light и relevant states.
MAP-A11Y-003 — Touch-target policy применяется непоследовательно
- Статус:
Open / Confirmed with normative split - Приоритет:
P2 - Normative baseline: WCAG 2.2 AA SC 2.5.8 —
24×24или spacing exception.32×32само по себе не failure. Product coarse target —44×44; WCAG 2.5.5 — AAA. - Measured coarse sizes: Search/Loop
44×44; MapLibre Zoom34×34; Library/Garage/Changelog32×32; Logout24×24; top segments около26pxвысотой; swatch16×16. - Подтверждённый AA risk: соседние
16pxswatches с4pxgap дают около20pxмежду центрами, поэтому spacing exception для24pxне выполняется очевидным образом. - Product ergonomics debt: MapLibre/top-right/logout/top segments не достигают согласованной
44pxcoarse target. - Fix contract: сначала закрыть swatches как нормативный минимум; затем унифицировать coarse hit areas без обязательного увеличения видимой иконки.
- Regression guard: измерять hit box, не SVG; отдельно fine и coarse contexts.
MAP-A11Y-004 — Primary app tools поздно появляются в tab order
- Статус:
Open / Confirmed heuristic - Приоритет:
P2 - Evidence: первые focusable items: Map → Zoom In → Zoom Out → compass → attribution toggle → MapLibre link → Routing profile → Surface → Gradient → Color.
- Impact: пользователь проходит map/vendor controls до основных route-planning tools при каждом входе в экран.
- Не заявляется: keyboard trap или однозначное WCAG failure не обнаружены.
- Fix contract: определить осознанный landmark/focus order и skip strategy; не использовать положительные
tabindexкак быстрый fix. - Regression guard: snapshot первых focus stops и доступный способ быстро перейти к primary tools/map.
MAP-VIEWPORT-001 — 100vh и safe-area contract
- Статус:
Partial / emulator verified - Приоритет:
P2 - Затронутые файлы:
apps/web/index.html,apps/web/src/pages/map/MapPage.module.css, styles дляChangelog,BottomBar,WaypointSearchBar,LoopPanel,PoiPanel,FloatingToolbar - Решение:
- В
index.htmlдобавленоviewport-fit=coverдля корректного отображения safe areas. - В
.pageContainerопределены общие CSS-переменные для отступов safe area:--safe-top,--safe-right,--safe-bottom,--safe-leftна базеenv(safe-area-inset-*, 0px). - Эти переменные применены к Changelog, BottomBar, мобильным drawer-панелям и
.topRightControls(с учетом landscape notch). - Высота
.pageContainerпереведена наheight: 100vh; height: 100dvh;для предотвращения обрезания интерфейса.
- В
- Verification: Успешно протестировано в Playwright на различных эмулированных viewport-ах (включая landscape notch и изменение высоты при открытой виртуальной клавиатуре).
- Ограничения: Физические устройства iPhone/Android не проверялись (для полного закрытия issue требуется ручной Safari/iOS или Android real-device check).
MAP-MOTION-001 — Perpetual/entry animations не имеют полного reduced-motion path
- Статус:
Open / Confirmed static - Приоритет:
P2 - Evidence:
pulseSave, HUD/dropdown/overlay animations и другие transitions определены вне единогоprefers-reduced-motioncontract; один существующий guard не покрывает приложение. - Fix contract: отключать perpetual animation и существенно сокращать non-essential transitions при
reduce; сохранять status communication без движения. - Ограничение: глобальное правило
* { transition-duration: 0.01ms !important }может сломать functional transitions и должно оцениваться осторожно. - Regression guard: emulation
reducedMotion: reduce; computed animation name/duration/count для критических controls.
MAP-STACK-001 — Межкомпонентная z-index scale используется непоследовательно
- Статус:
Open / Static confirmed - Приоритет:
P2 - Evidence:
ExportModalиспользует--z-toast;ProjectStageHUD—1000; toolbar dropdown —1000;MapPopups—950/1100. Текущие tokens: map/UI/dropdown/modal/tooltip/toast scale с--z-toast: 9999. - Impact: возможны скрытые toast, modal, hint и drawer collisions; для каждого отдельного runtime conflict evidence пока нет.
- Fix contract: сначала составить ownership map overlay layers, затем перенести только cross-component layers на semantic tokens. Internal
z-index: 1/2/10не запрещать автоматически. - Regression guard: pairwise overlay tests: modal+toast, drawer+popup, dropdown+toolbar, HUD+modal.
MAP-THEME-001 — Theme colors главной карты обходят semantic tokens
- Статус:
Open / Confirmed - Приоритет:
P2 - Evidence: top-right/Changelog buttons в
MapPage.tsxсодержат raw#313244,#45475a,#cdd6f4, hover/accent literals. Raw scanner по всемуapps/web/srcдал455совпадений с известными token values вне token source и226неизвестных literals. - Важно: scanner — triage, не число дефектов. Data ramps, map style colors, brand colors, SVG assets и комментарии требуют allowlist/semantic review.
- Fix contract: начать с
MapPage, где theme drift доказан; затем component-by-component. Не выполнять механический repo-wide replace. - Regression guard: dark/light computed styles и screenshot assertion конкретных components; lint/allowlist обсуждается после cleanup.
MAP-TEST-001 — Зелёные suites не ловят подтверждённые regressions
- Статус:
Open / Confirmed - Приоритет:
P2 - Evidence:
map-layout,responsive-map-controls,toolbar-fsmпрошли3/3, несмотря на MAP-LAYOUT-001/002. Screenshot suites также прошли, но только записывают PNG и не сравнивают их с baseline. - Impact: CI может оставаться зелёным при недоступных controls.
- Fix contract: geometry/hit-test assertions для cross-surface collisions, modal focus assertions, затем ограниченные
toHaveScreenshotbaselines для стабильных fixture states. - Regression guard: сам новый тест должен быть красным на baseline и зелёным только после соответствующего fix.
P3 — triage/code hygiene пакет
MAP-HYGIENE-001 — Низкоприоритетные cleanup и неподтверждённые риски
- Статус:
Open / Mixed - Приоритет:
P3 - Состав: дублирующий код после Escape branch; длинный development comment в
trackModeOutline;BottomBar bottom: 36px;767/768и content-specific breakpoints; local raw radius/z-index; overlayrgba; slope legend24×24; route hint/reset targets, для которых ещё нет полного measurement. - Правило: каждый пункт сначала либо связывается с воспроизводимым дефектом, либо остаётся cleanup. Не превращать пакет в массовую token rewrite.
- Exit criteria: удалён dead code; для breakpoints/radii/internal stacking записана convention; неподтверждённые UX risks измерены и либо повышены в отдельные issues, либо закрыты как intentional.
Отдельно опровергнутые находки
Следующие пункты не должны появляться как open issues без нового evidence:
- «SaveModal не имеет dialog semantics»;
- «Route color fieldset не имеет label/legend»;
- «Changelog button не имеет accessible name»;
- «все
32×32targets нарушают WCAG 2.2 AA»; - «все hardcoded
border-radius: 50%нужно заменить token»; - «visual regression suite уже сравнивает screenshots»;
- «focus trap в modal уже работает»;
- «весь contrast audit PASS».