diff --git a/HDESIGN-CODE.md b/HDESIGN-CODE.md
new file mode 100644
index 0000000..6961496
--- /dev/null
+++ b/HDESIGN-CODE.md
@@ -0,0 +1,111 @@
+# HDESIGN CODE
+
+Документ фиксирует канон интерфейса NODE.DC, чтобы не обсуждать одни и те же правила повторно.
+
+## Источник цветов
+- Основной runtime-конфиг цветов: [design.config.json](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/design.config.json)
+- Рабочая web-копия: [plane-src/apps/web/design.config.json](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src/apps/web/design.config.json)
+- В рантайме используются CSS variables:
+ - `--nodedc-accent-rgb`
+ - `--nodedc-card-passive-rgb`
+ - `--nodedc-card-active-rgb`
+
+## Цветовые правила
+- `accent_rgb`: акцентный цвет интерфейса.
+- `passive_card_rgb`: пассивные карточки.
+- `active_card_rgb`: активные карточки и primary CTA в зелёной теме.
+- Primary/active элементы используют акцентный или `active_card_rgb`.
+- Secondary элементы не должны иметь ярких outline и цветных рамок без явной причины.
+
+## Радиусы
+- Главные модалки и большие surface-контейнеры: `1.75rem`
+- Стандартные glass-карточки и settings-карточки: `1.35rem`
+- Поля ввода, селекты, secondary/primary кнопки, chip-кнопки: `1.25rem`
+- Малые круглые action-кнопки: `999px` или полный круг при квадратной коробке
+
+## Outline и рамки
+- Внешние outline у контролов запрещены.
+- Синий browser outline должен быть снят и заменён на нормальный hover/focus surface.
+- Если нужен контур, он должен быть частью дизайна:
+ - мягкий glass border
+ - акцентный border для drag/drop или active-state
+- Красные технические outline, debug-рамки и случайные browser-box shadow запрещены.
+
+## Glass и фон
+- Popup, dropdown, modal, sidebar overlays и settings-карточки используют matte black glass.
+- База:
+ - тёмный полупрозрачный фон
+ - `backdrop-filter: blur(...)`
+ - мягкая стеклянная граница
+- Popup не должны выглядеть просто прозрачными. Если blur не читается, проблема в слое рендера, а не в одном `rgba`.
+
+## Кнопки
+- Все кнопки без жёсткого outline.
+- Primary button:
+ - фон: акцентный или `active_card_rgb`
+ - текст: чёрный или очень тёмный, если фон светлый
+ - hover: более светлая версия того же цвета
+- Save/update button:
+ - если это зафиксированный green CTA, текст должен быть контрастным и читаемым
+ - hover осветляет текущий тон, а не уходит в синий
+- Secondary button:
+ - тёмный glass фон
+ - без border-outline
+ - hover немного светлее базового surface
+- Danger button:
+ - без кислотно-красных рамок
+ - мягкий danger surface
+
+## Поля и селекты
+- Все поля ввода, textarea, select, chip-select:
+ - скруглённые
+ - без внешних outline
+ - glass background
+ - единая вертикальная высота для одного класса контролов
+- Placeholder и label должны быть читаемы и не прилипать к краям.
+
+## Toolbar и верхние панели
+- Элементы верхней панели центрируются по одной горизонтальной оси.
+- Активный layout/tool mode выделяется кругом акцентного цвета, не квадратной плашкой.
+- Кнопки `Отображение`, `Аналитика`, `Добавить рабочий элемент`:
+ - одинаковая высота
+ - каноничные радиусы
+ - нормальные горизонтальные paddings, чтобы текст не лип к краям
+
+## Карточки
+- Внутренние карточки строятся по симметричным верхним и нижним padding.
+- Верхняя ось:
+ - аватар
+ - имя
+ - вторичная строка
+ - action-circle справа
+ должны сидеть на согласованной геометрии
+- Нижняя ось:
+ - assignee bubbles
+ - дата
+ должны быть симметричны верхней
+
+## Dropdown и popup
+- Все dropdown/popup приводятся к единому matte glass канону.
+- Запрещены:
+ - квадратные active-box вокруг круглых кнопок
+ - жёсткие border-outline
+ - светлый фон, если основной экран тёмный
+- Search shell внутри popup должен использовать тот же стиль, что и сам popup.
+
+## Drag and drop
+- Drag overlay использует акцентный контур.
+- Delete dropzone:
+ - без красного технического свечения
+ - текст локализован
+ - акцентный outline допустим
+
+## Тексты
+- Пользовательский UI на русском, если экран русифицирован.
+- Не оставлять смешанные подписи вида `Created at / Updated at / Label / State group`, если экран уже на русском.
+
+## Правило внедрения
+- Новый экран или popup не стилизуется локально “на глаз”.
+- Сначала используется существующий shared-класс или shared-component.
+- Если shared-слоя нет, создаётся reusable-класс/компонент и уже через него приводятся все похожие места.
+- Цель: не точечная покраска одного окна, а единый системный канон.
diff --git a/design.config.json b/design.config.json
deleted file mode 100644
index 012a87a..0000000
--- a/design.config.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "nodedc": {
- "accent_rgb": [195, 255, 102],
- "passive_card_rgb": [42, 43, 46],
- "active_card_rgb": [195, 255, 102]
- }
-}
diff --git a/design.config.json b/design.config.json
new file mode 120000
index 0000000..c65fb28
--- /dev/null
+++ b/design.config.json
@@ -0,0 +1 @@
+plane-src/apps/web/design.config.json
\ No newline at end of file
diff --git a/plane-src/apps/web/app/root.tsx b/plane-src/apps/web/app/root.tsx
index e390f35..236064a 100644
--- a/plane-src/apps/web/app/root.tsx
+++ b/plane-src/apps/web/app/root.tsx
@@ -4,7 +4,7 @@
* See the LICENSE file for details.
*/
-import type { ReactNode } from "react";
+import type { CSSProperties, ReactNode } from "react";
import Script from "next/script";
import { Links, Meta, Outlet, Scripts } from "react-router";
import type { LinksFunction } from "react-router";
@@ -22,6 +22,7 @@ import icon512 from "@/app/assets/icons/icon-512x512.png?url";
import ogImage from "@/app/assets/og-image.png?url";
import globalStyles from "@/styles/globals.css?url";
import type { Route } from "./+types/root";
+import designConfig from "../design.config.json";
// components
import { LogoSpinner } from "@/components/common/logo-spinner";
// local
@@ -35,6 +36,12 @@ import "@fontsource/ibm-plex-mono";
const APP_TITLE = "NODE.DC | Self-hosted task management workspace.";
+const designConfigStyle = {
+ "--nodedc-accent-rgb": designConfig.nodedc.accent_rgb.join(" "),
+ "--nodedc-card-passive-rgb": designConfig.nodedc.passive_card_rgb.join(" "),
+ "--nodedc-card-active-rgb": designConfig.nodedc.active_card_rgb.join(" "),
+} as CSSProperties;
+
export const links: LinksFunction = () => [
{ rel: "icon", type: "image/png", sizes: "32x32", href: favicon32 },
{ rel: "icon", type: "image/png", sizes: "16x16", href: favicon16 },
@@ -58,7 +65,7 @@ export function Layout({ children }: { children: ReactNode }) {
const isSessionRecorderEnabled = parseInt(process.env.VITE_ENABLE_SESSION_RECORDER || "0");
return (
-
+