Files
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

10 KiB
Raw Permalink Blame History

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 вместо кнопок, иконки без подписи.