Перейти к содержимому
OmniRoute source

OmniRoute — Design System & Visual Identity (Русский)

Маркетинговый сайт (viral.omniroute.online, why.omniroute.online, omniroute.online) и дашборд продукта должны выглядеть как единый продукт. Сайт уже заимствовал палитру из дашборда — в его css/tokens.css даже указано: «Палитра соответствует дашборду OmniRoute (src/app/globals.css)». Таким образом, на уровне цветов они уже согласованы примерно на 80%. Чего не хватает дашборду:

  1. Фона с сеткой в стиле миллиметровой бумаги, который используется на каждой странице сайта.
  2. Нескольких общих дизайн-токенов, которые есть на сайте, но отсутствуют в дашборде (шкала радиусов, фирменный градиент, surface-2, моноширинный шрифт).
  3. Согласованности на уровне компонентов — ряд компонентов дашборда обходит токены темы, используя жёстко заданные значения hex/rgba.

Этот документ содержит анализ и план.


  • Единый источник истины — src/app/globals.css. Сайт следует за дашбордом, но не наоборот. Новые токены сначала добавляются в globals.css.
  • Токены вместо литералов. Компоненты используют семантические токены (bg-surface, text-primary, border-border), а не необработанные значения #hex.
  • Ненавязчивость вместо броскости. Сетка — это едва заметный фон позади содержимого; она не должна снижать контрастность текста или отвлекать от интерфейса.
  • Поддержка тем. Всё работает как в .dark (фирменном стиле продукта), так и в светлой теме.
  • Поэтапное внедрение. Сначала выпускаются сетка и токены (низкий риск, высокая заметность), затем компоненты приводятся в порядок несколькими волнами.

3. Текущее состояние — что уже согласовано, а что ещё нет

Заголовок раздела «3. Текущее состояние — что уже согласовано, а что ещё нет»

Каждый фирменный цвет и цвет поверхности уже соответствует сайту по значению (различаются только имена — в дашборде используется префикс --color-). Проверено в src/app/globals.css:30-128:

Понятие Токен сайта (tokens.css) Токен дашборда (globals.css) Соответствие
основной --primary #e54d5e --color-primary #e54d5e ✅
основной при наведении --primary-hover #c93d4e --color-primary-hover #c93d4e ✅
акцентный --accent #6366f1 --color-accent #6366f1 ✅
акцентный-2 --accent-2 #8b5cf6 --color-accent-hover #8b5cf6 ✅ (переименован)
акцентный-3 --accent-3 #a855f7 --color-accent-light #a855f7 ✅ (переименован)
успех / предупреждение / ошибка #22c55e / #f59e0b / #ef4444 идентичны ✅
сигналы светофора #ff5f56 / #ffbd2e / #27c93f идентичны ✅
тёмный фон / поверхность / граница #0b0e14 / #161b22 / rgba(255,255,255,.08) идентичны ✅
светлый фон / поверхность / текст #f9f9fb / #fff / #1a1a2e идентичны ✅

Вывод: миграция цветов не требуется. Визуальный стиль уже является общим; мы завершаем его унификацию, а не перестраиваем заново.

Несоответствие На сайте есть Панель управления Действие
Фоновая сетка body::before в стиле миллиметровки, --grid-line, --grid-size 32px, --section-alt ✅ добавлено (Этап 1) Часть A
Шкала скруглений --radius 14px, --radius-sm 9px --radius 14px добавлен; -sm и переназначение компонентов ожидаются Часть B / Этап 2
Фирменный градиент --grad-brand 135deg primary→accent-3 ✅ токен добавлен (Этап 1); будет использован на Этапе 2 Часть B
Вложенная поверхность --surface-2 #1c2230 ✅ добавлено (Этап 1) Часть B
Моноширинный шрифт --font-mono (стек ui-monospace) ожидается (Этап 4, вместе с использующими компонентами) Часть B
text-muted (тёмная тема) #8b8b9e #a1a1aa (zinc-400) согласовать — Часть B

3.3 Механика темизации (чтобы ничего не сломать)

Заголовок раздела «3.3 Механика темизации (чтобы ничего не сломать)»
  • Tailwind v4, подход CSS-first (без tailwind.config.*). Токены определены в :root/.dark и доступны утилитам через @theme inline (globals.css:130-179).
  • Тёмная тема через класс .dark на <html> (@custom-variant dark в globals.css:22), переключаемый пользовательским хранилищем Zustand (src/store/themeStore.ts); тема по умолчанию = system (src/shared/constants/appConfig.ts:11). Вместо этого сайт использует html[data-theme="light"] — механизмы различаются, но никогда не пересекаются (разные источники), поэтому конфликта нет. Сохраняем механизм панели управления на основе .dark.
  • Есть переопределение основного цвета во время выполнения (themeStore.ts:85-97, предустановки в COLOR_THEMES) — пользователи могут менять --color-primary. Любой новый токен (градиент и т. п.), ссылающийся на --color-primary, автоматически наследует эти переопределения. ✅
  • Зарезервированные имена радиусов в Tailwind v4: --radius-sm/md/lg/... лежат в основе утилит rounded-*. Их переопределение задним числом изменит все существующие rounded-* (например, rounded-sm используется в 12 файлах). Поэтому значение малого радиуса и переназначение компонентов намеренно отложены до Этапа 2, где использующие их компоненты будут изменены одновременно.

4. Часть A — Фон в виде сетки миллиметровой бумаги (основная задача) — РЕАЛИЗОВАНО (этап 1)

Заголовок раздела «4. Часть A — Фон в виде сетки миллиметровой бумаги (основная задача) — РЕАЛИЗОВАНО (этап 1)»

Точный рецепт с сайта (_mono_repo/omnirouteSite/css/base.css): фиксированный псевдоэлемент размером во всю область просмотра, отрисовывающий два градиента с линиями толщиной 1px и расположенный с z-index:-1 позади всего содержимого.

body::before {
content: "";
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
background-image:
linear-gradient(to right, var(--grid-line) 1px, transparent 1px),
linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
background-size: var(--grid-size) var(--grid-size);
}

Почему это работает, несмотря на непрозрачный background-color у body: ::before с z-index:-1 отрисовывается поверх собственного фона элемента, но под его содержимым в потоке. Таким образом, --color-bg служит базовой заливкой, сетка накладывается поверх неё, а приложение отображается над сеткой.

4.2 Уже существующий прецедент в кодовой базе

Заголовок раздела «4.2 Уже существующий прецедент в кодовой базе»

src/app/landing/page.tsx:16-26 уже реализует такую же сетку на уровне страницы, но с красными линиями (#E54D5E, непрозрачность 0.06) с шагом 50px, а также с анимированными сферами. Таким образом, этот паттерн уже проверен в продукте; данная работа превращает его в глобальный фон с поддержкой тем.

:root {
/* светлая тема — непрозрачность сетки увеличена относительно значения 0.045 с сайта,
чтобы фон был действительно виден на плотной панели управления
(карточки и элементы интерфейса закрывают большую часть области просмотра) */
--grid-line: rgba(0, 0, 0, 0.07);
--grid-size: 32px;
--section-alt: rgba(0, 0, 0, 0.022);
}
.dark {
/* тёмная тема — значение увеличено относительно 0.035 по той же причине */
--grid-line: rgba(255, 255, 255, 0.06);
--section-alt: rgba(255, 255, 255, 0.018);
}

Сетка глобальна по своей реализации (она сразу охватывает панель, auth/login, страницы ошибок — все маршруты). Внутри панели её скрывал ровно один элемент:

  • src/shared/components/layouts/DashboardLayout.tsx — внешняя обёртка отрисовывала непрозрачный фон bg-bg. Всё, что находится под ней, уже прозрачно (<main>, контейнер прокрутки и внутренний контейнер max-w-7xl), поэтому удаление bg-bg позволяет сетке body просвечивать в области содержимого (при этом --color-bg у body остаётся базовой заливкой).

    <div className="flex h-dvh min-h-0 w-full overflow-hidden bg-bg">
    <div className="flex h-dvh min-h-0 w-full overflow-hidden">

4.5 Взаимодействие с элементами интерфейса (боковая панель / заголовок)

Заголовок раздела «4.5 Взаимодействие с элементами интерфейса (боковая панель / заголовок)»
  • Header (Header.tsx:207, bg-bg) и Sidebar (Sidebar.tsx:430, bg-sidebar) остаются непрозрачными → сетка отображается только в области содержимого, обрамлённой сплошными элементами интерфейса. Спокойный вариант по умолчанию, соответствующий тому, как на сайте элементы интерфейса отделены от рабочей области (решение D3 = сплошной фон).

4.6 Страницы входа / аутентификации / ошибок

Заголовок раздела «4.6 Страницы входа / аутентификации / ошибок»

Они отображаются непосредственно внутри &lt;body&gt; (без элементов интерфейса панели), поэтому глобальная сетка должна автоматически появляться позади них. Этап 5 — ВЫПОЛНЕН: автономные полноэкранные обёртки действительно были непрозрачными (min-h-screen … bg-bg, где bg-bg — та же сплошная заливка, что и у &lt;body&gt;), из-за чего сетка скрывалась на всех экранах вне панели, а не только на странице входа. Теперь все они прозрачны, поэтому общий фон виден насквозь: login, forgot-password, callback, maintenance, offline, status, terms, privacy, onboarding и ErrorPageScaffold (охватывает 400/401). Это закрывает D4 (область действия расширена со страницы входа на все автономные экраны). Проверяется тестом tests/unit/design-grid-background.test.ts.

landing/page.tsx сохраняет свой более насыщенный анимированный фон (сферы + виньетка) — собственную маркетинговую заставку (решение D5 = оставить без изменений).


На этапе 1 добавляются инертные, не вызывающие коллизий идентификационные токены (--surface-2/--color-surface-2, --grad-brand, --radius). На этапе 2 шкала радиусов подключается к Tailwind и компоненты переводятся на неё; на этапе 4 добавляется --font-mono и его потребители.

Токен Зачем Этап
--radius / --radius-sm Единая шкала радиусов (14/9) вместо разрозненных 6/8/12 1 (значение) / 2 (подключение + перевод)
--grad-brand Фирменный градиент для основных CTA (красный→фиолетовый), соответствующий сайту 1 (токен) / 2 (Button)
--surface-2 Вложенные панели / заголовки таблиц / внутренние строки 1
--font-mono Блоки кода, терминал, ID, эндпоинты 4
Согласование --text-muted Выбрать одно значение для сайта и панели (#a1a1aa рекомендуется) 2

D2 (text-muted): сайт — #8b8b9e, дашборд — #a1a1aa. Рекомендуется сохранить значение дашборда #a1a1aa и обновить сайт для соответствия. Косметическое изменение.


6. Часть C — стандартизация компонентов (этапы 2–4)

Заголовок раздела «6. Часть C — стандартизация компонентов (этапы 2–4)»

Собственные компоненты (без shadcn/Radix), Tailwind v4, семантические токены в основном внедрены (195 файлов импортируют общий barrel-файл). Задача — устранить обходные решения. Расположение: src/shared/components/.

# Элемент Файл(ы) Проблема → целевое состояние Этап
C1 Согласование радиусов Button.tsx:14-18, Card.tsx:39, Modal.tsx, Input.tsx, Select.tsx смесь 6/8/12px → --radius/--radius-sm (14/9) 2
C2 Градиент Button + вариант accent Button.tsx:5-12 основной вариант использует плоский переход красный→красный; привести к --grad-brand; добавить отсутствующий вариант accent. ~195 импортирующих файлов — максимальная заметность 2
C3 Таблицы DataTable.tsx:122-176, logTableStyles.ts, globals.css:405-414 полностью встроенные жёстко заданные rgba + несуществующие переменные; перенести на токены, удалить расходящиеся стили 3
C4 Централизация цветов статусов flow/edgeStyles.ts, TokenHealthBadge.tsx, DegradationBadge.tsx, ProviderCascadeNode.tsx, Badge.tsx + 5 вспомогательных модулей 6+ копий одних и тех же hex-значений → один модуль на основе --color-success/warning/error 3
C5 Рамка Card Card.tsx:39 border-white/5 → фирменный цвет /8 2
C6 Согласование фокусного кольца ✅ ГОТОВО globals.css --focus-ring (accent) и ring-primary/30 у элементов форм унифицировано на accent (фиолетовом) для соответствия глобальному кольцу и визуального отличия от красного кольца ошибки; ошибка остаётся красной 4
C7 Добавить Checkbox + Textarea необработанные &lt;input&gt;/&lt;textarea&gt; со встроенным accentColor:#6366f1 примитивы на основе токенов 4
C8 Удаление жёстко заданных hex-значений ConsoleLogViewer.tsx:240, ComboLiveStudio.tsx:306, точки Modal, ~14 файлов диаграмм литералы → токены 4
C9 cn() → clsx + tailwind-merge src/shared/utils/cn.ts конфликтующие классы накладываются друг на друга; требуется для переопределений C1 2

Уже соответствуют фирменному стилю (основаны на токенах, требуется только обновление радиусов): Badge, Toggle, SegmentedControl, Input, Select.


  • Фаза 1 — Сетка + токены идентичности (ЭТОТ PR). Сетка в globals.css + токены --surface-2/--grad-brand/--radius; фоновое изображение body::before; удаление блокирующего bg-bg; статический защитный тест. Низкий риск, можно откатить одним коммитом.
  • Фаза 2 — Примитивы (C1, C2, C5) — ВЫПОЛНЕНО в этом PR. Семантические утилиты радиуса rounded-card (14px) / rounded-control (9px) добавлены через @theme (пользовательские имена, поэтому стандартные rounded-sm/md/lg/xl остаются без изменений — без массового затрагивания 400 файлов); Card/Modal → 14px, Button/Input/Select → 9px; основной Button → --grad-brand (красный→фиолетовый) + новый вариант accent; границы Card → токен border-border (0.08). Отложено: для cn()→tailwind-merge (C9) нужны новые зависимости; массовая замена нестандартных rounded-lg (326 файлов) оставлена без изменений, поскольку примитивы охватывают основную часть поверхностей.
  • Фаза 3 — Цвета статусов + таблицы (C3, C4) — ВЫПОЛНЕНО в этом PR. ✅ C4 (src/shared/constants/statusColors.ts — единый источник STATUS_HEX; flow/edgeStyles.ts + TokenHealthBadge переведены на него, без изменений/с теми же hex-значениями). ✅ Токен --font-mono. ✅ C3 (DataTable) — все встроенные значения rgba и неработающие резервные значения var(--bg-table-header) / var(--text-secondary) заменены набором токенов --table-* (--table-header-bg/-row-zebra/-row-hover/-cell-border/-row-selected), чьи значения для тёмной темы в точности соответствуют старым жёстко заданным rgba (побайтовая идентичность для тёмной темы), а значения для светлой темы исправляют ранее всегда тёмное оформление. Граница заголовка → --color-border, вторичный текст → --color-text-muted. Перед слиянием требуется визуальная проверка. (Не затронуты: logTableStyles.ts и устаревшие правила Ant .ant-table — это отдельная задача с более низким приоритетом.)
  • Фаза 4 — Очистка (C6, C7, C9 выполнены; C8 ожидает выполнения). ✅ C9 cn() → twMerge(clsx(...)) (clsx + tailwind-merge добавлены как зависимости) — теперь className вызывающего кода корректно заменяет конфликтующий класс примитива, а не добавляется поверх него. ✅ C7 новые примитивы Checkbox + Textarea (на основе токенов, экспортируются из barrel-файла; изменение аддитивное — переход 32 обычных чекбоксов / 41 обычного текстового поля можно выполнять постепенно). ✅ C6 согласование кольца фокуса — элементы форм (Input/Select/Textarea/Toggle/Checkbox) теперь при фокусе используют кольцо акцентного (фиолетового) цвета, чтобы соответствовать глобальному --focus-ring и не конфликтовать с красным кольцом ошибки; красное состояние ошибки не изменено. ⏳ C8 — проверка hex-значений НЕ является слепым поиском и заменой — подтверждены случаи, которые являются намеренными и должны остаться: ConsoleLogViewer.tsx:240 (терминал всегда с тёмной темой), всплывающее окно TokenHealthBadge, SVG-обводки ReactFlow. Переносить следует только те hex-значения, которые действительно должны учитывать тему.

Для каждой фазы: npm run lint + npm run typecheck:core + визуальная проверка.


  • D1 — Основной Button: оставить красный→красный или перейти на красный→фиолетовый --grad-brand? Рекомендация: красный→фиолетовый (Фаза 2).
  • D2 — Цвет линий сетки: нейтральный (стиль сайта) — выбрано — вместо фирменного красного. Размер 32px (уменьшен примерно на 30% относительно исходных 46px после обратной связи от владельца — ячейки 46px выглядели слишком крупными в макете панели управления).
  • D3 — Выразительность chrome-элементов: боковая панель/заголовок сплошные — выбрано.
  • D4 — Сетка аутентификации/входа: ✅ ВЫПОЛНЕНО (Фаза 5) — непрозрачный bg-bg удалён из всех автономных полноэкранных обёрток (не только со страницы входа), поэтому сетка отображается на всех экранах. См. §4.6.
  • D5 — Посадочная страница: оставить анимированную заставку без изменений. Выбрано.
  • D6 — Радиус 14/9 во всём продукте: рекомендация: да (Фаза 2).
  • D7 — Фаза 1 выпускается первой: выбрано.
  • D8 — Ширина макета (Фаза 5): оболочка содержимого панели управления была ограничена max-w-7xl (1280px), из-за чего на больших мониторах содержимое центрировалось с широкими пустыми полями по бокам. ✅ ВЫПОЛНЕНО — ограничение увеличено до адаптивного max-w-[3840px] (настоящие 4K): теперь содержимое следует за шириной области просмотра примерно до 4K и центрируется только за её пределами (DashboardLayout.tsx). Намеренно узкие страницы по-прежнему остаются узкими (ProviderOnboardingWizard max-w-5xl, Rtk/CavemanContextPageClient max-w-6xl).
  • D9 — Непрозрачные таблицы данных (Фаза 6): после того как область содержимого панели управления стала прозрачной (чтобы через неё была видна фоновая сетка, Фаза 5), в таблицах данных, чей контейнер не был непрозрачной поверхностью, сетка просвечивала через прозрачные чётные строки / зебру с низкой альфа-прозрачностью. ✅ ВЫПОЛНЕНО — каждая таблица без Card теперь отрисовывает bg-surface (или, для примитива <DataTable>, background: var(--color-surface) в контейнере прокрутки). Исправлены: DataTable (примитив), ProxyLogger/RequestLoggerV2 (их оттенок <Card> bg-black/5 dark:bg-black/20 имел приоритет над bg-surface Card из-за tailwind-merge → прозрачность около 95%), BatchListTab/FilesListTab/CacheEntriesTab/ReasoningCacheTab/cache page/FreePoolTab/ModelMappingTable/HeaderTable, а также две CSS-grid-«таблицы» в представлениях кэша (bg-surface/35 → bg-surface). Таблицы, уже находящиеся внутри <Card>/Modal, были проверены на непрозрачность и намеренно оставлены без изменений (bg-surface там был бы избыточной пустой операцией). Саму сетку изменять не потребовалось — body::before панели управления побайтово идентичен сайту (--grid-size: 32px); любая «более крупная сетка», видимая в работающем экземпляре, относится к устаревшей сборке до #4143, а не к текущему коду. Контролируется тестом tests/unit/design-grid-background.test.ts (блок Фазы 6).

  • Без изменения палитры — цвета уже соответствуют требованиям; мы лишь добавляем недостающие токены. Нулевой риск изменения цветовой схемы продукта.
  • Без изменения механизма тем — сохраняем .dark + хранилище Zustand.
  • Изменение радиусов скругления (этап 2) носит масштабный характер — затрагивает каждую карточку, кнопку и поле ввода; перед слиянием визуально проверьте насыщенные элементами экраны (таблицы, модальные окна).
  • Таблицы (C3) содержат больше всего жёстко заданных стилей и имеют наибольшую поверхность регрессий — вынесите их в отдельный PR.

Область Путь
Токены панели управления src/app/globals.css (:root, .dark, @theme inline, body, body::before)
Хранилище темы src/store/themeStore.ts, src/shared/components/ThemeProvider.tsx, src/shared/constants/appConfig.ts:9-11
Оболочка панели (сетка разблокируется здесь) src/shared/components/layouts/DashboardLayout.tsx
Элементы интерфейса src/shared/components/Header.tsx:207, src/shared/components/Sidebar.tsx:430
Пример реализации сетки src/app/landing/page.tsx:16-26
Примитивы src/shared/components/{Button,Card,Input,Select,Badge,Modal,Toggle,SegmentedControl,Loading,Tooltip,DataTable}.tsx
Источники цветов статусов flow/edgeStyles.ts, TokenHealthBadge.tsx, DegradationBadge.tsx, logTableStyles.ts
Утилита cn src/shared/utils/cn.ts
Защитный тест этапа 1 tests/unit/design-grid-background.test.ts
Эталон сайта _mono_repo/omnirouteSite/css/tokens.css, css/base.css

Исходный код OmniRoute (a58000c7685f)

HagiCode

HagiCode — агентная среда разработки со структурированными процессами, параллельным выполнением несколькими агентами и интерфейсами Hero Dungeon.

Превращайте идеи в полезное ПО с более умным, быстрым и увлекательным агентным рабочим процессом.

Главный экран HagiCode в светлой теме
  • SmartСтруктурированные процессы превращают намерение в исполнимый путь от идеи до готового изменения.
  • EfficientМультиагентные процессы параллельно продвигают исследование, реализацию и проверку.
  • FunHero Dungeon делает длительную совместную разработку наглядной и увлекательной.
Перейти на HagiCode