# Дизайн-система 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) напрямую адаптирует JSON в Mantine. Файл — доверенная конфигурация репозитория; приложение не загружает внешние или пользовательские токены и не проверяет JSON runtime-схемой. Размеры px преобразуются native `rem`, сохраняя `--mantine-scale`, тени собираются из слоёв с `rgba`, а breakpoints остаются em без масштабирования. Неопубликованные палитры и настройки поведения компонентов остаются defaults Mantine. Небольшой [`theme.test.ts`](../../frontend/src/shared/theme/theme.test.ts) проверяет преобразования адаптера; корректность изменений локального JSON проверяется при review, сборке и проверке внешнего вида. Используйте 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-`, ссылающийся на 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. | Не используйте для палитр специальные имена Mantine `dimmed` и `bright`; `white` и `black` принадлежат base colors. Задавайте вес шрифта в диапазоне CSS 1–1000. Это правила редактирования формата, а не runtime-проверки. Внешний потребитель читает 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 и здесь не проводился.