usluga-klininga/design-system/docs/DESIGN.md

309 lines
41 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.

# KT AI Design System v3.0 «Mono» (токены v5)
Канон дизайн-системы KT AI. v3.0 от 2026-06-21 – визуальный язык в духе ElevenLabs: **light-first**, тёплый near-white фон, **near-black primary-кнопка** (в dark инвертируется в near-white), плоские поверхности на hairline-границах и шёпот-тенях, крупные жирные тёмные заголовки, цвет – только функциональный (статусы, чарты, единичная AI-искра орба Төре). Синий **демотирован** в ссылки/фокус/идентичность KT, перестал быть цветом primary-действия. Геометрия и Attio-детали (rounded-rect теги, frameless-таблицы с горизонтальными разделителями рядов — без вертикальных колонок-сепараторов, property-rows) сохранены; aurora-свечение карточек и тяжёлые AI-ореолы убраны. История: CHANGELOG.md. Анатомия shell/таблиц/рядов: COMPONENTS.md. Продуктовый контракт прототипа: PRODUCT_CONTRACT.md.
Файлы:
| Файл | Роль |
|---|---|
| `tokens.json` | ЕДИНСТВЕННЫЙ источник значений токенов. Правки только здесь |
| `scripts/build_tokens.py` | генератор: tokens.css, print.css, figma-variables.json, tailwind-preset |
| `kt-ai-tokens.css` | AUTOGENERATED: переменные, обе темы, @layer kt-tokens |
| `kt-ai-print.css` | AUTOGENERATED: @media print (light, A4, скрытие shell) |
| `figma-variables.json` | AUTOGENERATED: импорт в Figma Variables (dark/light) |
| `kt-ai-tailwind-preset.cjs` | AUTOGENERATED: preset для Next.js/kt-ai-shadcn |
| `kt-ai-components.css` | компоненты v2 на токенах, имена классов совместимы с v1 |
| `COMPONENTS.md` | нормативная анатомия shell, таблиц, рядов, тулбаров (перенос из v1-архива) |
| `showcase.html` | живая галерея всех компонентов с переключателем тем – открыть в браузере |
| `icons/kt-ai-lucide-sprite.svg` | локальный monochrome Lucide-sprite |
| `COMPONENTS.md` | правила компоновки экрана (активно) |
| `ELEVENLABS_DESIGN.md` | эталон эстетики/плотности/деталей (язык ElevenLabs) |
| `archive/` | замороженный v1 (kt_design_system, DESIGN_v1_archive, kt_ai_terminal_tokens.css, kt_ai_terminal_routes, kt_ai_atomic_product_system) – только история, вынесен в `08_Дизайн-система_история/archive/` |
Брендовые ассеты: `03_База знаний/Логотипы и бренд/` (ktai.svg, kt-logo.svg).
## Принципы
Полный контракт UX-принципов (Нильсен, гештальт, законы взаимодействия): `PRINCIPLES.md`. Единый чек-лист «прошло или нет» (Definition of Done, прогонять при любом изменении UI): `CHECKLIST.md`. Оба обязательны при проектировании.
1. Плотный операционный интерфейс: первый экран – рабочая поверхность, не лендинг.
2. Плоские поверхности на hairline-границах: в light – лёгкая тень (`card-shadow`/`shadow-sm`) + рамка, в dark – рамка без тени. Полновесные тени – только у overlay (modal, drawer, dropdown, command, toast). Никаких декоративных свечений/градиентов в контенте. Blur – только на sticky-хроме (топбар `bg-glass`) и overlay.
3. Тихие границы, приглушённые метаданные, один primary-акцент на экран.
4. **Mono-доминанта.** Primary-действие – монохром: `primary` = near-black (light) / near-white (dark), белый/тёмный текст. Синий (`link`, `focus`, `brand-blue`) – ссылки, фокус и идентичность KT, **не** заливка кнопок. AI-градиент (`ai-gradient`) – единичная сдержанная искра только на орбе Төре, send-кнопке и кромке prompt-bar в фокусе; тяжёлые ореолы и aurora-свечение карточек убраны. Остальные цвета – только статусы, чипы и графики.
5. Геометрия компонентов не зависит от темы: темы меняют только цвет.
6. Никаких сырых hex в продуктовом CSS – только semantic-токены.
## Архитектура токенов
Три уровня, имена v1 (`--kt-ai-*`) полностью сохранены – старые продукты работают без правок.
1. **Primitives** (`--kt-blue-500`, `--kt-gray-900`, `--kt-green-a22`...) – сырая палитра и шкалы. В продуктах напрямую НЕ используются.
2. **Semantic** – смысловые роли, переключаются темой:
- поверхности: `--kt-ai-bg`, `-bg-soft`, `-bg-elevated`, `-bg-sunken`;
- state-слои: `-bg-hover`, `-bg-active`, `-selection`;
- текст: `-fg`, `-fg-muted`, `-fg-faint`, `-fg-on-fill`;
- границы: `-border`, `-border-strong`, `-divider`;
- интерактив: `-primary`, `-primary-hover`, `-primary-subtle`, `-link`, `-danger`, `-danger-hover`;
- фокус: `-focus-ring`, `-focus-shadow`;
- статусы/чипы/чарты: как в v1;
- тени: `-shadow-sm/md/lg/xl`.
3. **Component** – размеры контролов: `--kt-ai-control-h-sm/md/lg` (28/32/40), layout-переменные v1.
Тема-независимые шкалы (в `:root`):
- **Spacing** `--kt-ai-space-1..12`: 2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64. Всё на 4px-сетке, плотный низ шкалы.
- **Типографика** `--kt-ai-text-2xs..3xl`: 11, 12, 13, 14, 15, 18, 26, 32. Body = 13px (`text-sm`). Веса 400/500/600/700. Числа – JetBrains Mono с `tabular-nums`.
- **Трекинг зависит от размера** (не одно значение на всё): крупные заголовки — отрицательный (`-.02em` дисплей, `-.01em` H1/H2), body — около `0`. Тесним заголовки, body оставляем нейтральным.
- **Leading обратно размеру:** плотный на крупных заголовках (`leading-tight` 1.25), свободнее на body (`leading-normal` 1.5). Иерархия строится связкой вес+размер+leading, а не размером одним.
- **Радиусы** (значения — в `tokens.json`, `scales.radius`): xs, sm, md, tag, lg, xl, 2xl, 3xl, full. Nav-ряды lg; кнопки/инпуты xl; карточки, modal, command, prompt-bar – 3xl; теги/статусы – tag (rounded-rect, не pill).
- **Моушн** `--kt-ai-dur-fast/base/slow/slower` (100/160/240/400ms), easing standard/enter/exit. `prefers-reduced-motion` обнуляет длительности автоматически.
- **Z-index**: sticky 20, dropdown 30, drawer 40, modal 50, toast 60, tooltip 70.
## Темы
Light – дефолт (`:root`, `data-theme="light"` или `kt-ai-terminal-light`): тёплый near-white, near-black primary – основной офисный вид (v3 «Mono», light-first). Dark – полноправный паритетный вариант (`data-theme="dark"` или `kt-ai-terminal`): near-black фон, primary инвертируется в near-white. `data-theme="auto"` следует за системной темой.
Правила паритета:
- каждый semantic-токен определён в обеих темах; добавил токен в dark – обязан добавить в light;
- интерактивный синий: dark `#4b9ce2`, light `#0077c8` (AA-контраст на своих фонах); фирменный KT blue `#0096d7` общий;
- статусные пары построены зеркально: в dark светлый текст на прозрачной подложке, в light тёмный текст на прозрачной подложке;
- проверка паритета – переключателем в `showcase.html`, это обязательный QA-шаг.
## Компоненты (kt-ai-components.css)
Имена классов v1 сохранены (`.kt-ai-card`, `.kt-ai-row`, `.kt-ai-chip`, `.kt-ai-nav-item`, `.kt-ai-table*`, `.kt-ai-search`, `.kt-ai-kbd`, `.kt-ai-icon-button`, `.kt-ai-segmented`...). Новое в v2:
- **Кнопки** `.kt-ai-btn`: варианты `data-variant="primary|ghost|danger"` (без атрибута – вторичная), размеры `data-size="sm|lg"`. Один primary на экран.
- **Формы**: `.kt-ai-field` (label + контрол + hint/error), `.kt-ai-input`, `.kt-ai-select`, `.kt-ai-textarea`, `data-invalid="true"`, `.kt-ai-checkbox`, `.kt-ai-switch`. Фокус: border primary + `--kt-ai-focus-shadow`.
- **Tabs** `.kt-ai-tabs/.kt-ai-tab` (`data-active`), сегменты как в v1.
- **Banner** `.kt-ai-banner data-tone="info|warn|risk|ok"` – строчные предупреждения в контенте.
- **Modal** `.kt-ai-modal-overlay/.kt-ai-modal` – bg-elevated, radius-2xl, shadow-xl, анимация rise.
- **Toast** `.kt-ai-toast-stack/.kt-ai-toast data-tone` – нижний правый угол, точка-индикатор тона.
- **Tooltip** `.kt-ai-tooltip[data-tip]` – чистый CSS.
- **Skeleton** `.kt-ai-skeleton` (shimmer), **Empty** `.kt-ai-empty` (иконка + title + действие).
- **KPI** `.kt-ai-kpi-strip/.kt-ai-kpi` – тихая метрик-полоса, не карточки.
- **Avatar**, `.kt-ai-divider`, `.kt-ai-status-pill` с точкой-индикатором.
- **Drawer** `.kt-ai-drawer` – правая панель деталей (правило: детали в drawer, не на новой странице).
- **Документ-артефакт** `.kt-ai-doc` / `.kt-ai-doc-list` – карточка выходного документа (иконка + имя + статус/формат + кнопка «Скачать»). То, что продукт ПРОИЗВОДИТ (ТС, отчёт, СЗ), показывается как скачиваемый документ — в drawer (секция «Документы» из `_docs` строки) и в nav-секции-коллекции (`nav.view="documents"`, фильтр по `docType`). Контракт: `table.rows[]._docs`.
Новое в v2.3 «Aurora × Attio»:
- **Карточки** – `card-bg/card-border/card-shadow` токены: в dark – hairline-рамка + фиолетовое aurora-свечение из угла (`data-glow="strong"` – усиленный hero-вариант с двойным свечением), в light – белая поверхность с тенью без рамки. Радиус — `radius-3xl`.
- **Кнопки (Attio)** – secondary: поверхность bg-elevated + рамка + shadow-sm, радиус 8; ghost/danger без поверхности.
- **Теги (Attio)** – chip/status-pill/filter-chip: rounded-rect `radius-tag` (5px), не pill.
- **Таблицы (ElevenLabs)** – дефолт frameless full-bleed: hairline под шапкой и между рядами, **без вертикальных колонок-сепараторов** (тихо, воздушно), заголовки sentence-case 12px medium muted; контейнерный вариант – `data-framed="true"`. Для стабильности ширин при фильтрации фильтруемым таблицам задаётся `table-layout:fixed` + colgroup.
- **Property-row** `.kt-ai-prop` (`.k` – иконка+метка 148px, `.v` – значение, `data-empty`) – детали объекта в drawer/карточке.
- **AI-aurora** – токены `ai-accent`, `ai-gradient` (синий → фиолет): градиентная кромка prompt-bar в фокусе + ореол, градиентные send-кнопка и streaming-каретка, орб-атрибуция вместо ✦, `.kt-ai-btn[data-ai="true"]` – точка входа AI. Градиент НЕ используется вне AI-моментов.
- **Тихая ссылка** `.kt-ai-link[data-arrow]` – «Подробнее →», стрелка сдвигается на hover.
- **Dot-grid** текстура на `.kt-ai-empty`. Mono-голос: label, cmd-group, время уведомлений, ключи filter-chips. Активный таб – синий. Трекинг заголовков -.02em/-.01em. Sidebar-ряды 26px.
Новое в v2.2:
- **Topbar** `.kt-ai-topbar` – канонический glass-хром: sticky, `bg-glass` + blur(8). Blur разрешён только здесь и на overlay.
- **AI-диалог**: `.kt-ai-chat` + `.kt-ai-msg data-role="user|assistant"` (ассистент – полная ширина без пузыря, пользователь – пузырь bg-soft ≤76%), `data-streaming="true"` – каретка; `.kt-ai-prompt-bar` (autogrow textarea + `.kt-ai-prompt-send`, `data-busy`), `.kt-ai-suggestions/.kt-ai-suggestion` – стартовые подсказки, `.kt-ai-source-pill` – ссылка-источник в ответе, `.kt-ai-gen-label` – атрибуция «Сгенерировано ИИ» (обязательна для AI-контента).
- **Menu** `.kt-ai-menu-anchor > .kt-ai-menu[data-open]` – dropdown действий (kebab в рядах таблиц): `.item` (`data-danger`), `.sep`.
- **Command palette** – достроена: `.kt-ai-command-overlay`, `.cmd-input`, `.cmd-list`, `.cmd-group`, `.cmd-item[data-active]`, `.cmd-empty`.
- **Filter-chips** `.kt-ai-filterbar` + `.kt-ai-filter-chip` (`.k` – имя фильтра, `.remove`) + `.clear-all` – применённые фильтры над таблицей.
- **Radio** `.kt-ai-radio`, `.kt-ai-radio-group`.
- **Toast**: `.action` (undo-ссылка) и `.close`.
- **Popover** `.kt-ai-popover-anchor > .kt-ai-popover[data-open]` (`data-align="right"`) – якорный интерактивный слой (date-picker, настройки колонок).
- **Bulk-bar** `data-floating="true"` – плавающий вариант снизу по центру для длинных таблиц.
- **Row actions** `.kt-ai-row .actions` / `.kt-ai-table tr .actions` – тихие действия, видимы на hover/focus-within/selected; на touch видимы всегда.
- Активный пункт навигации получает 2px-акцент слева (работает и в icon-rail).
Анатомия shell, таблиц, рядов – COMPONENTS.md (перенесено из v1-архива, нормативно).
## Operational Dashboard Contract
- Boilerplate задаёт обязательный shell: sidebar, `.kt-ai-topbar`, theme toggle и AI-кнопку Төре. Эти элементы нельзя перерисовывать, заменять самодельными глифами или делать декоративными. Main content проектируется под задачу процесса.
- Для рабочих очередей, реестров и консолидации закупок предпочтителен classic app-shell: таблица как главный объект, детали в drawer, короткие workflow-status, одно primary-действие. Custom workspace, матрица, split-view или decision panel используются только когда таблица + drawer не решают основную работу пользователя.
- H1/topbar title – короткий рабочий объект, 1-3 слова. Не использовать описание процесса как заголовок.
- Subtitle – одна короткая scope-фраза до 90 символов.
- KPI показывают рабочее состояние текущего пользователя: новые, требуют решения, риски, ждут документы, просрочено, закрыто сегодня, SLA или сумма риска. ROI, FTE, экономия часов и тенге эффекта не выводятся на пользовательский dashboard по умолчанию. Эти метрики относятся к management/reporting view, паспорту или расчёту эффекта проекта. Исключение – операционный экран консолидации, где пользователю нужны рабочие числа для решения: сколько ТС обработано, сколько имеют потенциал консолидации, какой предварительный потенциал экономии в тенге.
- Search, theme toggle, AI button and visible primary actions must work in the prototype when shown. Search inputs are empty by default and must not be prefilled with arbitrary examples.
- Primary filters – только workflow-status, максимум 4 пункта плюс `Все`. Confidence, risk type, системы и длинные причины не попадают в default segmented filters.
- Workflow `status`, `confidence` и `riskType` моделируются разными полями.
- Labels компактные: filter до 2 слов, status до 4 слов, table header до 2 слов; детали и длинные объяснения – в drawer/help.
- UI-copy не показывает внутреннюю кухню генерации: `модель вернула`, `fallback`, `не проходит дизайн-систему`, `shell`, `JSON` и похожие формулировки запрещены в пользовательском интерфейсе.
- **Голос интерфейса нейтрален и продуктовый.** Запрещены: дисклеймеры прототипа («это прототип на дизайн-системе», «данные иллюстративны»); нравоучения про human-in-the-loop («AI не действует на клиента сам», «решение подтверждает человек», «контрольная точка») — человек-в-контуре показывается контролами (рекомендация + кнопки accept/reject), а не объяснениями; лозунг-лейблы капсом («РЕШЕНИЕ — ЗА ВАМИ») — секции называются нейтральными существительными («Решение»). Любой продукт-специфичный текст (что сделал агент, разбор) живёт в контракте (`scenario.aiSummary`), а не зашит в shell — иначе он протекает в другие продукты. Валидатор ловит эти формулировки (`PREACHY_LANG`).
## Состояния интерактива
Единая лестница: default → hover (`-bg-hover` или border-strong) → active/selected (`-bg-active`) → focus-visible (outline `-focus-ring`) → disabled (opacity .4). Hover-переходы `dur-fast`, появления `dur-base`, drawer/modal `dur-slow`. Нажатие кнопки: `scale(.98)` 60ms.
## Движение (motion)
Правила — не украшение, а поведение. Каждое имеет причину.
- **Длительности по типу:** микро/hover 100–150мс (`dur-fast` 100), обычное появление/смена 150–250мс (`dur-base` 160), overlay/drawer/modal ~240мс (`dur-slow`). Дольше 300мс движение начинает раздражать; короче — пропадает читаемость перехода.
- **Появляйся от `scale(.95)`, не от `scale(0)`** (и не от `opacity:0` в одиночку): элемент «подрастает» из своего места, а не возникает из ниоткуда. Уводи так же — до `.95`, не в `0`.
- **Отклик на нажатие, не на отпускание:** подсветка/`scale(.98)` на pointer-down мгновенно; коммит — на pointer-up. Лаг убивает ощущение прямоты.
- **Переходы прерываемы и обратимы:** закрывающийся drawer можно открыть на полпути. Анимируй от ТЕКУЩЕГО значения (presentation), а не от целевого — иначе «прыжок».
- **Вход и выход по одному пути; overlay привязан к источнику:** меню/popover растут из кнопки (`transform-origin` = триггер), drawer уходит туда, откуда пришёл. На реверсе — зеркальный easing (`ease-enter` ↔ `ease-exit`).
- **Анимируй только `transform` и `opacity`** (композиторные, без ре-лейаута). Не анимируй `width/height/top/left/box-shadow` — дёргается кадр.
- **`prefers-reduced-motion`** обнуляет длительности автоматически (токены + компоненты); оставляем только opacity/цвет, что помогают понять переход.
## Иконки
Lucide из локального sprite, монохром через `currentColor`, stroke 1.75. Размеры 16 (база), 14 (плотные ряды), 20 (крупные). Без CDN-шрифтов и CSS-псевдоиконок. Правила и список имён – v1-архив.
## Доступность и QA
- Контраст: основной текст ≥ 7:1, muted ≥ 4.5:1, faint только для необязательных подписей.
- Фокус видим всегда: глобальный `:focus-visible` в components.css.
- `prefers-reduced-motion` поддержан на уровне токенов (обнуляет длительности).
- `prefers-contrast: more`: толще фокус-кольцо, границы = `border-strong`, ссылки подчёркнуты, обводка у пилюль.
- `prefers-reduced-transparency: reduce`: blur на sticky-топбаре снимается, фон становится плотным (легибельность > эффект).
- Чек-лист перед сдачей экрана — единый гейт `CHECKLIST.md` (Definition of Done): обе темы, состояния loading/empty/error, один primary, нет сырых hex, адаптив, паритет, валидатор 0/0. Прогоняется при любом изменении UI, не только при сдаче нового экрана.
## Подключение
```html
<link rel="stylesheet" href="kt-ai-tokens.css">
<link rel="stylesheet" href="kt-ai-components.css">
<body class="kt-ai-app" data-theme="dark">
```
Для Next.js-прототипов – кит `templates/kt-ai-shadcn` (**внутри папки ДС**, рядом с app-shell — ДС одна самодостаточная папка; переменные совместимы, источник значений – kt-ai-tokens.css; установка – его `templates/kt-ai-shadcn/PROTOTYPING_WORKFLOW.md`).
**Два рантайма одного контракта.** HTML app-shell (`templates/kt-ai-app-shell.html`) — демо-рантайм с роутингом разделов: рендерит nav-секции (`view="documents"`, мастер `steps`), баннер-навигацию (`banner.actionNav`), коллекции документов. React-кит — **одноэкранный handoff для разработки**: рендерит главный архетип-экран (queue/dashboard/…) + drawer (включая секцию «Документы» из `_docs`), но НЕ роутит вторичные nav-секции — это намеренно (их реализует разработчик под свой роутер). Поэтому `nav.view`, `steps`, `banner.actionNav` — поведение HTML-рантайма; в React это точки расширения.
## Миграция v1 → v2
1. Заменить подключение `archive/kt_ai_terminal_tokens.css` на пару `kt-ai-tokens.css` + `kt-ai-components.css`. Все старые переменные и классы продолжают работать.
2. Hardcoded-размеры и цвета постепенно переводить на шкалы (`--kt-ai-space-*`, `--kt-ai-text-*`, semantic-цвета).
3. Новые компоненты (кнопки, формы, modal, toast, banner, skeleton, empty) не писать заново – брать из components.css.
4. Паритет тем проверять в showcase.html.
## Ритм отступов (правило близости)
`space-*` – сырая шкала для внутренних отступов компонента. **Расстояние МЕЖДУ блоками экрана берётся из отдельной шкалы `stack-*`** – одно значение на роль, а не решение каждого компонента:
| Токен | Значение | Когда |
|---|---|---|
| `--kt-ai-stack-tight` | 12px | внутри группы: подпись ↔ контрол, контрол ↔ контрол в одном ряду |
| `--kt-ai-stack-block` | 16px | между элементами одного блока; полоса карточек KPI; панель управления ↔ то, чем она управляет |
| `--kt-ai-stack-group` | 24px | **между блоками экрана**: шапка / баннер / KPI / чарт / таблица. Значение по умолчанию |
| `--kt-ai-stack-region` | 32px | поля рабочей области, топбар ↔ заголовок |
| `--kt-ai-pad-card` | 20px | интерьер карточки/секции |
Почему шкала, а не `space-*` напрямую: раньше каждый компонент выбирал отбивку сам, и на одном экране получалось пять разных значений для одного и того же отношения «блок ↔ блок» (8/12/16/20/24). Глаз не читает такую разницу как иерархию – он читает её как небрежность, и экран выглядит захламлённым при формально верных токенах. Ровно так разошлись два рантайма: HTML-шелл давал 16/20, React-кит 24/28.
Правило разрешения споров прежнее: если сомневаешься, к какой группе элемент относится визуально – он стоит неправильно. Секционный разрыв (40+, `space-10`) остаётся для смены смысла страницы, а не для соседних блоков одного экрана.
### Одинаковый зазор — не ритм
Шкала не означает «поставь `stack-group` на всё». Ровно так экран становится плоским: панель фильтров, её вкладки и карточки списка расходятся на одно и то же расстояние, и глаз не видит ни одной группы — только столбец одинаково далёких полос. Разнобой читается как небрежность, равномерность — как «здесь ничто ни с чем не связано». Обе ошибки одинаково дорогие.
**Рецепт экрана — три ступени, не одна:**
```
шапка страницы ← 24 (stack-group): регион
системный баннер ← 24 (stack-group): регион
┌ панель управления ← 12 (stack-tight): фильтры и вкладки — ОДНА вещь
│ фильтры
│ вкладки
└ ← 16 (stack-block): панель ближе к тому, чем управляет
┌ содержимое
│ KPI · чарт · карточки списка ← 16 между блоками, 12 между однородными карточками
└
```
Выражается стеком, а не своим классом продукта: `<div class="kt-ai-v" data-gap="group|block|tight|region">`. Если продукту понадобился собственный `app-stack-*` — это сигнал, что группировку не выразили, а обошли.
**Проверка одной фразой:** пройди по вертикали и назови каждый зазор. Если все числа разные — сломана согласованность. Если все одинаковые — сломана группировка. Правильно, когда чисел ровно столько, сколько на экране уровней вложенности.
## Отзывчивость
Брейкпоинты: sm 640 / md 832 / lg 1024 / xl 1280 (`--kt-ai-bp-*`; в media-запросах значения дублируются числом – var() там недоступен).
Shell – три состояния (реализовано в components.css):
1. ≥ lg: полный sidebar 232px.
2. md..lg: icon-rail 52px – подписи прячутся; в разметке подписи пунктов оборачивать в `<span class="nav-label">`.
3. < md: sidebar = overlay-drawer (`data-open="true"`), в топбаре burger; drawer документов – full-screen.
Touch (`pointer: coarse`): токены контролов автоматически растут (sm 36, md 44, lg 48, nav-row 40, table-row 52) – компоненты масштабируются сами, ничего переопределять не нужно.
Мобильный топбар: на < sm в топбаре остаются логотип (без текста), статус-чип с ellipsis и одно главное действие; текстовые кнопки прячутся или сворачиваются в иконки; элементы с flex-shrink:0 допустимы только для иконок и тумблера. Бургер для off-canvas панелей – иконка `menu` из спрайта (три линии), не самодельные глифы. Горизонтальный скролл страницы запрещён и гасится системой на < md (overflow-x hidden на .kt-ai-app/.kt-ai-shell).
Таблицы: горизонтальный скролл внутри `table-wrap`; первая колонка может быть sticky (`.kt-ai-sticky-first`); при < md и более 4 колонок – переключаться на список карточек.
## API компонентов (enum-контракт)
| Компонент | Атрибут | Значения |
|---|---|---|
| `.kt-ai-btn` | data-variant | (нет) = secondary, `primary`, `ghost`, `danger` |
| `.kt-ai-btn` | data-size | (нет) = md, `sm`, `lg` |
| `.kt-ai-btn` | data-loading | `true` – спиннер, клики заблокированы |
| `.kt-ai-chip` | data-tone | `gray`, `green`, `orange`, `blue`, `purple`, `red` |
| `.kt-ai-chip` | data-removable | присутствие + `<span class="remove">×</span>` |
| `.kt-ai-status-pill` | data-status | `ok`, `warn`, `risk`, `info` |
| `.kt-ai-banner`, `.kt-ai-toast` | data-tone | `ok`, `warn`, `risk`, `info` |
| `.kt-ai-input/.kt-ai-textarea` | data-invalid | `true` |
| `.kt-ai-field` | data-invalid | `true` — красит вложенный input/select/textarea (удобно, когда состояние живёт на поле) |
| `.kt-ai-table` | data-density | (нет) = 48px, `compact`/`dense` 38, `relaxed` 56 (значения из `tokens.json`) |
| `.kt-ai-table th` | data-sort | присутствие = сортируемая, `asc`, `desc` |
| `.kt-ai-step-item` | data-state | (нет) = upcoming, `current`, `done` |
| `.kt-ai-combobox` | data-open | `true` – меню видно |
| `.kt-ai-table td` | data-label | подпись ячейки в мобильном card-режиме (≤640px). Без неё значение просто выравнивается влево |
| `.kt-ai-switch-row` | — | готовая строка «подпись + свитч справа». Сам `.kt-ai-switch` — только трек 34px |
| `.kt-ai-modal` | — | ОБЯЗАТЕЛЬНО внутри `.kt-ai-modal-overlay` — оверлей и есть центрирующий контейнер (`position: fixed` + flex). Соседями они не работают: модалка встанет в поток и уедет со скроллом |
| `.kt-ai-scroll-locked` | — | на `<html>`, пока открыт модал/drawer: иначе фон скроллится под оверлеем. Снимать при закрытии |
| `.kt-ai-disclosure` | data-open | `true` — раскрыт. Карточка, разворачивающаяся НА МЕСТЕ. Для очередей, где объекты сравнивают: drawer закрывает список и заставляет открывать по одному. Части: `-head` (кнопка, `aria-expanded`), `-chevron`, `-body`, `-actions` |
| `.kt-ai-kpi` | — | подпись всегда над значением, порядок `.label`/`.value` в разметке любой |
| `.kt-ai-combobox .option` | data-selected, data-active | `true` |
| `.kt-ai-dropzone` | data-drag | `true` – состояние перетаскивания |
| `.kt-ai-table-bulkbar` | data-open | `true` |
| `.kt-ai-sidebar` | data-open | `true` (только < md, overlay) |
| `.kt-ai-notif-item` | data-unread | `true` |
| `.kt-ai-theme-toggle` | data-mode | `dark`, `light` – бегунок и подсветка иконки |
| `.kt-ai-diff .line` | data-op | `add`, `del` |
| nav-item, tab, segmented .seg, pagination .page-btn | data-active | `true` |
| `.kt-ai-msg` | data-role | `user`, `assistant` |
| `.kt-ai-msg` | data-streaming | `true` – каретка генерации |
| `.kt-ai-prompt-bar` | data-busy | `true` – ввод заблокирован на время генерации |
| `.kt-ai-menu`, `.kt-ai-popover` | data-open | `true` |
| `.kt-ai-menu .item` | data-danger | `true` – деструктивный пункт |
| `.kt-ai-popover` | data-align | (нет) = left, `right` |
| `.kt-ai-table-bulkbar` | data-floating | `true` – плавающий снизу |
| `.kt-ai-command .cmd-item` | data-active | `true` |
| `.kt-ai-card` | data-glow | `strong` – усиленное aurora-свечение (hero/AI) |
| `.kt-ai-btn` | data-ai | `true` – градиентная AI-кнопка (точка входа Төре) |
| `.kt-ai-table-wrap` | data-framed | `true` – контейнер с рамкой (дефолт – frameless) |
| `.kt-ai-link` | data-arrow | присутствие – тихая ссылка «… →» |
| `.kt-ai-prop .v` | data-empty | `true` – незаполненное значение |
| `.kt-ai-kpi-strip` | data-cards | `true` – сетка стат-карточек (radius-2xl, card-токены) вместо тихой полосы |
## Локализация
- Казахские строки длиннее русских на ~20%: запрещены фиксированные ширины под текст кнопок, чипов, пунктов меню; truncation с ellipsis + tooltip полного значения.
- Числа: тысячи пробелом (`1 240`), тенге после числа (`1 240 ₸`), числа в таблицах — **sans + `tabular-nums`** (цифры Inter выровнены по ширине; в JetBrains Mono нет глифа ₸, из-за чего валюта подставлялась из другого шрифта и рвала колонку). Моноширинный остаётся только у `.kt-ai-kbd` и командных групп.
- Даты: `dd.mm.yyyy`, время `hh:mm`, относительные («2 мин», «вчера») только в уведомлениях и лентах.
## Чарты
Максимум 6 серий на график (порядок: chart-blue, green, orange, salmon, pink, red). Оси и подписи – fg-faint, сетка – divider. Заливка под линией – только primary-subtle и только для одной серии. Без 3D, теней и градиентов в данных. Пустое состояние графика – kt-ai-empty, не пустые оси.
## Печать и экспорт
Подключить `kt-ai-print.css` (генерируется из tokens.json): при печати принудительно light-палитра, скрываются shell/кнопки/тосты, формат A4 с полями 18/16 мм, карточки и таблицы не разрываются. Элементы, которые не должны попасть в PDF, помечать `data-no-print`.
## Иконочные кнопки и деструктивные действия
Icon-only кнопка обязана иметь tooltip (`.kt-ai-tooltip[data-tip]`) и aria-label. Деструктивные действия: никогда не icon-only; подтверждение в modal; кнопка подтверждения – глаголом действия («Удалить агента», «Отправить в Реестр»), никогда «Да»/«ОК»; для необратимых операций – ввод имени объекта.
## Правило двух продуктов
Новый компонент попадает в систему только когда он понадобился второму продукту. До этого живёт в продукте. Это держит систему маленькой и честной.
## Визуальная регрессия
`scripts/visual_check.mjs` (Playwright): скриншоты showcase в обеих темах и трёх ширинах (1280/900/600), сравнение с эталонами в `scripts/__screenshots__/`. Запуск локально: `npx playwright install chromium && node scripts/visual_check.mjs`. Прогонять при любом изменении tokens.json или components.css.
## Do / Don't
- Do: ряды вместо крупных карточек; таблица – главный объект экрана; детали в drawer; метрики тихой полосой (в дашбордах/кабинетах с 2-4 главными метриками – `data-cards="true"`); primary-действие монохромное (near-black/near-white); кнопки create/«Новый X» – с ведущим «+»; один фильтрующий поиск на экран; AI-кнопка Төре – в топбаре рядом с поиском; продукт-специфичный текст – в контракте, не в shell.
- Don't: карточки в карточках; декоративные градиенты, свечения и blur в контенте (исключения: единичная AI-искра орба/send/prompt-bar, blur на sticky-хроме и overlay); синий как заливка primary-кнопки; больше одного primary; цвет hex напрямую; новые цвета/иконки/геометрия под отдельный продукт; разная геометрия в темах; дисклеймеры/нравоучения в UI; два одинаково фильтрующих поиска; лишний input/селект там, где хватает прямой кнопки.