Прогнать Prettier: чинит format:check в CI

This commit is contained in:
2026-09-03 03:59:56 +03:00
parent ef327aa748
commit db85639086
2 changed files with 118 additions and 230 deletions
+114 -85
View File
@@ -12,16 +12,18 @@
## 1. Палитра
### Базовые цвета
| Назначение | HEX | Заметка |
|---|---|---|
| Фиолетовый (акцент) | `#a96bff` | HSL 275°, 100%, 71% — светлый, для тёмной темы и акцентов |
| Фиолетовый (основной) | `#6a45b8` | тон ~40 — заливка кнопок, активные элементы |
| Тёмно-фиолетовый (текст) | `#5a2da8` | текст на светло-фиолетовом контейнере |
| Крем (фон) | `#faf6f0` | основная поверхность |
| Крем (исходный) | `#f6f0ea` / `#ece0d1` | более тёплые/тёмные оттенки нейтрала |
| Оранжевый (исходный) | `#ffb76b` | стартовый цвет, ушёл в роль `tertiary` |
| Назначение | HEX | Заметка |
| ------------------------ | --------------------- | --------------------------------------------------------- |
| Фиолетовый (акцент) | `#a96bff` | HSL 275°, 100%, 71% — светлый, для тёмной темы и акцентов |
| Фиолетовый (основной) | `#6a45b8` | тон ~40 — заливка кнопок, активные элементы |
| Тёмно-фиолетовый (текст) | `#5a2da8` | текст на светло-фиолетовом контейнере |
| Крем (фон) | `#faf6f0` | основная поверхность |
| Крем (исходный) | `#f6f0ea` / `#ece0d1` | более тёплые/тёмные оттенки нейтрала |
| Оранжевый (исходный) | `#ffb76b` | стартовый цвет, ушёл в роль `tertiary` |
### Связь оттенков
Крем `#ece0d1` (тон ~33°) и оранжевый `#ffb76b` (тон ~31°) — родственники по тёплому углу спектра.
Фиолетовый — холодный акцент в противовес тёплому нейтралу. Отсюда баланс «тёплая база + прохладный акцент».
@@ -29,27 +31,28 @@
## 2. Роли Material 3
| Роль MD3 | Светлая тема | Текст на ней (`on-`) |
|---|---|---|
| `primary` | `#6a45b8` | `#ffffff` |
| `primary-container` | `#ebddff` | `#5a2da8` |
| `secondary-container` | `#ebddff` | `#5a2da8` |
| `tertiary-container` | `#ffdcad` | `#7a4a14` |
| `surface` | `#faf6f0` | `#3a2e22` |
| `surface-container` | `#f0e8dd` | `#3a2e22` |
| `surface-container-high` | `#ece2d3` | `#3a2e22` |
| `surface-bright` (рабочая зона) | `#fdfaf6` | `#3a2e22` |
| `outline` | `#d8cdbe` | — |
| `outline-variant` | `#ece2d3` | — |
| `error` | `#ba1a1a` | `#ffffff` |
| `error-container` | `#ffdad6` | `#410002` |
| Роль MD3 | Светлая тема | Текст на ней (`on-`) |
| ------------------------------- | ------------ | -------------------- |
| `primary` | `#6a45b8` | `#ffffff` |
| `primary-container` | `#ebddff` | `#5a2da8` |
| `secondary-container` | `#ebddff` | `#5a2da8` |
| `tertiary-container` | `#ffdcad` | `#7a4a14` |
| `surface` | `#faf6f0` | `#3a2e22` |
| `surface-container` | `#f0e8dd` | `#3a2e22` |
| `surface-container-high` | `#ece2d3` | `#3a2e22` |
| `surface-bright` (рабочая зона) | `#fdfaf6` | `#3a2e22` |
| `outline` | `#d8cdbe` | — |
| `outline-variant` | `#ece2d3` | — |
| `error` | `#ba1a1a` | `#ffffff` |
| `error-container` | `#ffdad6` | `#410002` |
### Статусные цвета (тёплый подтон)
| Статус | Фон чипа | Текст | Точка |
|---|---|---|---|
| Онлайн / успех | `#c0dd97` | `#2c4d0a` | `#639922` |
| Статус | Фон чипа | Текст | Точка |
| ----------------- | --------- | --------- | --------- |
| Онлайн / успех | `#c0dd97` | `#2c4d0a` | `#639922` |
| Офлайн / нейтраль | `#e4dac9` | `#7a6a55` | `#a99a85` |
| Ошибка | `#ffdad6` | `#410002` | `#ba1a1a` |
| Ошибка | `#ffdad6` | `#410002` | `#ba1a1a` |
> Принцип: `primary` (тёмный `#6a45b8`) — для заливок и текста по контрасту.
> Светлый `#a96bff` держим как акцент/иконки/тёмную тему, не как фон под белый текст.
@@ -64,26 +67,27 @@
- Моноширинный шрифт для технических данных: IP-адреса, VPN-ключи, rx/tx.
Шкала:
| Элемент | Размер | Вес |
|---|---|---|
| Заголовок раздела | 20px | 500 |
| Элемент | Размер | Вес |
| -------------------------------- | ------- | --- |
| Заголовок раздела | 20px | 500 |
| Подзаголовок / название карточки | 15–16px | 500 |
| Основной текст | 14px | 400 |
| Заголовки таблицы | 13px | 500 |
| Данные таблицы | 13px | 400 |
| Подписи / hint | 12px | 400 |
| Основной текст | 14px | 400 |
| Заголовки таблицы | 13px | 500 |
| Данные таблицы | 13px | 400 |
| Подписи / hint | 12px | 400 |
---
## 4. Скругления (MD3 shape scale)
| Элемент | Радиус |
|---|---|
| Кнопки, чипы, инпуты (pill) | `100px` |
| Карточки / контейнеры | `24px` |
| Внутренние блоки таблицы / метрики | `16px` |
| Иконка-логотип | `8–9px` |
| Аватар, точка статуса | `50%` |
| Элемент | Радиус |
| ---------------------------------- | ------- |
| Кнопки, чипы, инпуты (pill) | `100px` |
| Карточки / контейнеры | `24px` |
| Внутренние блоки таблицы / метрики | `16px` |
| Иконка-логотип | `8–9px` |
| Аватар, точка статуса | `50%` |
> Скругления только полные (по всем сторонам). Односторонних бордюров с radius не делаем.
@@ -92,6 +96,7 @@
## 5. Состояния и взаимодействие (MD3 state layers)
Поверх элемента — полупрозрачный слой акцентного цвета:
- Hover: ~8% непрозрачности.
- Focus / pressed: ~12%.
- Для фиолетовых элементов слой фиолетовый, для нейтральных — тёмный нейтрал.
@@ -99,10 +104,12 @@
Элевация — через тональные поверхности (светлее/темнее крем), а не через тени. Иерархия строится тональностью: рабочая зона данных светлее крема, крем — для фона и хрома.
**Опора на платформу.**
- На десктопе (указатель) hover-слой работает как первичная обратная связь.
- На тач-устройствах hover не срабатывает — обратная связь строится на нажатии и выборе, не на наведении.
**Инертные vs действенные элементы.**
- Навигационные строки и статичные строки данных — **инертны** (без эффекта вдавливания); активное состояние показывается заливкой-таблеткой (`primary-container`).
- Реальные действия (кнопки, переключатель, QR, удаление) могут иметь лёгкий pressed-эффект через state layer акцентного цвета (~12%).
@@ -125,30 +132,31 @@
Фиолетовый акцент — общий со светлой темой по смыслу, но осветлён под тёмный фон.
| Роль | Тёмная тема | Текст на ней (`on-`) |
|---|---|---|
| `surface` | `#1a1611` | `#ede2d4` |
| `surface-container` | `#251f18` | `#ede2d4` |
| `surface-container-high` | `#302820` | `#ede2d4` |
| `surface-bright` (рабочая зона) | `#2b251d` | `#ede2d4` |
| `primary` | `#cbb3ff` (светлый фиолетовый) | `#381b6c` |
| `primary-container` | `#52389e` | `#ebddff` |
| `secondary-container` | `#483a55` | `#ebddff` |
| `outline` | `#4d4334` | — |
| `outline-variant` | `#342c20` | — |
| `error` | `#ffb4ab` | `#5c1414` |
| `error-container` | `#5c1414` | `#ffb4ab` |
| текст / `on-surface` | `#ede2d4` | — |
| приглушённый текст | `#b6a994` | — |
| Роль | Тёмная тема | Текст на ней (`on-`) |
| ------------------------------- | ------------------------------ | -------------------- |
| `surface` | `#1a1611` | `#ede2d4` |
| `surface-container` | `#251f18` | `#ede2d4` |
| `surface-container-high` | `#302820` | `#ede2d4` |
| `surface-bright` (рабочая зона) | `#2b251d` | `#ede2d4` |
| `primary` | `#cbb3ff` (светлый фиолетовый) | `#381b6c` |
| `primary-container` | `#52389e` | `#ebddff` |
| `secondary-container` | `#483a55` | `#ebddff` |
| `outline` | `#4d4334` | — |
| `outline-variant` | `#342c20` | — |
| `error` | `#ffb4ab` | `#5c1414` |
| `error-container` | `#5c1414` | `#ffb4ab` |
| текст / `on-surface` | `#ede2d4` | — |
| приглушённый текст | `#b6a994` | — |
### Статусные цвета (тёмная тема)
Фон и текст осветлены/переинвертированы под тёмный фон, подтон тот же тёплый.
| Статус | Фон чипа | Текст | Точка |
|---|---|---|---|
| Онлайн / успех | `#2d4413` | `#c8e3a0` | `#8fc24f` |
| Статус | Фон чипа | Текст | Точка |
| ----------------- | --------- | --------- | --------- |
| Онлайн / успех | `#2d4413` | `#c8e3a0` | `#8fc24f` |
| Офлайн / нейтраль | `#34291c` | `#d0c0a4` | `#b0a085` |
| Ошибка | `#5c1414` | `#ffb4ab` | `#ff5449` |
| Ошибка | `#5c1414` | `#ffb4ab` | `#ff5449` |
> Значения — рабочая отправная точка. Контраст текста на поверхностях стоит прогнать через проверку (цель ≥ 4,5:1 для мелкого текста) перед фиксацией.
>
@@ -161,16 +169,19 @@
Эти компоненты используют одни и те же роли на обеих платформах; различается только их расположение в раскладке.
### Кнопки
- **Главное действие** (Создать пользователя): заливка `primary` `#6a45b8`, текст белый (`on-primary`), pill.
- **Вторичное** (Обновить): tonal — фон `secondary-container` `#ebddff`, текст `#5a2da8`, pill.
- **Опасное** (Выход, удаление): контур `#e6c4bc` / роль `error`, текст `#b34a3a`, без заливки, pill.
- Иконка слева от текста, gap 6–8px.
### Поля ввода и поиска
- Pill-инпут, фон `surface-bright` `#fdfaf6`, бордюр `outline` `#d8cdbe`, плейсхолдер приглушённым.
- Поле поиска — с иконкой поиска слева.
### Чипы статуса
- Pill, маленькая цветная точка + подпись. Цвета — из таблицы статусов (успех / нейтраль / ошибка), текст соответствующей роли `on-`.
---
@@ -178,10 +189,12 @@
# Часть III. Десктоп
### Компоновка
- Многоколоночная сетка.
- Заголовок раздела — в шапке контента.
### Боковое меню (постоянная панель)
- Фиксированная ширина ~240px, всегда на экране.
- Фон `#f0e8dd` (`surface-container`, на тон темнее основного — мягкое разделение без жёсткой границы).
- Логотип сверху, навигация ниже, «Выход» прижат к низу (`margin-top: auto`).
@@ -189,6 +202,7 @@
- Неактивные: текст `#5a4a3a`, при наведении — лёгкая фиолетовая подсветка (state layer).
### Таблица пользователей
- Контейнер `surface-bright` `#fdfaf6`, скругление 16px, бордюр `#ece2d3` (`outline-variant`).
- Шапка на тоне `#f0e8dd`, текст среднего веса `#5a4a3a`.
- Разделители строк — `0.5px solid #ece2d3`.
@@ -201,6 +215,7 @@
# Часть IV. Мобильный
## 1. Принципы адаптации
1. Одна колонка. Контент течёт вертикально на ширине телефона (рабочая ширина ~360px, поля по 16px).
2. Никаких таблиц с горизонтальной прокруткой — табличные данные разворачиваются в карточки.
3. Боковое меню десктопа превращается в модальный navigation drawer (паттерн Material 3).
@@ -210,42 +225,51 @@
## 2. Навигация и drawer
### Модель
Приложение имеет один основной раздел («Пользователи»). На десктопе это постоянная боковая панель ~240px; на мобильном — **модальный drawer**, скрытый по умолчанию.
### Открытие и закрытие (важно по MD3)
- Открытие — **только тапом по иконке-гамбургеру** в верхнем баре.
- Закрытие — тап по затемнению (scrim) или свайп панели к её левому краю (срабатывает при сдвиге ~40px+).
- Открытие edge-swipe'ом от края экрана **не используется**: жест конфликтует с системным «назад» (predictive back) на Android.
### Поведение
- Drawer выезжает слева, поверх контента, с затемнением под ним.
- Затемнение блокирует взаимодействие с экраном, пока панель открыта.
- Анимация — горизонтальный сдвиг панели + плавное появление scrim (быстрый, «решительный» easing, ~250–280 мс); затемнение и сдвиг анимируются вместе.
### Содержимое панели
Поверхность панели — `surface-container` (на тон темнее основного экрана), мягкое разделение без жёсткой границы. Скругление контейнера 24px, правые углы.
Сверху вниз:
1. **Шапка** — вордмарк приложения (роль заголовка) + версия (роль приглушённого текста), внутренний отступ.
2. **Тонкий разделитель** (`outline`, 0.5px).
3. **Навигация** — пункты-строки с ведущей иконкой 22px и подписью 15px, gap 16px.
- Активный пункт: pill-контейнер `primary-container` во всю ширину; текст, иконка и счётчик — `on-primary-container`, вес 500.
- Неактивный пункт: подпись и иконка — основной текст (тёмный нейтрал), без подсветки при наведении.
- Строки навигации **инертны при нажатии** — без эффекта вдавливания, выделяет только активное состояние.
- Активный пункт: pill-контейнер `primary-container` во всю ширину; текст, иконка и счётчик — `on-primary-container`, вес 500.
- Неактивный пункт: подпись и иконка — основной текст (тёмный нейтрал), без подсветки при наведении.
- Строки навигации **инертны при нажатии** — без эффекта вдавливания, выделяет только активное состояние.
4. **«Выход»** — прижат к низу (`margin-top: auto`), стиль опасного действия (контур, без заливки), pill, иконка слева.
### Замечание про объём меню
Длинный список разделов (как у Gmail-паттерна) оправдан только при 5+ назначениях. У приложения раздел один, поэтому drawer минимальный: шапка → активный пункт → «Выход» внизу. Доп. пункты добавляются только под реальные экраны.
## 3. Верхний бар (top app bar)
- Слева — иконка-гамбургер (кнопка-таргет ≥ 42px, круглая зона нажатия).
- Рядом — заголовок раздела (роль заголовка, 20px / 500).
- Бар без заливки и без тени; высота поверхности задаётся фоном экрана (`surface`).
## 4. Экран «Пользователи»
Порядок блоков сверху вниз: верхний бар → карточка сервера → «Добавить сервер» → форма создания → счётчик → список пользователей.
### 4.1. Карточка VPN-сервера
- Поверхность — `surface-bright`, скругление 24px.
- **Активная обводка** — рамка 2px роли `primary` (приглушённый светло-фиолетовый вариант): сигнал, что сервер выбран/активен.
- Слева — компактный **переключатель** (pill-трек, маленький круглый ползунок). Состояние «вкл» отражается цветом трека из роли статуса «успех» и положением ползунка.
@@ -253,20 +277,25 @@
- Справа — нейтральный чип счётчика пиров (роль статуса «нейтраль», pill).
### 4.2. «Добавить сервер»
- Полноширинная кнопка-«призрак»: пунктирный контур (`outline`), без заливки, иконка `+` слева, приглушённый текст. Pill (100px).
### 4.3. Форма создания пользователя
- Поле ввода: pill-инпут на `surface-bright`, бордюр `outline`, плейсхолдер приглушённым. Полная ширина.
- Под ним пара кнопок в ряд (50/50):
- **Создать** — главное действие: заливка `primary`, текст `on-primary`, иконка `+`, pill.
- **Обновить** — вторичное: tonal (`secondary-container` / `on-secondary-container`), иконка обновления, pill.
- **Создать** — главное действие: заливка `primary`, текст `on-primary`, иконка `+`, pill.
- **Обновить** — вторичное: tonal (`secondary-container` / `on-secondary-container`), иконка обновления, pill.
### 4.4. Список пользователей
- Над списком — счётчик («N пользователей») приглушённым текстом 12px.
- Десктопная таблица (`#`, Статус, Имя, IP, rx, tx, действия) на мобильном **разворачивается в карточки** — по одной на пользователя.
### 4.5. Карточка пользователя
Поверхность `surface-bright`, бордюр `outline-variant`, скругление 24px. Внутри:
1. **Верхняя строка** — слева порядковый `#N` (приглушённый) + имя (16px / 500); справа чип статуса «онлайн» (роль статуса «успех»: точка + подпись, pill).
2. **Строка IP** — иконка-глобус + адрес моноширинным.
3. **Два блока метрик** (rx / tx) в ряд 50/50: внутренние блоки на `surface-container`, скругление 16px. Сверху подпись со стрелкой (↓ rx / ↑ tx) приглушённым 12px, под ней значение моноширинным 18px / 500.
@@ -274,31 +303,31 @@
## 5. Сводка мобильных компонентов (через роли и токены)
| Компонент | Поверхность / контур | Форма | Текст |
|---|---|---|---|
| Карточка сервера | `surface-bright` + рамка `primary` 2px | 24px | подзаголовок + mono |
| Карточка пользователя | `surface-bright` + `outline-variant` | 24px | подзаголовок + mono |
| Блок метрики (rx/tx) | `surface-container` | 16px | подпись + mono |
| Кнопка «Создать» | заливка `primary` | pill 100px | `on-primary`, 500 |
| Кнопка «Обновить» | tonal `secondary-container` | pill 100px | `on-…`, 500 |
| «Добавить сервер» | пунктир `outline` | pill 100px | приглушённый |
| «Выход» | контур (опасное) | pill 100px | роль опасного |
| Поле ввода | `surface-bright` + `outline` | pill 100px | основной / плейсхолдер |
| Чип статуса | роль статуса (успех/нейтраль/ошибка) | pill 100px | соответствующий `on-` |
| Активный пункт меню | `primary-container` | pill 100px | `on-primary-container`, 500 |
| Компонент | Поверхность / контур | Форма | Текст |
| --------------------- | -------------------------------------- | ---------- | --------------------------- |
| Карточка сервера | `surface-bright` + рамка `primary` 2px | 24px | подзаголовок + mono |
| Карточка пользователя | `surface-bright` + `outline-variant` | 24px | подзаголовок + mono |
| Блок метрики (rx/tx) | `surface-container` | 16px | подпись + mono |
| Кнопка «Создать» | заливка `primary` | pill 100px | `on-primary`, 500 |
| Кнопка «Обновить» | tonal `secondary-container` | pill 100px | `on-…`, 500 |
| «Добавить сервер» | пунктир `outline` | pill 100px | приглушённый |
| «Выход» | контур (опасное) | pill 100px | роль опасного |
| Поле ввода | `surface-bright` + `outline` | pill 100px | основной / плейсхолдер |
| Чип статуса | роль статуса (успех/нейтраль/ошибка) | pill 100px | соответствующий `on-` |
| Активный пункт меню | `primary-container` | pill 100px | `on-primary-container`, 500 |
---
# Отличия десктоп / мобильный (кратко)
| Аспект | Десктоп | Мобильный |
|---|---|---|
| Навигация | постоянная боковая панель ~240px | модальный drawer (гамбургер) |
| Список пользователей | таблица с колонками | карточки, по одной на пользователя |
| rx / tx | ячейки таблицы | два блока-метрики внутри карточки |
| Действия в строке | иконки в конце строки | ряд кнопок внизу карточки |
| Ширина | многоколоночная сетка | одна колонка, поля 16px |
| Заголовок раздела | в шапке контента | в верхнем баре рядом с гамбургером |
| Аспект | Десктоп | Мобильный |
| -------------------- | -------------------------------- | ---------------------------------- |
| Навигация | постоянная боковая панель ~240px | модальный drawer (гамбургер) |
| Список пользователей | таблица с колонками | карточки, по одной на пользователя |
| rx / tx | ячейки таблицы | два блока-метрики внутри карточки |
| Действия в строке | иконки в конце строки | ряд кнопок внизу карточки |
| Ширина | многоколоночная сетка | одна колонка, поля 16px |
| Заголовок раздела | в шапке контента | в верхнем баре рядом с гамбургером |
---
@@ -310,4 +339,4 @@
4. Sentence case, два веса шрифта, без капса. Технические данные — моноширинным.
5. Главное действие — залито фиолетовым; вторичное — tonal; опасное — контур.
6. Таблицы → карточки, rx/tx → блоки-метрики на мобильном; одна колонка, крупные тач-таргеты, действия в зоне большого пальца.
7. Drawer открывается гамбургером, закрывается scrim/свайпом; edge-swipe не задействуем. Hover — опора десктопа; на тач-устройствах обратная связь на нажатии/выборе.
7. Drawer открывается гамбургером, закрывается scrim/свайпом; edge-swipe не задействуем. Hover — опора десктопа; на тач-устройствах обратная связь на нажатии/выборе.