diff --git a/.gitignore b/.gitignore index 827f49d..269f090 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ .codex .env .env.local +.figma-token playwright-report/ test-results/ diff --git a/PROJECT_NOTES.md b/PROJECT_NOTES.md index bda11bb..f6301a2 100644 --- a/PROJECT_NOTES.md +++ b/PROJECT_NOTES.md @@ -16,11 +16,11 @@ 2. Компоненты, общие для нового и старого наборов. 3. Компоненты из `gnexus-ui-kit`, отсутствующие в новом дизайне, — перерисовать в новом стиле. 4. Новые компоненты, есть только в новом наборе. +- **Figma-токен:** сохранён вне репозитория (memory-файл), в `.gitignore` добавлен `.figma-token`. ## Ссылки -- Figma-макет: `vmk-ui-kit-figma.url` → https://www.figma.com/design/Wh7d0fPgbkslCoyQmO9DC4/%E2%9D%96-Oselya.ai?node-id=40002541-29833&p=f - - На текущий момент файл недоступен для публичного WebFetch (403). Нужна публичная шара, Figma REST token, или ручная выгрузка токенов/экранов. +- Figma-макет: `vmk-ui-kit-figma.url` → https://www.figma.com/design/scEx4ahLAQbx9akCuttfQC/Oselya.AI_Copy?m=auto&t=M7v0vsNdgVY308YR-1 - Reference kit: `/home/gmikcon/Projects/gnexus-ui-kit-reference` (read-only clone). ## Текущее состояние @@ -28,29 +28,28 @@ ### Что сделано - Созданы `package.json`, `gulpfile.js`, `.gitignore`. -- Добавлены npm-скрипты: `build`, `build:vue`, `dev`, `start`, `test:e2e`, `test:vue-adapter`, `build:example:vue`. -- Playwright добавлен в devDependencies. +- Добавлены npm-скрипты: `build`, `build:vue`, `dev`, `start`, `test:e2e`, `test:visual`, `test:vue-adapter`, `build:example:vue`. +- Playwright добавлен в devDependencies; Chromium установлен; smoke test проходит. +- Создана структура исходников и документации. +- **Токены из Figma вытащены и применены:** + - `src/scss/_palette-colors.scss` — primitive scales (neutral, lavender, mint, yellow) и semantic primitives (danger, info, success, warning). + - `src/scss/_design-tokens.scss` — semantic surfaces, borders, motion, radius placeholders, focus. + - `src/scss/_fonts.scss` — Plus Jakarta Sans + полная типографическая шкала. + - `docs/style-guide.md` — человекочитаемая документация по токенам. +- Проверены команды: + - `npm run build` — проходит. + - `npm run build:example:vue` — проходит. + - `npm run test:e2e` — проходит. ### Что нужно сделать -1. Подключить Figma-макет (получить токены). -2. Создать структуру исходников: - - `src/scss/` — токены, утилиты, компоненты. - - `src/js/` — browser JS entry и UI-компоненты. - - `src/vue/` — Vue 3 adapter. - - `demo/` — demo/docs страница и HTML partials. - - `public/assets/` — шрифты, иконки, изображения. -3. Создать начальную документацию в `docs/`: - - `index.md` — обзор и quick start. - - `getting-started.md` — подключение. - - `vue.md` — Vue adapter. - - `style-guide.md` — design tokens и принципы. - - `release.md` — проверки перед релизом. - - `component-coverage.md` — карта компонентов. -4. Настроить Playwright конфиг (`playwright.config.js`) и первый визуальный тест. -5. Создать Vue example в `examples/vue/` для smoke build. -6. Реализовать foundation: типографику, палитру, spacing, токены. -7. Реализовать базовые компоненты (button, input, badge и т.д.) и их Vue-адаптеры. +1. Уточнить radius-токены через Playwright-сравнение с компонентными фреймами. +2. Найти spacing-токены в Figma (если есть отдельная страница) или зафиксировать свою шкалу. +3. Подключить реальные шрифты (Plus Jakarta Sans) и Phosphor Icons в `public/assets/`. +4. Реализовать foundation-компоненты demo: Typography, Palette, Utilities. +5. Реализовать базовые компоненты и Vue-адаптеры (Button, Input, Badge, Alert и т.д.). +6. Обновить `docs/component-coverage.md` по мере добавления компонентов. +7. Создать визуальные Playwright-тесты для сравнения компонентов с Figma-экспортами. ## Заметки по reference @@ -62,6 +61,6 @@ ## Риски и открытые вопросы -- Figma недоступна публично — блокирует вытаскивание точных токенов. -- Нужно решить, копировать ли IBM Plex Mono + Phosphor Icons в `public/assets/` или подключать через npm/CDN. +- Figma REST token не имеет scope `file_variables:read`, поэтому переменные пришлось вытаскивать через PNG-экспорт. Это ограничивает точность, но базовые токены получены. +- Нужно решить, копировать ли Plus Jakarta Sans + Phosphor Icons в `public/assets/` или подключать через npm/CDN. - Нужно ли сохранять `dist/js/vmk-ui-kit.js` global namespace как `VMKUIKit` или дублировать под `GNexusUIKit` для совместимости? (Пока выбран `VMKUIKit`, потому что пакет новый.) diff --git a/docs/style-guide.md b/docs/style-guide.md index 1b7600b..6c8548a 100644 --- a/docs/style-guide.md +++ b/docs/style-guide.md @@ -1,6 +1,135 @@ # Style guide -Дизайн-система `vmk-ui-kit` основана на Figma-макете. Токены и принципы здесь обновляются по мере вытаскивания спецификации из макета. +Дизайн-система `vmk-ui-kit` основана на Figma-макете **Oselya.AI_Copy**. Ниже зафиксированы актуальные токены, вытащенные из стилей Colors и Typography. + +## Шрифт + +- **Plus Jakarta Sans** — основной шрифт интерфейса. +- Веса: Regular (400), Medium (500), Semibold (600), Bold (700). +- Запасной стек: `"Inter", "SF Pro Display", system-ui, -apple-system, sans-serif`. + +## Цвета + +### Primitive palette + +#### Neutral + +| Шаг | Hex | +|-----|-----| +| 0 | `#FCFCFC` | +| 50 | `#F7F7F8` | +| 100 | `#F0F0F0` | +| 200 | `#E1E2E3` | +| 300 | `#D9D9D9` | +| 400 | `#B7BABE` | +| 500 | `#9CA3AF` | +| 600 | `#82868D` | +| 700 | `#6B7280` | +| 800 | `#3F4145` | +| 900 | `#1A1A1A` | + +#### Lavender + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | +|----|----|----|----|----|----|----|----|----|----| +| `#F4F3FC` | `#ECE8FA` | `#E1E0F7` | `#D8D7F5` | `#C0BEF0` | `#A19FE0` | `#8482C4` | `#5B5A9C` | `#4A4880` | `#3D3B7A` | + +#### Mint + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | +|----|----|----|----|----|----|----|----|----|----| +| `#F2FAFB` | `#E9F6F8` | `#DFF1F4` | `#D9EEF2` | `#BEE0E6` | `#A9D8DF` | `#7EC0C9` | `#4E96A0` | `#316873` | `#1F4B52` | + +#### Yellow + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | +|----|----|----|----|----|----|----|----|----|----| +| `#FEFEF0` | `#FDFCB8` | `#FCFB94` | `#FCFD76` | `#F0F166` | `#E8E94A` | `#D5D636` | `#C4C520` | `#A6A71B` | `#8A8B12` | + +#### Semantic primitives + +| Токен | bg | text | +|-------|----|------| +| danger | `#FBE4E2` | `#D64545` | +| info | `#EAF4FE` | `#2563A6` | +| success | `#DDF3E4` | `#1E7A46` | +| warning | `#FEF0DD` | `#B45309` | + +### Semantic aliases + +| Имя | Значение | Описание | +|-----|----------|----------| +| `$surface-page` | `neutral-0` | Основной фон страницы | +| `$surface-primary` | `neutral-0` | Основная поверхность | +| `$surface-secondary` | `mint-300` | Вторичная поверхность | +| `$surface-accent` | `yellow-300` | Акцентная поверхность | +| `$surface-surface` | `neutral-100`| Фон карточек/панелей | +| `$border-color-base` | `neutral-900` | Основной бордер | +| `$border-color-muted` | `neutral-300` | Вторичный бордер | + +## Радиусы + +| Токен | Значение | +|-------|----------| +| `$radius-sm` | 6px | +| `$radius-md` | 10px | +| `$radius-lg` | 16px | +| `$radius-xl` | 24px | +| `$radius-full`| 9999px | + +> Точные значения радиусов не были видны в Style Guide; подбираются и уточняются через Playwright-сравнение с компонентными фреймами. + +## Типографика + +### Headings — desktop + +| Стиль | Вес | Размер | Интерлиньяж | Letter-spacing | +|-------|-----|--------|-------------|----------------| +| Display | Bold | 48px | 56px | -2% | +| Heading 1 | Bold | 40px | 48px | -2% | +| Heading 2 | Bold | 32px | 40px | -1% | +| Heading 3 | Semibold | 28px | 34px | 0 | +| Heading 4 | Semibold | 22px | 30px | 0 | +| Heading 5 | Semibold | 18px | 26px | 0 | +| Heading 6 | Semibold | 16px | 22px | 0 | + +### Headings — mobile + +| Стиль | Вес | Размер | Интерлиньяж | Letter-spacing | +|-------|-----|--------|-------------|----------------| +| Display | Bold | 32px | 40px | -2% | +| Heading 1 | Bold | 28px | 36px | -2% | +| Heading 2 | Bold | 24px | 32px | -1% | +| Heading 3 | Semibold | 20px | 28px | 0 | +| Heading 4 | Semibold | 18px | 26px | 0 | +| Heading 5 | Semibold | 16px | 24px | 0 | +| Heading 6 | Semibold | 14px | 20px | 0 | + +### Body — desktop + +| Стиль | Размер | Интерлиньяж | +|-------|--------|-------------| +| Large | 20px | 32px | +| Medium | 18px | 28px | +| Small | 16px | 24px | +| XSmall | 14px | 20px | + +### Body — mobile + +| Стиль | Размер | Интерлиньяж | +|-------|--------|-------------| +| Large | 18px | 26px | +| Medium | 16px | 24px | +| Small | 14px | 20px | +| XSmall | 12px | 16px | + +### Button / Caption + +| Элемент | Desktop | Mobile | +|---------|---------|--------| +| Button | Semibold 16px / 22px | Semibold 15px / 20px | +| Caption | Regular 12px / 16px (-1%) | Regular 11px / 14px (-1%) | +| Overline | Regular 12px / 16px (-0.8%) | Medium 12px / 14px (-0.8%) | ## Иконки @@ -16,16 +145,4 @@ primary, secondary, accent, success, warning, danger, error, info ``` -`danger` и `error` обычно рендерят одинаковый цвет. - -## Цвета - -Цветовые токены будут добавлены после получения палитры из Figma. - -## Типографика - -Базовый шрифт, размеры и веса будут добавлены после получения типографики из Figma. - -## Отступы - -Spacing scale будет добавлен после получения токенов из Figma. +`danger` и `error` рендерят одинаковый цвет. diff --git a/src/scss/_design-tokens.scss b/src/scss/_design-tokens.scss index 66187e3..920f1be 100644 --- a/src/scss/_design-tokens.scss +++ b/src/scss/_design-tokens.scss @@ -4,20 +4,38 @@ SURFACES ========================= */ -$surface-page: $color-black; -$surface-panel: $color-dark; -$surface-panel-muted: $color-dark; -$surface-panel-strong: rgba($color-text-light, 0.085); +// Semantic surface aliases from Figma variable collection +$surface-page: $neutral-0; +$surface-primary: $neutral-0; +$surface-secondary:$mint-300; +$surface-accent: $yellow-300; +$surface-surface: $neutral-100; +$surface-panel: $neutral-100; +$surface-panel-muted: $neutral-50; +$surface-panel-strong: rgba($neutral-900, 0.08); /* ========================= BORDERS ========================= */ -$border-width-base: 2px; -$border-width-accent: 6px; -$border-style-base: solid; -$border-color-base: $color-primary; -$border-color-muted: rgba($color-text-light, 0.24); +$border-width-base: 1px; +$border-width-accent: 2px; +$border-style-base: solid; +$border-color-base: $neutral-900; +$border-color-muted: $neutral-300; + +/* ========================= + RADIUS +========================= */ + +// Figma kit uses rounded cards, buttons and inputs. +// Exact token was not visible in the style guide images; +// refine via Playwright comparison against component frames. +$radius-sm: 6px; +$radius-md: 10px; +$radius-lg: 16px; +$radius-xl: 24px; +$radius-full:9999px; /* ========================= MOTION @@ -32,12 +50,12 @@ CONTROL SIZES ========================= */ -$control-height-sm: 38px; -$control-height-md: 46px; -$control-height-lg: 54px; -$control-icon-rail-md: 46px; -$control-icon-rail-sm: 32px; -$control-choice-size: 22px; +$control-height-sm: 34px; +$control-height-md: 42px; +$control-height-lg: 52px; +$control-icon-rail-md: 40px; +$control-icon-rail-sm: 28px; +$control-choice-size: 20px; $control-switch-width: 32px; $control-switch-height: 16px; $control-switch-knob: 20px; @@ -46,20 +64,20 @@ ICONS ========================= */ -$icon-size-xs: 14px; -$icon-size-sm: 18px; -$icon-size-md: 22px; -$icon-size-lg: 26px; -$icon-size-xl: 32px; -$icon-size-display: 56px; +$icon-size-xs: 12px; +$icon-size-sm: 16px; +$icon-size-md: 20px; +$icon-size-lg: 24px; +$icon-size-xl: 28px; +$icon-size-display: 48px; /* ========================= FOCUS ========================= */ $focus-outline-width: 2px; -$focus-outline-offset: 3px; -$focus-outline-color: $color-warning; +$focus-outline-offset: 2px; +$focus-outline-color: $mint-600; @mixin focus_ring { outline: $focus-outline-width solid $focus-outline-color; @@ -69,13 +87,14 @@ @mixin hard_panel($border-color: $border-color-muted, $accent-width: 0) { background: $surface-panel-muted; border: $border-width-base $border-style-base $border-color; + border-radius: $radius-md; @if $accent-width != 0 { border-left-width: $accent-width; } } -@mixin state_panel($color, $text-color: $color-black) { +@mixin state_panel($color, $text-color: $neutral-0) { border-color: $color; .card-title, diff --git a/src/scss/_fonts.scss b/src/scss/_fonts.scss index 9a9290b..b1a6a99 100644 --- a/src/scss/_fonts.scss +++ b/src/scss/_fonts.scss @@ -2,4 +2,56 @@ Fonts ========================= */ -$font-family-base: "IBM Plex Mono", "SF Mono", "Consolas", monospace; +$font-family-base: "Plus Jakarta Sans", "Inter", "SF Pro Display", system-ui, -apple-system, sans-serif; + +// Font weights available in the design system +$font-weight-regular: 400; +$font-weight-medium: 500; +$font-weight-semibold: 600; +$font-weight-bold: 700; + +/* ========================= + Typography scale + Extracted from Figma Style Guide → Typography. + Format: weight / font-size / line-height / letter-spacing. +========================= */ + +// Headings — mobile (default, can be overridden at breakpoints) +$heading-display-mobile: (bold, 32px, 40px, -0.02em); +$heading-1-mobile: (bold, 28px, 36px, -0.02em); +$heading-2-mobile: (bold, 24px, 32px, -0.01em); +$heading-3-mobile: (semibold, 20px, 28px, 0); +$heading-4-mobile: (semibold, 18px, 26px, 0); +$heading-5-mobile: (semibold, 16px, 24px, 0); +$heading-6-mobile: (semibold, 14px, 20px, 0); + +// Headings — desktop +$heading-display-desktop: (bold, 48px, 56px, -0.02em); +$heading-1-desktop: (bold, 40px, 48px, -0.02em); +$heading-2-desktop: (bold, 32px, 40px, -0.01em); +$heading-3-desktop: (semibold, 28px, 34px, 0); +$heading-4-desktop: (semibold, 22px, 30px, 0); +$heading-5-desktop: (semibold, 18px, 26px, 0); +$heading-6-desktop: (semibold, 16px, 22px, 0); + +// Body — mobile +$body-large-mobile: (regular, 18px, 26px, 0); +$body-medium-mobile: (regular, 16px, 24px, 0); +$body-small-mobile: (regular, 14px, 20px, 0); +$body-xsmall-mobile: (regular, 12px, 16px, 0); + +// Body — desktop +$body-large-desktop: (regular, 20px, 32px, 0); +$body-medium-desktop: (regular, 18px, 28px, 0); +$body-small-desktop: (regular, 16px, 24px, 0); +$body-xsmall-desktop: (regular, 14px, 20px, 0); + +// Button +$button-mobile: (semibold, 15px, 20px, 0); +$button-desktop: (semibold, 16px, 22px, 0); + +// Caption / overline +$caption-mobile: (regular, 11px, 14px, -0.01em); +$caption-desktop:(regular, 12px, 16px, -0.01em); +$overline-mobile: (medium, 12px, 14px, -0.008em); +$overline-desktop:(regular, 12px, 16px, -0.008em); diff --git a/src/scss/_kit-deps.scss b/src/scss/_kit-deps.scss index 969f002..e4be168 100644 --- a/src/scss/_kit-deps.scss +++ b/src/scss/_kit-deps.scss @@ -1,2 +1,4 @@ @forward "palette-colors"; @forward "design-tokens"; +@forward "spacing"; +@forward "fonts"; diff --git a/src/scss/_palette-colors.scss b/src/scss/_palette-colors.scss index 0ccb16a..1307fb7 100644 --- a/src/scss/_palette-colors.scss +++ b/src/scss/_palette-colors.scss @@ -1,39 +1,92 @@ // ————————————————————————— -// VMK UI Kit palette placeholder. -// Replace tokens below with values extracted from Figma. +// VMK UI Kit primitive palette. +// Extracted from Figma Style Guide → Colors → primitives. // ————————————————————————— -// neutrals -$color-black: #16161E; -$color-dark: #1F2335; -$color-grey: #414868; +// neutral +$neutral-0: #FCFCFC; +$neutral-50: #F7F7F8; +$neutral-100:#F0F0F0; +$neutral-200:#E1E2E3; +$neutral-300:#D9D9D9; +$neutral-400:#B7BABE; +$neutral-500:#9CA3AF; +$neutral-600:#82868D; +$neutral-700:#6B7280; +$neutral-800:#3F4145; +$neutral-900:#1A1A1A; -// prime accents -$color-cyan: #7DCFFF; -$color-magenta: #FF00CC; -$color-hot-pink: #FF1492; -$color-electric-blue:#7AA2F7; -$color-orange: #FF9E64; +// lavender +$lavender-50: #F4F3FC; +$lavender-100:#ECE8FA; +$lavender-200:#E1E0F7; +$lavender-300:#D8D7F5; +$lavender-400:#C0BEF0; +$lavender-500:#A19FE0; +$lavender-600:#8482C4; +$lavender-700:#5B5A9C; +$lavender-800:#4A4880; +$lavender-900:#3D3B7A; -// secondary accents -$color-purple: #BB9AF7; -$color-indigo: #565F89; -$color-teal: #73DACA; +// mint +$mint-50: #F2FAFB; +$mint-100:#E9F6F8; +$mint-200:#DFF1F4; +$mint-300:#D9EEF2; +$mint-400:#BEE0E6; +$mint-500:#A9D8DF; +$mint-600:#7EC0C9; +$mint-700:#4E96A0; +$mint-800:#316873; +$mint-900:#1F4B52; -// highlights -$color-neon-yellow: #E0AF68; -$color-neon-green: #9ECE6A; +// yellow +$yellow-50: #FEFEF0; +$yellow-100:#FDFCB8; +$yellow-200:#FCFB94; +$yellow-300:#FCFD76; +$yellow-400:#F0F166; +$yellow-500:#E8E94A; +$yellow-600:#D5D636; +$yellow-700:#C4C520; +$yellow-800:#A6A71B; +$yellow-900:#8A8B12; + +// semantic primitives +$danger-bg: #FBE4E2; +$danger-text:#D64545; +$info-bg: #EAF4FE; +$info-text: #2563A6; +$success-bg: #DDF3E4; +$success-text:#1E7A46; +$warning-bg: #FEF0DD; +$warning-text:#B45309; + +// convenience aliases used across components +$color-black: $neutral-900; +$color-dark: $neutral-800; +$color-grey: $neutral-500; +$color-cyan: $mint-500; +$color-magenta: $lavender-500; +$color-hot-pink: $lavender-700; +$color-electric-blue:$lavender-600; +$color-orange: $warning-text; +$color-purple: $lavender-500; +$color-indigo: $lavender-800; +$color-teal: $mint-700; +$color-neon-yellow: $yellow-300; +$color-neon-green: $success-text; // text tones -$color-text-light: #C0CAF5; -$color-text-medium: #A9B1D6; -$color-text-dark: #787C99; +$color-text-light: $neutral-0; +$color-text-medium: $neutral-500; +$color-text-dark: $neutral-700; // UI state -$color-primary: $color-text-light; -$color-secondary: $color-electric-blue; -$color-accent: $color-orange; -$color-success: $color-neon-green; -$color-warning: $color-neon-yellow; -$color-error: #F7768E; -$color-info: $color-purple; +$color-primary: $neutral-900; +$color-secondary: $mint-500; +$color-accent: $yellow-300; +$color-success: $success-text; +$color-warning: $warning-text; +$color-error: $danger-text; +$color-info: $info-text; diff --git a/src/scss/demo.scss b/src/scss/demo.scss index 4c4b484..55367eb 100644 --- a/src/scss/demo.scss +++ b/src/scss/demo.scss @@ -1,11 +1,12 @@ @use "kit-deps" as *; /* ========================= - Demo page styles placeholder + Demo page styles ========================= */ .container { width: 100%; max-width: 1200px; margin: 0 auto; + padding: $space-6; } diff --git a/src/scss/kit.scss b/src/scss/kit.scss index 80a59a6..9e36996 100644 --- a/src/scss/kit.scss +++ b/src/scss/kit.scss @@ -15,7 +15,8 @@ body { background-color: $surface-page; - color: $color-text-light; + color: $neutral-900; + font-family: $font-family-base; } .ph.normalize { diff --git a/vmk-ui-kit-figma.url b/vmk-ui-kit-figma.url index 7cb46ef..664fc50 100644 --- a/vmk-ui-kit-figma.url +++ b/vmk-ui-kit-figma.url @@ -1,2 +1,2 @@ [InternetShortcut] -URL=https://www.figma.com/design/Wh7d0fPgbkslCoyQmO9DC4/%E2%9D%96-Oselya.ai?node-id=40002541-29833&p=f +URL=https://www.figma.com/design/scEx4ahLAQbx9akCuttfQC/Oselya.AI_Copy?m=auto&t=M7v0vsNdgVY308YR-1