# BRD: Enduro Trails — Фаза 5 «Редизайн» **Версия:** 1.0 **Дата:** 2026-05-04 **Автор:** Стрим 🌊 **Статус:** На согласовании --- ## 1. Контекст и проблема Текущий UI — функциональный прототип, не готовый к реальному использованию. Основные боли: - **Не мобильный** — панели перекрывают карту, мелкие кнопки, тяжело нажимать в перчатках - **Нет стиля** — белый фон, emoji-иконки, выглядит как dev-прототип - **Всё поверх карты** — карта не видна когда работают панели - **Нет режима вождения** — на мотоцикле невозможно пользоваться ## 2. Цель Создать мобильный UI уровня onX Offroad / Locus Map — тёмный, эндуро-стильный, thumb-friendly. Карта — главный герой. UI — минималистичный HUD. ## 3. Дизайн-система ### 3.1 Цветовая палитра — две темы Тема переключается кнопкой ☀️/🌙 в search bar. **Три режима:** | Режим | Описание | |-------|----------| | **Авто** (по умолчанию) | Тема определяется по реальному восходу/закату солнца для текущей геолокации. Если браузер отдал геопозицию — используем её; иначе — запасной город (Москва, 55.75°N). День = светлая, ночь = тёмная. Переключение происходит без перезагрузки. | | **Светлая** | Принудительно светлая тема, независимо от времени суток. | | **Тёмная** | Принудительно тёмная тема. | **Реализация авто-режима:** - Используется SunCalc (MIT, ~3KB) через CDN: `https://unpkg.com/suncalc@latest` - `SunCalc.getTimes(date, lat, lng)` → `sunrise`, `sunset` - Текущее время между `sunrise` и `sunset` → светлая тема; иначе — тёмная - При изменении геолокации — пересчёт восхода/заката - Проверка раз в минуту (на случай, если сессия длительная) **UI переключателя:** тап на ☀️/🌙 циклически переключает: Авто → Светлая → Тёмная → Авто. Текущий режим показан маленькой подписью под иконкой (или tooltip на десктопе): «Авто», «День», «Ночь». В авто-режиме иконка динамическая: ☀️ если сейчас день, 🌙 если ночь. **Тёмная (ночная езда):** ``` --bg: #0D1117 --surface: #161B22 --surface2: #21262D --border: #30363D --text: #E6EDF3 --text2: #8B949E --accent: #FF6B00 --gold: #FFD700 --red: #FF3B1F --success: #2EA043 ``` **Светлая (дневная езда):** ``` --bg: #F5F5F0 (бежевый, не слепит на солнце) --surface: #FFFFFF --surface2: #F0F0EA --border: #D0CFC8 --text: #1A1A1A --text2: #6B6B6B --accent: #E55A00 (оранжевый чуть темнее — виден на белом) --gold: #C89B00 --red: #CC2200 --success: #1A7A30 ``` Реализация через CSS custom properties на `:root` + класс `body.theme-dark` / `body.theme-light`. Стиль карты MapLibre меняется соответственно (тёмный/светлый style.json — уже существуют). ### 3.2 Типографика ``` Font: system-ui, -apple-system, 'SF Pro Display', 'Segoe UI', sans-serif Заголовок: 16px, 700, #E6EDF3 Подзаголовок: 13px, 600, #8B949E, uppercase, letter-spacing: 0.08em Текст: 14px, 400, #E6EDF3 Мелкий: 12px, 400, #8B949E Цифры: font-variant-numeric: tabular-nums ``` ### 3.3 Иконки **НЕ emoji!** SVG-иконки через inline SVG или icon font. Источник: **Lucide Icons** (MIT, 24px stroke, line-cap: round). Ключевые иконки: - 🗺 Route: `map` (lucide) - 📍 Recon: `search` или `radar` - 🔗 Link: `git-merge` - 🎨 Scenic: `sparkles` - 📏 Ruler: `ruler` - 📌 Marker: `map-pin` - 🎯 Locate: `navigation` - 🧭 Compass: `compass` - ⬇ Download/GPX: `download` - ✕ Close: `x` - ➕ Add: `plus` - 🔥 Difficulty: `flame` - 💧 Water: `droplets` - 👁 View: `eye` ### 3.4 Компоненты #### Кнопка карты (FAB style) ```css .map-btn { width: 48px; height: 48px; background: #161B22; border: 1px solid #30363D; border-radius: 12px; color: #E6EDF3; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 16px rgba(0,0,0,0.5); transition: all 0.15s; -webkit-tap-highlight-color: transparent; } .map-btn:active { background: #21262D; transform: scale(0.94); } .map-btn.active { background: #FF6B00; color: #fff; border-color: #FF6B00; } ``` #### Bottom Sheet (панели) ```css .bottom-sheet { position: fixed; bottom: 0; left: 0; right: 0; background: #161B22; border-radius: 20px 20px 0 0; border-top: 1px solid #30363D; padding: 0 16px 32px; /* 32px = safe area снизу */ z-index: 100; max-height: 75vh; overflow-y: auto; transform: translateY(100%); transition: transform 0.3s cubic-bezier(0.32, 0, 0.15, 1); } .bottom-sheet.open { transform: translateY(0); } /* Drag handle */ .sheet-handle { width: 36px; height: 4px; background: #30363D; border-radius: 2px; margin: 12px auto 16px; } ``` #### Карточка маршрута ```css .route-card { background: #21262D; border: 1.5px solid #30363D; border-radius: 12px; padding: 12px 14px; margin-bottom: 8px; cursor: pointer; transition: border-color 0.15s; } .route-card.active { border-color: #FF6B00; box-shadow: 0 0 0 1px #FF6B00; } ``` #### Stat pill (статистика) ```css .stat-pill { display: inline-flex; align-items: center; gap: 4px; background: #0D1117; border: 1px solid #30363D; border-radius: 20px; padding: 3px 10px; font-size: 12px; font-weight: 600; color: #E6EDF3; } .stat-pill.dirt { border-color: #FFD700; color: #FFD700; } .stat-pill.asphalt { border-color: #8B949E; color: #8B949E; } ``` ## 4. Layout — Mobile First ### Основной экран ``` ┌─────────────────────────────────┐ │ [🔍 Поиск...........] [☰] │ ← search bar, 44px, top: safe │ │ │ │ │ КАРТА │ ← 100% экрана │ │ │ [🧭] │ │ [🎯] │ ← кнопки справа, 48×48 │ ────────────────────────────────│ │ [🗺][🔗][🎨][📍][📏][📌] │ ← bottom toolbar, 64px └─────────────────────────────────┘ ``` **Bottom toolbar**: 6 режимов, иконки 24px, tap target 48px minimum. Активный режим — оранжевый фон + label появляется под иконкой. ### При активном режиме (пример: Маршрут) ``` ┌─────────────────────────────────┐ │ [🔍 Поиск...] [☰] │ │ │ │ КАРТА │ │ │ │ [🧭] │ │ [🎯] │ ├─────────────────────────────────┤ │ ▬▬▬ drag handle ▬▬▬ │ ← Bottom Sheet │ 🗺 МАРШРУТ [✕] │ │ ─────────────────────────────── │ │ [A: Хоруговино ] [B: ...] │ ← точки (горизонтально) │ [+ Точка] [Сбросить] [GPX⬇] │ │ │ │ Строю маршрут... │ │ ┌───────────────────────────┐ │ │ │● Вариант 1 1013 км 14ч │ │ │ │ ████████░░ 97% грунт │ │ │ └───────────────────────────┘ │ └─────────────────────────────────┘ ``` ## 5. Компоненты — детальный дизайн ### 5.1 Toolbar (нижняя панель) ```html ``` ```css #toolbar { position: fixed; bottom: 0; left: 0; right: 0; height: 72px; background: #161B22; border-top: 1px solid #30363D; display: flex; align-items: center; justify-content: space-around; padding: 0 4px; padding-bottom: env(safe-area-inset-bottom, 0px); z-index: 200; } .tb-btn { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; height: 56px; border: none; background: none; color: #8B949E; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; border-radius: 10px; cursor: pointer; transition: color 0.15s, background 0.15s; -webkit-tap-highlight-color: transparent; } .tb-btn svg { width: 22px; height: 22px; stroke-width: 1.8; } .tb-btn.active { color: #FF6B00; } .tb-btn.active svg { stroke: #FF6B00; } ``` ### 5.2 Bottom Sheet — Маршрут Заменяет существующий `#route-panel`. **Секция точек (горизонтальный scroll если много):** ```html
Хоруговино
Корак-Чурачки
``` **Карточка маршрута:** ```html
Основной 1013 км 14ч 22м
🟡 97% грунт ⬜ 3% асфальт
``` ### 5.3 Bottom Sheet — Разведка При нажатии открывается sheet с радиус-контролом. После клика на карте — обновляется. ```html

Разведка

Тапни точку на карте — узнаешь сколько грунтовок рядом

``` ### 5.4 Bottom Sheet — Красивый маршрут ```html

Красивый маршрут

Тапни точку старта на карте

``` ### 5.5 Кнопки карты (правый столбец) ```html
``` ### 5.6 Search Bar (верхняя строка) ```html ``` ```css #search-bar { position: fixed; top: env(safe-area-inset-top, 12px); left: 12px; right: 12px; height: 48px; background: #161B22; border: 1px solid #30363D; border-radius: 14px; display: flex; align-items: center; padding: 0 14px; gap: 10px; z-index: 200; box-shadow: 0 4px 20px rgba(0,0,0,0.4); } #search-input { flex: 1; background: none; border: none; color: #E6EDF3; font-size: 15px; outline: none; } #search-input::placeholder { color: #484F58; } ``` ## 6. Анимации и микроинтерактивность - **Bottom sheet**: `transform: translateY` + `cubic-bezier(0.32, 0, 0.15, 1)`, 280ms - **Кнопки**: `transform: scale(0.94)` при tap (active state), 100ms - **Карточки маршрутов**: `border-color` transition 150ms - **Loading**: пульсирующий skeleton (opacity animation) вместо spinner - **Маркеры на карте**: появление через `transform: scale(0) → scale(1)`, 200ms ## 7. Адаптив - **Мобила (< 768px)**: bottom sheet, toolbar снизу - **Десктоп (≥ 768px)**: боковая панель 320px слева, кнопки сохраняются - **Landscape мобила**: toolbar справа вертикально, sheet с max-height: 80vh ## 8. Тест-кейсы ### P0 — Критично (должно работать идеально) | ID | Сценарий | Устройство | Шаги | Ожидаемый результат | |----|----------|-----------|------|---------------------| | T01 | Открытие приложения | iPhone SE (375px) | Открыть URL | Карта 100% экрана, toolbar снизу виден, search bar сверху | | T02 | Нажать режим Маршрут | iPhone | Тап на иконку 🗺 | Bottom sheet выезжает снизу, иконка оранжевая | | T03 | Установить A и B точки | iPhone | Тап A, тап B | Маркеры на карте, карта НЕ перекрыта | | T04 | Маршрут построен | iPhone | После T03 | Карточки маршрутов в sheet, карта видна | | T05 | Тап по карточке | iPhone | Тап на вариант 2 | Маршрут 2 активен (оранжевый), карточка выделена | | T06 | Закрыть sheet | iPhone | Тап ✕ или свайп вниз | Sheet уходит, toolbar возвращается, карта чистая | | T07 | Разведка — тап на карту | iPhone | Включить Разведку, тапнуть | Круг на карте, sheet со статистикой | | T08 | Переключение радиуса | iPhone | В sheet Разведки нажать 50км | Круг обновился, статистика пересчиталась | | T09 | Красивый маршрут | iPhone | Включить, тапнуть, нажать «Построить» | Кольцевой маршрут на карте, карточки в sheet | | T10 | Связка | iPhone | Включить, тапнуть 2 точки | Маршрут между точками, карточки | | T11 | Поиск места | iPhone | Тапнуть search bar, ввести «Тверь» | Результаты поиска, тап → карта летит | | T12 | GPX скачать | iPhone | Построить маршрут → Download | Файл скачался | ### P1 — Важно | ID | Сценарий | Устройство | Шаги | Ожидаемый результат | |----|----------|-----------|------|---------------------| | T13 | Тема — авто по умолчанию | Любое | Открыть (днём) | Фон светлый (бежевый #F5F5F0), иконка ☀️, подпись «Авто» | | T13a | Тема — авто ночью | Любое | Открыть (после заката) | Фон тёмный #0D1117, иконка 🌙, подпись «Авто» | | T13b | Тема — ручной цикл | Любое | Тап ☀️/🌙 3 раза | Авто → Светлая → Тёмная → Авто, при каждом тапе тема и подпись меняются | | T13c | Тема — ручная не зависит от времени | Любое | Переключить на «Светлая» ночью | Фон бежевый, подпись «День», иконка ☀️ | | T13d | Тема — авто пересчитывает при геолокации | Любое | В авто-режиме, разрешить геолокацию | Восход/закат пересчитаны по реальным координатам, тема соответствует | | T14 | Толстые пальцы в перчатках | iPhone | Нажимать кнопки | Все кнопки min 48×48px, не промахиваешься | | T15 | Landscape поворот | iPhone | Повернуть телефон | UI перестроился, карта видна | | T16 | Десктоп Chrome | MacBook | Открыть | Боковая панель 320px, кнопки слева | | T17 | Свайп вниз для закрытия | iPhone | Свайп вниз по handle | Sheet закрывается | | T18 | Два режима не активны одновременно | iPhone | Открыть Маршрут, потом Разведку | Маршрут закрылся, Разведка открылась | | T19 | Геолокация | iPhone | Нажать 🎯 | Запрос разрешения, потом маркер на карте | | T20 | Компас | iPhone | Нажать 🧭 | Карта вращается, кнопка активна | | T21 | Метка — добавить | iPhone | Включить 📌, тапнуть | Диалог выбора типа метки (popup) | | T22 | Метка — иконка на карте | iPhone | После T21 | Метка видна, тап открывает popup с опциями | | T23 | Линейка | iPhone | Включить 📏, тапнуть несколько точек | Линия + дистанция | | T24 | Анимация sheet | iPhone | Открыть/закрыть | Плавная, 280ms, без дёрганий | | T25 | Skeleton loading | iPhone | Построить длинный маршрут | Skeleton в карточках пока грузится | | T26 | Ошибка маршрута | iPhone | Поставить точки в море | Понятное сообщение об ошибке в sheet | ### P2 — Nice to have | ID | Сценарий | Шаги | Ожидаемый результат | |----|----------|------|---------------------| | T27 | Safari iOS | Открыть | Safe area работает, нет обрезания снизу | | T28 | Медленный интернет | Throttle 3G | Skeleton, потом данные | | T29 | Очень длинное название места | Поставить точку у «деревня Нижние Бородавки» | Название обрезается с ellipsis | | T30 | 3 альтернативных маршрута | Построить маршрут | 3 карточки, можно переключать | | T31 | Scenic score визуализация | В карточке Красивого | Звёздочки или bar для scenic_score | | T32 | POI на маршруте в Красивом | В карточке | Иконки POI с названиями | | T33 | Кнопки режима вместе видны | На экране 375px | Все 6 иконок в toolbar без обрезания | | T34 | Статус бар iOS | iPhone | Нет перекрытия search bar статус-баром | ## 9. Definition of Done - [ ] Все P0 тест-кейсы прошли на iPhone SE (375px) - [ ] Все P0 + P1 тест-кейсы прошли на iPhone 14 Pro (393px) - [ ] Bottom sheet плавно открывается/закрывается - [ ] Toolbar: все 6 режимов, min 48px tap target - [ ] Тема: авто (по восходу/закату SunCalc) + ручной переключатель (3 режима: Авто/Светлая/Тёмная) - [ ] Тёмная тема везде, нет белых вспышек; светлая тема без ослепляющего белого - [ ] SVG иконки (Lucide), никаких emoji в UI-элементах - [ ] Карта видна при активных панелях - [ ] Safe area корректная (notch, home indicator) - [ ] Десктоп: боковая панель, не ломается layout - [ ] GPX скачивание работает - [ ] Деплой + health check OK ## 10. Технические ограничения - Менять бэкенд (app.py) нельзя — только фронт - MapLibre GL остаётся - Без новых npm-зависимостей (только inline CSS/JS) - Lucide иконки — подключить через CDN: `https://unpkg.com/lucide@latest` - SunCalc — подключить через CDN: `https://unpkg.com/suncalc@latest` (MIT, ~3KB, для расчёта восхода/заката) - Деплой через ssh2 (стандартная схема) --- *Документ готов к согласованию.*