Files
pgames-manager/docs/UI_DESIGN.md
T
mrleo1nidandClaude Sonnet 5.5 9ee3e1ff6e UI: unified design system, light/dark themes, redesigned pages and player UI
Tokens and components in styles.css, shared Ui (dialogs, toasts), icons,
bottom tab bar on mobile, keyboard/gamepad navigation, library shelf with
filters and conversion badges, hero game page, restyled player screens.
Games list now returns per-game version counts. Adds docs/UI_DESIGN.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 03:43:53 +03:00

124 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UI: дизайн-система PGames
Документ для тех, кто добавляет или дорабатывает интерфейс. Прочитайте перед
правкой `frontend/`. Код — источник истины, здесь только правила и карта.
## Принципы
1. **«Кинотека»**: спокойный тёмный фон, обложки — главные, один акцент (оранжевый).
2. **Одна система.** Цвета, отступы, радиусы — только через токены (`--accent`, `--space-3`…).
Никаких захардкоженных `#hex` и «магических» px в страницах.
3. **Две темы** (тёмная по умолчанию, светлая, «как в системе»). Всё, что вы
добавите, должно выглядеть нормально в обеих — проверяйте обе.
4. **Все устройства**: десктоп, планшет, телефон, ТВ с геймпадом. Мышь — не единственный сценарий.
5. **Без нативных диалогов.** `alert/confirm/prompt` запрещены (ломаются в
полноэкранном режиме и на геймпаде) — используйте `useUi()`.
6. **Язык интерфейса — русский**, тон короткий и без жаргона.
## Карта файлов
| Файл | Что внутри |
|---|---|
| `src/styles.css` | Весь CSS сайта: токены и темы, контролы, поверхности, шапка, страницы, адаптив |
| `src/theme.ts` | `useTheme()` — выбор темы (`system`/`dark`/`light`), хранится в localStorage `pg-theme` |
| `src/ui/Icon.tsx` | Набор иконок (stroke, 24px). Новая иконка — добавить путь в `PATHS` |
| `src/ui/Ui.tsx` | `useUi()` → `confirm`, `prompt`, `toast` (провайдер в `main.tsx`) |
| `src/ui/Bits.tsx` | `PageHeader`, `EmptyState`, `ErrorNote`, `Spinner`, `Cover` |
| `src/ui/nav.ts` | Навигация стрелками и геймпадом (`useSpatialNav`) |
| `src/ui/ThemeButton.tsx` | Кнопка темы для экранов без шапки |
| `src/recent.ts` | «Продолжить игру» (localStorage `pg-recent`) |
| `src/player/player.css` | Стили плеера (отдельная система, см. ниже) |
## Токены (`:root`)
- Фоны: `--bg`, `--surface`, `--surface-2`, `--surface-3` (от дальнего к ближнему слою).
- Границы: `--border`, `--border-strong`. Текст: `--text`, `--text-2`, `--muted`.
- Акцент: `--accent`, `--accent-hover`, `--accent-ink` (цвет текста на акценте), `--accent-soft` (фон-подсветка).
- Семантика: `--ok`, `--danger`, `--info` и их `*-soft`.
- Геометрия: `--radius-s` 8 / `--radius` 12 / `--radius-l` 18; `--space-1…6` (4–36px);
`--control-h` — высота контролов (растёт на тач-устройствах).
- Тень: `--shadow-1/2/3`. Оверлей: `--overlay`.
Светлая тема переопределяет токены в `:root[data-theme="light"]` и в
`@media (prefers-color-scheme: light)` для `:root:not([data-theme])`. **Добавляя
токен, добавьте его во все три блока.**
## Компоненты (CSS-классы)
- **Кнопки** `.btn` + модификатор: `.btn-primary` (одна главная на экран),
`.btn-ghost`, `.btn-danger` (контурная), `.btn-danger-solid` (в диалоге удаления),
размеры `.btn-sm`, `.btn-lg`, `.btn-icon`, `.btn-block`. Иконка слева, 16–18px (в `-lg` 20–22).
Кнопка только с иконкой обязана иметь `aria-label` и `title`.
- **Поля**: `<label className="field"><span>Подпись</span><input/> <span className="hint">…</span></label>`.
Формы — `.form`, ряд полей — `.form-row`.
- **Карточка** `.card`; заметки `.note .note-error|ok|info` (через `ErrorNote`).
- **Статусы** `.badge` + `badge-ok|warn|error|plain`, `busy` — пульсирующая точка для процесса.
- **Чипы** `.chip` (`.on` — выбран), **теги** `.tag`.
- **Таблицы** `.table` в `.table-wrap`. На телефоне таблицу с действиями
заменяйте карточками (пример — `Users.tsx`, `.users-table` / `.user-cards`).
- **Переключатель права** `.switch` (input + `<i/>`).
- **Пустое состояние** — `EmptyState`, загрузка списков — `.skeleton`, загрузка действия — `Spinner`.
- **Меню профиля** — `UserMenu` в `App.tsx`; админ-разделы живут там, не в основной навигации.
## Правила для страниц
- Обёртка `.page` (`.narrow` 600px / `.medium` 960px для форм и админки).
- Заголовок — `PageHeader` (title, subtitle, actions справа).
- Одна главная кнопка (`btn-primary`) на экран/блок. Опасные действия — `btn-danger`
и **всегда** через `ui.confirm({ danger: true })` с пояснением последствий.
- Результат действия — `ui.toast(text, "ok" | "error")`. Ошибки загрузки страницы —
`ErrorNote` в потоке. Не оставляйте «молчаливых» действий.
- Любой список имеет три состояния: загрузка (скелетон/спиннер), пусто (`EmptyState` с
подсказкой, что делать), данные.
- Ввод имени/значения — `ui.prompt`, а не `window.prompt`.
- Тексты: кнопки — глагол («Создать форк»), подтверждение — конкретное действие, не «Ок».
## Адаптив и устройства
- Точка перехода на телефон — **860px**: верхняя навигация скрывается, появляется нижний `.tabbar`
(до 5 пунктов, иконка + подпись), профиль — по аватару. Узкий телефон — **520px** (сетка в 2 колонки).
- `@media (pointer: coarse)`: контролы выше (2.9rem). Минимальная зона касания 44px.
- ТВ: от 1700px увеличивается шрифт, от 2400px ещё больше. Не фиксируйте размеры в px там, где можно `rem`.
- **Фокус всегда виден** (`:focus-visible` — 3px акцентная рамка). Не убирайте `outline`.
- Клавиатура/геймпад: стрелки и D-pad двигают фокус к ближайшему элементу (`nav.ts`),
A — нажать, B — назад. Для этого интерактивные элементы должны быть настоящими
`button`/`a`/`input`, а не `div onClick`. Для карточек используйте `<Link>`.
- Уважайте `prefers-reduced-motion` (уже глобально сокращает анимации).
## Плеер (`src/player`)
Разделяется на два слоя, их стили не смешиваются:
1. **Игровой слой** (`.pg-textbox`, `.pg-namebox`, `.pg-text`, `.pg-menu`, `.pg-choice`, NVL, centered) —
должен выглядеть как оригинальная новелла. Цвета и шрифты может переопределять сама игра
(`--pg-accent`, `--pg-text`, шрифты). Не привязывайте его к токенам сайта.
2. **Служебный UI плеера** (`.pg-quick`, `.pg-screen`, `.pg-main-panel`, слоты, настройки, `.pg-dialog`,
`.pg-hud-btn`) — единый стиль «стеклянных» панелей поверх игры, одинаковый для всех игр.
Нюансы:
- Сцена масштабируется `transform: scale()`, размеры внутри — в `em` от `--pg-font`.
Поэтому **кнопки для касания выносите за сцену** (как `.pg-hud-btn` в `.pg-root`, фиксированные px).
- Подтверждения внутри плеера — `Engine.confirmAsk()`, не `confirm()`.
- Новые экраны открывайте через `openScreen(title, build)`: заголовок, кнопка закрытия и Esc уже есть.
- Кнопки в экранах — обычные `button`, чтобы работала навигация геймпадом (`nav.ts` ограничивается видимым
`.pg-screen` / `.pg-menu`).
- Управление: Esc — меню, Space/Enter — дальше, `A` — авто, `H` — скрыть UI, Ctrl/Tab — пропуск;
геймпад: A/B/X/Y.
## Как добавить новый экран (чек-лист)
1. Страница в `src/pages`, маршрут и `RequirePerm` в `App.tsx`; пункт в нижнем `.tabbar` — только если
это основной раздел (иначе в меню профиля).
2. `PageHeader`, состояния загрузки/пусто/ошибка, действия через `useUi`.
3. Только токены и существующие классы; новый повторяющийся паттерн — в `styles.css` (раздел по смыслу), не inline.
4. Проверка: обе темы; ширины 375 / 768 / 1280 / 1920; навигация только клавиатурой (Tab и стрелки);
`npm run build` без ошибок.
## Чего не делать
- `alert/confirm/prompt`, `window.open` для служебных окон.
- Цвета и размеры «от руки», `!important` (исключение — уже существующие в плеере).
- Анимации длиннее ~200 мс на интерактивных элементах.
- Информация только цветом: у статуса всегда есть текст.
- Скрытие фокуса, `div` вместо кнопок, иконки без подписи.