Files
docs/development/design-system.md
T

133 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.
# Дизайн-система WealthLedger
Общий фундамент находится в
[`frontend/src/shared/theme`](../../frontend/src/shared/theme/index.ts).
Его public API экспортирует `theme`, `cssVariablesResolver`, `colorRoles`, `designTokens` и
подключает базовые CSS. App владеет MantineProvider; Storybook подключает свой
изолированный provider к тому же API. Тема и значения не копируются в preview.
Оба runtime используют светлую схему; dark theme не заявлена как поддерживаемая.
## Тема и токены
[`tokens.json`](../../frontend/src/shared/theme/tokens.json) — единственный
редактируемый источник опубликованных токенов WealthLedger. Он содержит восемь
палитр по десять оттенков: blue, gray, dark, red, green, yellow, grape и orange;
базовые white/black, оттенки по умолчанию, семантические роли с описаниями,
типографику, интервалы, радиусы, тени и breakpoints. Существующие значения
приняты из темы приложения и Mantine 9.5.1; теперь ими владеет JSON. Default
radius остаётся md, font family заголовков совпадает с общей.
[`theme.ts`](../../frontend/src/shared/theme/theme.ts) проверяет source через
установленный Zod и [`tokens.schema.ts`](../../frontend/src/shared/theme/tokens.schema.ts),
затем адаптирует данные в Mantine. Схема проверяет структуру, единицы и диапазоны,
число оттенков и ссылки ролей; ошибочные данные не подменяются defaults.
Размеры px преобразуются native `rem`, сохраняя `--mantine-scale`, тени собираются
из слоёв с `rgba`, а breakpoints остаются em без масштабирования. Неопубликованные
палитры и настройки поведения компонентов остаются defaults Mantine.
Используйте props Mantine (`gap="md"`, `p="xl"`, `radius="md"`, `shadow="xs"`)
или `--mantine-spacing-*`, `--mantine-radius-*` и другие native CSS variables.
[Foundations](http://localhost:6006/?path=/story/design-system-foundations--colors)
читает эффективную тему и описания из JSON, показывает фактические значения
и образцы. Обновление Mantine не заменяет принятые шкалы автоматически и требует
повторной проверки адаптера и внешнего вида приложения.
`colorRoles` связывает смысл и native Mantine reference; resolver создаёт
`--wl-color-<role>`, ссылающийся на Mantine variable, без повторного hex-значения.
| Role | Native reference | Применение |
| --- | --- | --- |
| canvas | gray.2 | Фон приложения |
| surface | white | Paper и формы в светлой схеме |
| subtle | gray.0 | Приглушённая поверхность |
| text | dark.7 | Основной текст страницы |
| muted | gray.6 | Вторичный текст (dimmed светлой схемы) |
| border | gray.3 | Граница поверхности |
| primary | blue filled | Основное действие и активная навигация |
| info | blue.6 | Информация |
| positive | green.6 | Положительный результат с текстом/знаком |
| negative | red.6 | Ошибка или отрицательный результат с текстом/знаком |
| warning | yellow.6 | Предупреждение, не мелкий текст на белом |
Базовые CSS используют canvas/text aliases. Остальные роли доступны для
постепенного использования; существующие локальные Mantine references не
переписаны массово. Цвет не заменяет подпись статуса. Палитра не гарантирует
доступный контраст произвольных сочетаний: для текста и фона проверяйте контекст.
Grape и orange используются Product; предметные значения задают их FSD-владельцы.
Особые card gradients/shadows, размеры страницы и финансовые цвета CSS остаются
у своих FSD-владельцев. Это сохранение существующего UI, а не завершённый редизайн
или миграция всех literals в tokens. Новую общую шкалу стоит вводить только при
реальном повторном использовании, если native Mantine scale не подходит.
## Переносимый JSON
Файл использует собственный формат WealthLedger `formatVersion: 1`, без runtime
imports, CSS variables или исполняемого кода. Это контракт данных, а не заявление
о совместимости с DTCG или importer конкретной системы.
| Данные | Представление |
| --- | --- |
| Цвета | Hex; palette — массив оттенков 0–9; base — именованные white/black. |
| Семантическая роль | `color`, `shade`, `description`; ссылка на palette или base. Для palette `shade: null` означает `primaryShade.light`, для base оттенок не нужен. |
| Типографика | CSS font stacks; размеры и headings в px, веса числовые, line heights без единиц. |
| Интервалы и радиусы | Именованные `values` xs–xl с `unit: "px"`; `radius.default` выбирает имя шкалы. |
| Тени | Упорядоченные слои: offsetX/offsetY/blur/spread в px, ссылка `color` на base и opacity 0–1. |
| Breakpoints | Именованные `values` xs–xl с `unit: "em"`, не зависят от масштаба UI. |
Имена палитр `dimmed` и `bright` зарезервированы адаптером Mantine;
`white` и `black` принадлежат base colors. Вес шрифта ограничен диапазоном 1–1000.
Внешний потребитель читает raw JSON напрямую. Например, из workspace root,
без Node, React или Mantine:
```python
import json
from pathlib import Path
tokens = json.loads(Path("frontend/src/shared/theme/tokens.json").read_text())
assert tokens["formatVersion"] == 1
colors = tokens["color"]
role = colors["roles"]["primary"]
shade = role["shade"]
if shade is None:
shade = colors["primaryShade"]["light"]
primary_hex = colors["palettes"][role["color"]][shade]
```
`shared/theme/index.ts` предназначен для frontend: он подключает CSS и Mantine
runtime. При интеграции с другой системой сопоставьте единицы, font stacks,
слои теней и semantic references с её форматом; парсер JSON сам этого не делает.
Несовместимое изменение требует новой версии контракта. Внешняя интеграция,
генератор артефактов и публикация пакета пока не реализованы.
## Общие компоненты
Public API — [`shared/ui`](../../frontend/src/shared/ui/index.ts). Минимальный
каталог включает две существующие реализации, реально используемые приложением:
| Компонент | Контракт и правила |
| --- | --- |
| StateView | Empty/error с title, text, необязательным action; error получает role alert. Кнопке нужен доступный текст; не использовать для initial loading. |
| LoadingSkeleton | Общая status/busy-область с доступной label; caller задаёт только декоративные shapes без фокуса. Структура skeleton принадлежит section/page. |
Они не импортируют API, auth, счета или позиции. Не создаются wrappers над всеми
примитивами: для действий используется Mantine Button с общей темой. В каталоге
показаны filled для основного действия, default для вторичного/retry и subtle
для вспомогательного, а также loading и disabled. Provider не меняет defaults
Button или Paper ради каталога. Loading действия не заменяет loading страницы;
при refetch сохраняются данные и показывается локальное состояние.
Route-aware AppShell не входит в заявленный общий набор. Бизнес-компоненты
и композиции остаются у entities/pages и показываются в Product; витрина не
определяет FSD ownership. Подробности запуска, взаимодействий и coverage —
[UI-каталог Storybook](ui-catalog.md).
## Границы доказательств
Каталог использует synthetic fixtures, локальные Actions и MemoryRouter, без
application API/auth/storage runtime. Dependency-cruiser проверяет FSD и
транзитивную runtime-изоляцию stories. Browser проверяет виды, размеры, focus и
действия. Эти примеры не доказывают API recovery и прохождение инвестиционного
сценария. Продуктовый пилот остаётся отдельным change и здесь не проводился.