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>
10 KiB
10 KiB
UI: дизайн-система PGames
Документ для тех, кто добавляет или дорабатывает интерфейс. Прочитайте перед
правкой frontend/. Код — источник истины, здесь только правила и карта.
Принципы
- «Кинотека»: спокойный тёмный фон, обложки — главные, один акцент (оранжевый).
- Одна система. Цвета, отступы, радиусы — только через токены (
--accent,--space-3…). Никаких захардкоженных#hexи «магических» px в страницах. - Две темы (тёмная по умолчанию, светлая, «как в системе»). Всё, что вы добавите, должно выглядеть нормально в обеих — проверяйте обе.
- Все устройства: десктоп, планшет, телефон, ТВ с геймпадом. Мышь — не единственный сценарий.
- Без нативных диалогов.
alert/confirm/promptзапрещены (ломаются в полноэкранном режиме и на геймпаде) — используйтеuseUi(). - Язык интерфейса — русский, тон короткий и без жаргона.
Карта файлов
| Файл | Что внутри |
|---|---|
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-s8 /--radius12 /--radius-l18;--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 (в-lg20–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(.narrow600px /.medium960px для форм и админки). - Заголовок —
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)
Разделяется на два слоя, их стили не смешиваются:
- Игровой слой (
.pg-textbox,.pg-namebox,.pg-text,.pg-menu,.pg-choice, NVL, centered) — должен выглядеть как оригинальная новелла. Цвета и шрифты может переопределять сама игра (--pg-accent,--pg-text, шрифты). Не привязывайте его к токенам сайта. - Служебный 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.
Как добавить новый экран (чек-лист)
- Страница в
src/pages, маршрут иRequirePermвApp.tsx; пункт в нижнем.tabbar— только если это основной раздел (иначе в меню профиля). PageHeader, состояния загрузки/пусто/ошибка, действия черезuseUi.- Только токены и существующие классы; новый повторяющийся паттерн — в
styles.css(раздел по смыслу), не inline. - Проверка: обе темы; ширины 375 / 768 / 1280 / 1920; навигация только клавиатурой (Tab и стрелки);
npm run buildбез ошибок.
Чего не делать
alert/confirm/prompt,window.openдля служебных окон.- Цвета и размеры «от руки»,
!important(исключение — уже существующие в плеере). - Анимации длиннее ~200 мс на интерактивных элементах.
- Информация только цветом: у статуса всегда есть текст.
- Скрытие фокуса,
divвместо кнопок, иконки без подписи.