From e6cf8527410398f1b73fba6227da4aa544b7af8a Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Sun, 21 Jun 2026 15:40:17 +0300 Subject: [PATCH] docs(design-system): publish usage guidelines and ADR-016 --- apps/docs/docs/adr/ADR-016-design-system.md | 79 ++++++ apps/docs/docs/adr/index.md | 2 + apps/docs/docs/design-system/accessibility.md | 49 ++++ apps/docs/docs/design-system/components.md | 259 ++++++++++++++++++ apps/docs/docs/design-system/foundations.md | 52 ++++ apps/docs/docs/design-system/governance.md | 38 +++ apps/docs/docs/design-system/overview.md | 42 +++ apps/docs/docs/design-system/patterns.md | 94 +++++++ apps/docs/docs/design-system/tokens.md | 106 +++++++ apps/docs/docs/frontend/styling.md | 12 +- apps/docs/sidebars.ts | 13 + packages/design-system/CHANGELOG.md | 11 + 12 files changed, 756 insertions(+), 1 deletion(-) create mode 100644 apps/docs/docs/adr/ADR-016-design-system.md create mode 100644 apps/docs/docs/design-system/accessibility.md create mode 100644 apps/docs/docs/design-system/components.md create mode 100644 apps/docs/docs/design-system/foundations.md create mode 100644 apps/docs/docs/design-system/governance.md create mode 100644 apps/docs/docs/design-system/overview.md create mode 100644 apps/docs/docs/design-system/patterns.md create mode 100644 apps/docs/docs/design-system/tokens.md create mode 100644 packages/design-system/CHANGELOG.md diff --git a/apps/docs/docs/adr/ADR-016-design-system.md b/apps/docs/docs/adr/ADR-016-design-system.md new file mode 100644 index 0000000..8402226 --- /dev/null +++ b/apps/docs/docs/adr/ADR-016-design-system.md @@ -0,0 +1,79 @@ +# ADR-016: Дизайн-система — гибридный подход (theme + wrapper-компоненты) + +**Статус:** Accepted + +**Дата:** 2026-06-21 + +## Контекст + +Фронтенд MoexVibe нуждался в единообразном UI. Ранее каждая страница использовала MUI напрямую, что приводило к: + +- неконсистентным вариантам компонентов (разные цвета, размеры, отступы); +- отсутствию единой системы токенов; +- размножению стилей в CSS custom properties. + +Рост количества страниц и компонентов потребовал системного подхода. + +## Рассмотренные варианты + +### 1. Theme-only + +Оставить прямое использование MUI, но настроить единую тему (palette, typography, shape). + +**Плюсы:** минимальные изменения, полная гибкость MUI. +**Минусы:** нет гарантии консистентности — разработчики могут использовать любые варианты MUI-компонентов. + +### 2. Hybrid (выбран) + +MUI theme обеспечивает фундамент (палитра, типографика, скругления); wrapper-компоненты поверх MUI гарантируют консистентность и ограничивают варианты. + +**Плюсы:** + +- единый source of truth в теме MUI; +- компоненты с ограниченным API (только нужные варианты); +- возможность миграции (можно заменить реализацию под капотом); +- Box/Stack/Grid остаются как allowlist для лэйаута. + +**Минусы:** + +- необходимо написать 20+ wrapper-компонентов; +- двойная прослойка для некоторых сценариев. + +### 3. Full wrapper + +Все UI через собственные компоненты, без прямого использования MUI. + +**Плюсы:** полный контроль, независимость от MUI. +**Минусы:** огромный объём работы, дублирование функциональности MUI. + +## Решение + +Выбран гибридный подход (option 2): + +- **Токены:** трёхуровневая система (primitives → semantic → component) с DTCG-резолвером. +- **Тема:** MUI 6.5 theme, построенная на семантических токенах. CSS-переменные `--mv-*` для доступа из CSS. +- **Компоненты:** 20+ wrapper-компонентов, каждый с ограниченным пропс-интерфейсом. +- **ESLint:** `no-restricted-imports` запрещает прямой импорт MUI-компонентов (кроме Box, Stack, Grid). +- **Storybook:** инженерный стенд с a11y-проверками. +- **Документация:** Docusaurus для опубликованных гайдлайнов (этот сайт). + +## Последствия + +### Положительные + +- Консистентный UI во всех страницах. +- Единая система токенов, доступная из CSS (через `--mv-*`). +- Изолированная библиотека компонентов, которую можно тестировать и развивать независимо. +- Возможность замены реализации под капотом без изменения потребителей. + +### Отрицательные + +- 20+ компонентов нужно поддерживать. +- Оверхед на поддержку пропс-интерфейсов (каждый компонент ограничивает MUI API). +- Новые разработчики должны знать два слоя (MUI theme + wrapper-компоненты). + +### Future + +- DTCG-экспорт токенов для дизайн-инструментов. +- Android-адаптеры для нативных приложений. +- Расширение компонентной базы. diff --git a/apps/docs/docs/adr/index.md b/apps/docs/docs/adr/index.md index 66378c7..ac259c5 100644 --- a/apps/docs/docs/adr/index.md +++ b/apps/docs/docs/adr/index.md @@ -16,5 +16,7 @@ | [ADR-012](ADR-012-frontend-broker-account-aggregation) | Accepted | Агрегация сводки брокерских счетов на frontend | | [ADR-013](ADR-013-frontend-fsd-broker-pilot) | Accepted | Пилотная FSD-миграция broker-домена | | [ADR-014](ADR-014-frontend-fsd-market-pages) | Accepted | FSD-миграция market pages и market widgets | +| [ADR-015](ADR-015-frontend-libraries-modernization) | — | Модернизация инфраструктуры фронтенда | +| [ADR-016](ADR-016-design-system) | Accepted | Дизайн-система — гибридный подход | Все опубликованные ADR находятся в `apps/docs/docs/adr/` и отображаются в этом Docusaurus-разделе. diff --git a/apps/docs/docs/design-system/accessibility.md b/apps/docs/docs/design-system/accessibility.md new file mode 100644 index 0000000..e4f104f --- /dev/null +++ b/apps/docs/docs/design-system/accessibility.md @@ -0,0 +1,49 @@ +# Доступность (Accessibility) + +## Целевой уровень + +WCAG 2.2 AA. + +## Требования к компонентам + +### IconButton + +**Требует `label`** — свойство `aria-label` обязательно для всех иконок без текста. + +```tsx + + + +``` + +### Dialog + +- **Требует `title`** — заголовок, используемый как `aria-labelledby` +- Содержит focus trap (фокус не покидает диалог) +- Закрывается по Escape + +### Progress + +- `indeterminate`-режим выставляет `aria-busy` на контейнере + +### PriceChange + +- Использует **текстовый индикатор направления** (`+`, `−`, `—`), а не только цвет +- Скринридеры получают знак перед числом + +### DataTable + +- **Требует `caption`** — описание таблицы для скринридеров +- Семантические заголовки через `` + +### Skeleton + +- `aria-hidden` по умолчанию — скелетоны не должны озвучиваться + +### Alert + +- Использует `role="alert"` для немедленного объявления скринридером + +### LoadingState + +- `aria-live="polite"` — скринридер объявит об изменении после завершения текущей речи diff --git a/apps/docs/docs/design-system/components.md b/apps/docs/docs/design-system/components.md new file mode 100644 index 0000000..73b98cb --- /dev/null +++ b/apps/docs/docs/design-system/components.md @@ -0,0 +1,259 @@ +# Компоненты + +## Матрица «задача → компонент» + +| Задача | Рекомендуемый компонент | Не использовать | +|--------|------------------------|-----------------| +| Отображение текста | Text, Heading | MUI Typography напрямую | +| Ссылка | Link | MUI Link напрямую | +| Действие | Button, IconButton | MUI Button напрямую | +| Ввод текста | TextField | MUI TextField напрямую | +| Выбор из списка | Select | MUI Select напрямую | +| Выбор опции | Checkbox | MUI Checkbox напрямую | +| Контейнер/фон | Surface | MUI Paper напрямую | +| Карточка | Card | MUI Card напрямую | +| Ярлык/тег | Chip | MUI Chip напрямую | +| Бейдж | Badge | MUI Badge напрямую | +| Уведомление | Alert | MUI Alert напрямую | +| Модальное окно | Dialog | MUI Dialog напрямую | +| Загрузка скелета | Skeleton | MUI Skeleton напрямую | +| Индикатор прогресса | Progress | MUI CircularProgress / LinearProgress напрямую | +| Финансовое значение | Money, Metric, PriceChange | сырое число | +| Таблица | DataTable | MUI Table напрямую | +| Поле формы | FormField | ручная связка label + helperText | +| Панель фильтров | FilterBar | ручная вёрстка фильтров | +| Пустое состояние | EmptyState | ручная вёрстка | +| Ошибка | ErrorState | ручная вёрстка | +| Загрузка | LoadingState | ручная вёрстка | + +## Каталог компонентов + +### Text + +Отображение текста с заданным семантическим размером. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `variant` | `'body1' \| 'body2' \| 'caption' \| 'overline'` | `'body1'` | +| `color` | `'primary' \| 'secondary' \| 'disabled' \| 'inverse'` | `'primary'` | + +### Heading + +Заголовок. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `variant` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6'` | `'h3'` | + +### Link + +Ссылка. Под капотом MUI Link с цветом `color.action.primary`. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `href` | `string` | — | +| `underline` | `'none' \| 'hover' \| 'always'` | `'hover'` | + +### Button + +Кнопка действия. Обёртка MUI Button. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `variant` | `'primary' \| 'secondary' \| 'danger'` | `'primary'` | +| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | +| `loading` | `boolean` | `false` | + +### IconButton + +Кнопка-иконка. **Требует `label`** (aria-label). + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `label` | `string` | — | +| `size` | `'sm' \| 'md'` | `'md'` | + +### TextField + +Текстовое поле ввода. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `size` | `'sm' \| 'md'` | `'md'` | +| `fullWidth` | `boolean` | `true` | + +### Select + +Выпадающий список. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `size` | `'sm' \| 'md'` | `'md'` | +| `fullWidth` | `boolean` | `true` | + +### Checkbox + +Чекбокс. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `label` | `string` | — | + +### Surface + +Базовый контейнер с фоном и скруглением. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `variant` | `'default' \| 'subtle' \| 'raised'` | `'default'` | + +### Card + +Карточка. Поверхность с тенью. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `padding` | `'sm' \| 'md' \| 'lg'` | `'md'` | + +### Chip + +Компактный элемент для отображения тега или статуса. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `variant` | `'filled' \| 'outlined'` | `'filled'` | +| `color` | `'default' \| 'info' \| 'success' \| 'warning' \| 'error'` | `'default'` | + +### Badge + +Бейдж с числовым или текстовым значением. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `variant` | `'dot' \| 'standard'` | `'standard'` | +| `color` | `'info' \| 'success' \| 'warning' \| 'error'` | `'info'` | + +### Alert + +Уведомление. Использует `role="alert"`. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `severity` | `'info' \| 'success' \| 'warning' \| 'error'` | `'info'` | +| `title` | `string` | — | + +### Dialog + +Модальное окно. **Требует `title`**, содержит focus trap, закрывается по Escape. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `open` | `boolean` | — | +| `title` | `string` | — | +| `onClose` | `() => void` | — | + +### Skeleton + +Скелетон загрузки. `aria-hidden` по умолчанию. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `width` | `string \| number` | `'100%'` | +| `height` | `string \| number` | `20` | + +### Progress + +Индикатор прогресса. Поддерживает indeterminate-режим с `aria-busy`. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `variant` | `'determinate' \| 'indeterminate'` | `'indeterminate'` | +| `value` | `number` (0–100) | `0` | + +### DataTable + +Таблица данных на основе TanStack Table. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `columns` | `ColumnDef[]` | — | +| `data` | `T[]` | — | +| `caption` | `string` | — | +| `density` | `'balanced' \| 'compact'` | `'balanced'` | + +### Money + +Финансовое значение с форматированием через `Intl.NumberFormat`. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `value` | `number` | — | +| `currency` | `string` | `'RUB'` | +| `locale` | `string` | `'ru-RU'` | + +### Metric + +Метрика: лейбл + значение + опционально supporting/trend. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `label` | `string` | — | +| `value` | `ReactNode` | — | +| `supporting` | `ReactNode` | — | +| `trend` | `'up' \| 'down' \| 'neutral'` | — | + +### PriceChange + +Изменение цены. Использует **текстовое направление** (не только цвет). + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `value` | `number` | — | +| `percent` | `number` | — | +| `variant` | `'absolute' \| 'percent' \| 'both'` | `'both'` | + +### FormField + +Обёртка для поля формы: label + helperText + error. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `label` | `string` | — | +| `error` | `string` | — | +| `helperText` | `string` | — | + +### FilterBar + +Панель фильтров с responsive-обёрткой. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `children` | `ReactNode` | — | + +### EmptyState + +Пустое состояние. **Требует `title`**. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `title` | `string` | — | +| `description` | `string` | — | +| `action` | `ReactNode` | — | + +### ErrorState + +Состояние ошибки. **Требует `title`**. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `title` | `string` | — | +| `description` | `string` | — | +| `onRetry` | `() => void` | — | + +### LoadingState + +Состояние загрузки. `aria-live="polite"`. + +| Свойство | Тип | По умолчанию | +|----------|-----|--------------| +| `title` | `string` | — | diff --git a/apps/docs/docs/design-system/foundations.md b/apps/docs/docs/design-system/foundations.md new file mode 100644 index 0000000..62703dd --- /dev/null +++ b/apps/docs/docs/design-system/foundations.md @@ -0,0 +1,52 @@ +# Фундаментальные токены + +## Архитектура + +Система токенов состоит из трёх уровней: + +```text +Primitives ──► Semantic ──► Component + (сырые (алиасы (алиасы для + значения) назначения) компонентов) +``` + +### Primitives + +Хранят абсолютные значения: конкретные цвета, размеры, шрифты. Не несут семантической нагрузки. + +Пример: `color.neutral.900` → `#1a1a1a`, `space.4` → `32px`. + +### Semantic + +Алиасы, ссылающиеся на примитивы и описывающие назначение. Используются в приложении и компонентах. + +Пример: `color.text.primary` → `{color.neutral.900}` → `#1a1a1a`. + +### Component + +Алиасы для конкретных компонентов. Позволяют переопределять токены на уровне компонента без изменения семантических токенов. + +Пример: `button.bg` → `{color.action.primary}` → `{color.green.700}`. + +## Резолвер + +Используется DTCG-подобный резолвер (Design Tokens Community Group): + +- Цепочная резолюция: `component → semantic → primitive` +- Проверка циклов +- Проверка типов (`$type` должен совпадать) +- Кэширование результатов + +## Токены v1 + +В версии 1 все токены светлые (light-only). Ключевые группы: + +- **color** — цвета (canvas, surface, text, border, action, feedback, finance) +- **typography** — шрифты (family, weight) +- **spacing** — отступы (inset, stack, inline) +- **shape** — скругления (в примитивах) +- **shadow** — тени (в примитивах) +- **duration** — длительности анимаций (в примитивах) +- **easing** — функции сглаживания (в примитивах) + +Полный список семантических токенов — [Токены](tokens). diff --git a/apps/docs/docs/design-system/governance.md b/apps/docs/docs/design-system/governance.md new file mode 100644 index 0000000..4f7045b --- /dev/null +++ b/apps/docs/docs/design-system/governance.md @@ -0,0 +1,38 @@ +# Управление (Governance) + +## Режим v1 + +- **Только светлая тема**. Тёмная тема отложена до v2. +- Изменения, затрагивающие цветовую палитру или контрастность, должны проверяться на WCAG 2.2 AA. + +## Процесс добавления компонентов + +Новый компонент проходит полный цикл: + +1. **Spec** — спецификация API компонента +2. **Plan** — план реализации +3. **TDD** — тесты пишутся до реализации +4. **Storybook** — интерактивный каталог с a11y-аддоном +5. **Review** — код-ревью с проверкой accessibility + +## Breaking changes + +Любое изменение публичного API компонента или токена требует: + +- Обновления мажорной версии +- Миграционной заметки в `CHANGELOG.md` + +## Импорты MUI + +Прямые импорты из MUI в коде приложения (`apps/frontend`) запрещены, за исключением allowlist: + +| Компонент | Разрешён | +|-----------|----------| +| Box | ✅ | +| Stack | ✅ | +| Grid | ✅ | +| Typography | ❌ → использовать Text / Heading | +| Button | ❌ → использовать Button | +| Остальные MUI | ❌ | + +Нарушение контролируется ESLint-правилом `no-restricted-imports` (уровень `warn`). diff --git a/apps/docs/docs/design-system/overview.md b/apps/docs/docs/design-system/overview.md new file mode 100644 index 0000000..fb92c7c --- /dev/null +++ b/apps/docs/docs/design-system/overview.md @@ -0,0 +1,42 @@ +# Дизайн-система + +## Что это + +`@moex-vibe/design-system` — внутренний пакет дизайн-системы (v0.1.0). Построен на [MUI 6.5](https://mui.com/material-ui/). Поддерживает только светлую тему (light-only). + +## Архитектура токенов + +Трёхуровневая система токенов: + +1. **Primitives** — сырые значения (цвета, размеры, шрифты) +2. **Semantic** — алиасы, описывающие назначение (`color.text.primary`) +3. **Component** — алиасы для конкретных компонентов (`button.bg`) + +Подробнее — [Токены](foundations). + +## Компоненты + +Библиотека включает 25 компонентов: + +| Категория | Компоненты | +|-----------|------------| +| Typography | Text, Heading, Link | +| Actions | Button, IconButton | +| Inputs | TextField, Select, Checkbox | +| Surfaces | Surface, Card | +| Feedback | Chip, Badge, Alert, Dialog, Skeleton, Progress | +| Financial | Money, Metric, PriceChange | +| Form | FormField, FilterBar | +| Page States | EmptyState, ErrorState, LoadingState | +| Data | DataTable | + +Подробнее — [Компоненты](components). + +## Инструменты + +- **Storybook** — инженерный стенд для разработки и визуального ревью +- **Docusaurus** — опубликованная документация (этот сайт) + +## ADR + +Архитектурное решение описано в [ADR-016](../adr/ADR-016-design-system). diff --git a/apps/docs/docs/design-system/patterns.md b/apps/docs/docs/design-system/patterns.md new file mode 100644 index 0000000..ecc7418 --- /dev/null +++ b/apps/docs/docs/design-system/patterns.md @@ -0,0 +1,94 @@ +# Паттерны + +## Финансовые данные + +### Money + +Форматирование через `Intl.NumberFormat`: + +- Локаль: `ru-RU` +- Валюта по умолчанию: `RUB` + +```tsx + // 1 234,50 ₽ + // 1 234,50 $ +``` + +### PriceChange + +Отображает изменение цены. Обязательное правило: **никогда не использовать только цвет**. Всегда добавлять текстовый индикатор направления (`+`, `−`, `—`). + +| Направление | Индикатор | Цвет токена | +|-------------|-----------|-------------| +| Рост | `+` | `color.finance.positive` | +| Падение | `−` | `color.finance.negative` | +| Нейтрально | `—` | `color.finance.neutral` | + +### Metric + +Композитный компонент: label + value + опционально supporting (доп. текст) и trend (направление). + +```tsx +} + trend="up" +/> +``` + +## Формы + +### FormField + +Обёртка для поля, связывающая label, helperText и error через ассоциацию `htmlFor`/`id`. + +```tsx + + + +``` + +### FilterBar + +Responsive-панель фильтров с автоматическим переносом (wrap). + +```tsx + +