docs(design-system): publish usage guidelines and ADR-016
This commit is contained in:
parent
53659a36b1
commit
e6cf852741
79
apps/docs/docs/adr/ADR-016-design-system.md
Normal file
79
apps/docs/docs/adr/ADR-016-design-system.md
Normal file
@ -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-адаптеры для нативных приложений.
|
||||||
|
- Расширение компонентной базы.
|
||||||
@ -16,5 +16,7 @@
|
|||||||
| [ADR-012](ADR-012-frontend-broker-account-aggregation) | Accepted | Агрегация сводки брокерских счетов на frontend |
|
| [ADR-012](ADR-012-frontend-broker-account-aggregation) | Accepted | Агрегация сводки брокерских счетов на frontend |
|
||||||
| [ADR-013](ADR-013-frontend-fsd-broker-pilot) | Accepted | Пилотная FSD-миграция broker-домена |
|
| [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-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-разделе.
|
Все опубликованные ADR находятся в `apps/docs/docs/adr/` и отображаются в этом Docusaurus-разделе.
|
||||||
|
|||||||
49
apps/docs/docs/design-system/accessibility.md
Normal file
49
apps/docs/docs/design-system/accessibility.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Доступность (Accessibility)
|
||||||
|
|
||||||
|
## Целевой уровень
|
||||||
|
|
||||||
|
WCAG 2.2 AA.
|
||||||
|
|
||||||
|
## Требования к компонентам
|
||||||
|
|
||||||
|
### IconButton
|
||||||
|
|
||||||
|
**Требует `label`** — свойство `aria-label` обязательно для всех иконок без текста.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<IconButton label="Настройки">
|
||||||
|
<SettingsIcon />
|
||||||
|
</IconButton>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dialog
|
||||||
|
|
||||||
|
- **Требует `title`** — заголовок, используемый как `aria-labelledby`
|
||||||
|
- Содержит focus trap (фокус не покидает диалог)
|
||||||
|
- Закрывается по Escape
|
||||||
|
|
||||||
|
### Progress
|
||||||
|
|
||||||
|
- `indeterminate`-режим выставляет `aria-busy` на контейнере
|
||||||
|
|
||||||
|
### PriceChange
|
||||||
|
|
||||||
|
- Использует **текстовый индикатор направления** (`+`, `−`, `—`), а не только цвет
|
||||||
|
- Скринридеры получают знак перед числом
|
||||||
|
|
||||||
|
### DataTable
|
||||||
|
|
||||||
|
- **Требует `caption`** — описание таблицы для скринридеров
|
||||||
|
- Семантические заголовки через `<th scope="col">`
|
||||||
|
|
||||||
|
### Skeleton
|
||||||
|
|
||||||
|
- `aria-hidden` по умолчанию — скелетоны не должны озвучиваться
|
||||||
|
|
||||||
|
### Alert
|
||||||
|
|
||||||
|
- Использует `role="alert"` для немедленного объявления скринридером
|
||||||
|
|
||||||
|
### LoadingState
|
||||||
|
|
||||||
|
- `aria-live="polite"` — скринридер объявит об изменении после завершения текущей речи
|
||||||
259
apps/docs/docs/design-system/components.md
Normal file
259
apps/docs/docs/design-system/components.md
Normal file
@ -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<T>[]` | — |
|
||||||
|
| `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` | — |
|
||||||
52
apps/docs/docs/design-system/foundations.md
Normal file
52
apps/docs/docs/design-system/foundations.md
Normal file
@ -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).
|
||||||
38
apps/docs/docs/design-system/governance.md
Normal file
38
apps/docs/docs/design-system/governance.md
Normal file
@ -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`).
|
||||||
42
apps/docs/docs/design-system/overview.md
Normal file
42
apps/docs/docs/design-system/overview.md
Normal file
@ -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).
|
||||||
94
apps/docs/docs/design-system/patterns.md
Normal file
94
apps/docs/docs/design-system/patterns.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Паттерны
|
||||||
|
|
||||||
|
## Финансовые данные
|
||||||
|
|
||||||
|
### Money
|
||||||
|
|
||||||
|
Форматирование через `Intl.NumberFormat`:
|
||||||
|
|
||||||
|
- Локаль: `ru-RU`
|
||||||
|
- Валюта по умолчанию: `RUB`
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<Money value={1234.5} /> // 1 234,50 ₽
|
||||||
|
<Money value={1234.5} currency="USD" /> // 1 234,50 $
|
||||||
|
```
|
||||||
|
|
||||||
|
### PriceChange
|
||||||
|
|
||||||
|
Отображает изменение цены. Обязательное правило: **никогда не использовать только цвет**. Всегда добавлять текстовый индикатор направления (`+`, `−`, `—`).
|
||||||
|
|
||||||
|
| Направление | Индикатор | Цвет токена |
|
||||||
|
|-------------|-----------|-------------|
|
||||||
|
| Рост | `+` | `color.finance.positive` |
|
||||||
|
| Падение | `−` | `color.finance.negative` |
|
||||||
|
| Нейтрально | `—` | `color.finance.neutral` |
|
||||||
|
|
||||||
|
### Metric
|
||||||
|
|
||||||
|
Композитный компонент: label + value + опционально supporting (доп. текст) и trend (направление).
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<Metric
|
||||||
|
label="Общая доходность"
|
||||||
|
value={<Money value={12345} />}
|
||||||
|
trend="up"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Формы
|
||||||
|
|
||||||
|
### FormField
|
||||||
|
|
||||||
|
Обёртка для поля, связывающая label, helperText и error через ассоциацию `htmlFor`/`id`.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<FormField label="Название" error="Обязательное поле">
|
||||||
|
<TextField />
|
||||||
|
</FormField>
|
||||||
|
```
|
||||||
|
|
||||||
|
### FilterBar
|
||||||
|
|
||||||
|
Responsive-панель фильтров с автоматическим переносом (wrap).
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<FilterBar>
|
||||||
|
<Select label="Тип" />
|
||||||
|
<TextField label="Поиск" />
|
||||||
|
</FilterBar>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Состояния страницы
|
||||||
|
|
||||||
|
### EmptyState, ErrorState, LoadingState
|
||||||
|
|
||||||
|
Три компонента для трёх состояний любой страницы или секции:
|
||||||
|
|
||||||
|
- **EmptyState** — данных нет, `title` обязателен
|
||||||
|
- **ErrorState** — ошибка загрузки, `title` обязателен, `onRetry` для повтора
|
||||||
|
- **LoadingState** — загрузка, `title` обязателен, `aria-live="polite"`
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
{isLoading && <LoadingState title="Загрузка портфеля" />}
|
||||||
|
{error && <ErrorState title="Ошибка загрузки" onRetry={refetch} />}
|
||||||
|
{data && data.length === 0 && <EmptyState title="Нет операций" />}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Таблицы
|
||||||
|
|
||||||
|
### DataTable
|
||||||
|
|
||||||
|
Обёртка над TanStack Table с обязательным `caption` для доступности. Два режима плотности:
|
||||||
|
|
||||||
|
- **balanced** (по умолчанию) — отступы md
|
||||||
|
- **compact** — отступы sm, для плотных таблиц
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={rows}
|
||||||
|
caption="Список операций"
|
||||||
|
density="compact"
|
||||||
|
/>
|
||||||
|
```
|
||||||
106
apps/docs/docs/design-system/tokens.md
Normal file
106
apps/docs/docs/design-system/tokens.md
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# Семантические токены
|
||||||
|
|
||||||
|
Компонентные токены документированы на страницах соответствующих компонентов.
|
||||||
|
|
||||||
|
## Color
|
||||||
|
|
||||||
|
### Canvas
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `color.canvas` | `{color.neutral.50}` | `#f5f5f5` |
|
||||||
|
|
||||||
|
### Surface
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `color.surface.default` | `{color.neutral.0}` | `#ffffff` |
|
||||||
|
| `color.surface.subtle` | `{color.neutral.50}` | `#f5f5f5` |
|
||||||
|
| `color.surface.raised` | `{color.neutral.0}` | `#ffffff` |
|
||||||
|
|
||||||
|
### Text
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `color.text.primary` | `{color.neutral.900}` | `#1a1a1a` |
|
||||||
|
| `color.text.secondary` | `{color.neutral.600}` | `#666666` |
|
||||||
|
| `color.text.disabled` | `{color.neutral.400}` | `#999999` |
|
||||||
|
| `color.text.inverse` | `{color.neutral.0}` | `#ffffff` |
|
||||||
|
|
||||||
|
### Border
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `color.border.subtle` | `{color.neutral.100}` | `#e0e0e0` |
|
||||||
|
| `color.border.default` | `{color.neutral.200}` | `#cccccc` |
|
||||||
|
| `color.border.strong` | `{color.neutral.400}` | `#999999` |
|
||||||
|
| `color.border.focus` | `{color.blue.600}` | `#1e88e5` |
|
||||||
|
|
||||||
|
### Action
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `color.action.primary` | `{color.green.700}` | `#388e3c` |
|
||||||
|
| `color.action.primaryHover` | `{color.green.800}` | `#2e7d32` |
|
||||||
|
| `color.action.secondary` | `{color.neutral.600}` | `#666666` |
|
||||||
|
| `color.action.danger` | `{color.red.600}` | `#e53935` |
|
||||||
|
|
||||||
|
### Feedback
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `color.feedback.info` | `{color.blue.600}` | `#1e88e5` |
|
||||||
|
| `color.feedback.success` | `{color.green.600}` | `#43a047` |
|
||||||
|
| `color.feedback.warning` | `{color.amber.600}` | `#ffb300` |
|
||||||
|
| `color.feedback.error` | `{color.red.600}` | `#e53935` |
|
||||||
|
|
||||||
|
### Finance
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `color.finance.positive` | `{color.green.700}` | `#388e3c` |
|
||||||
|
| `color.finance.negative` | `{color.red.700}` | `#d32f2f` |
|
||||||
|
| `color.finance.neutral` | `{color.neutral.600}` | `#666666` |
|
||||||
|
|
||||||
|
## Typography
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `font.family.body` | `{font.family.sans}` | `Inter, system-ui, sans-serif` |
|
||||||
|
| `font.weight.body` | `{font.weight.regular}` | `400` |
|
||||||
|
| `font.weight.heading` | `{font.weight.semibold}` | `600` |
|
||||||
|
| `font.weight.strong` | `{font.weight.bold}` | `700` |
|
||||||
|
|
||||||
|
## Spacing
|
||||||
|
|
||||||
|
### Inset (внутренние отступы)
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `space.inset.sm` | `{space.2}` | `8px` |
|
||||||
|
| `space.inset.md` | `{space.3}` | `12px` |
|
||||||
|
| `space.inset.lg` | `{space.4}` | `16px` |
|
||||||
|
|
||||||
|
### Stack (вертикальные отступы)
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `space.stack.sm` | `{space.1}` | `4px` |
|
||||||
|
| `space.stack.md` | `{space.2}` | `8px` |
|
||||||
|
| `space.stack.lg` | `{space.4}` | `16px` |
|
||||||
|
|
||||||
|
### Inline (горизонтальные отступы)
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `space.inline.sm` | `{space.1}` | `4px` |
|
||||||
|
| `space.inline.md` | `{space.2}` | `8px` |
|
||||||
|
| `space.inline.lg` | `{space.3}` | `12px` |
|
||||||
|
|
||||||
|
## Size / Focus
|
||||||
|
|
||||||
|
| Токен | Значение | Разрешение |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `focus.ring.width` | `{size.focus.ring}` | `2px` |
|
||||||
|
| `focus.ring.offset` | `{size.focus.offset}` | `2px` |
|
||||||
|
| `focus.ring.color` | `{color.blue.600}` | `#1e88e5` |
|
||||||
@ -2,7 +2,17 @@
|
|||||||
|
|
||||||
## Подход
|
## Подход
|
||||||
|
|
||||||
CSS через единый `styles.css` с CSS custom properties. Без CSS-in-JS или Tailwind.
|
Управление стилями осуществляется через пакет [дизайн-системы](../design-system/overview) `@moex-vibe/design-system`, который предоставляет:
|
||||||
|
|
||||||
|
- Систему токенов (primitives → semantic → component)
|
||||||
|
- MUI 6.5 тему, построенную на токенах
|
||||||
|
- CSS-переменные `--mv-*` для доступа из обычного CSS
|
||||||
|
|
||||||
|
Все новые компоненты должны использовать токены дизайн-системы. Подробнее — [Дизайн-система](../design-system/overview).
|
||||||
|
|
||||||
|
## Legacy: CSS custom properties
|
||||||
|
|
||||||
|
Ранее стили определялись через единый `styles.css` с CSS custom properties. Этот подход считается устаревшим — новые страницы должны использовать токены дизайн-системы.
|
||||||
|
|
||||||
## CSS custom properties
|
## CSS custom properties
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,19 @@ const sidebars: SidebarsConfig = {
|
|||||||
label: 'Инфраструктура',
|
label: 'Инфраструктура',
|
||||||
items: ['infrastructure/docker', 'infrastructure/ci'],
|
items: ['infrastructure/docker', 'infrastructure/ci'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Дизайн-система',
|
||||||
|
items: [
|
||||||
|
'design-system/overview',
|
||||||
|
'design-system/foundations',
|
||||||
|
'design-system/tokens',
|
||||||
|
'design-system/components',
|
||||||
|
'design-system/patterns',
|
||||||
|
'design-system/accessibility',
|
||||||
|
'design-system/governance',
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
label: 'Разработка',
|
label: 'Разработка',
|
||||||
|
|||||||
11
packages/design-system/CHANGELOG.md
Normal file
11
packages/design-system/CHANGELOG.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 0.1.0 (2026-06-21)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Token system: primitives, semantic (light), and component tokens with DTCG-like resolver
|
||||||
|
- Theme adapter: MUI 6.5 theme with CSS variables (`--mv-*`)
|
||||||
|
- Components: 20+ production-ready wrappers (Typography, Actions, Inputs, Surfaces, Feedback, Financial Data, Form, Page States)
|
||||||
|
- Storybook: interactive catalog with a11y checks
|
||||||
|
- First stable public API
|
||||||
Loading…
x
Reference in New Issue
Block a user