Files
mdm-ordinis/ordinis-admin-ui/src/styles.css
T
Zimin A.N. 07a748be53 fix(admin-ui): полноценные стили DayPicker'а + 1 месяц в PeriodFilter
PeriodFilter рендерил DayPicker numberOfMonths={2} со стандартными
rdp-* стилями — `.rdp-months` имеет `flex-wrap: wrap`, при недостатке
ширины два месяца стакались вертикально, ячейки большие/разреженные,
selected/hover состояний под наш accent не было. Выглядело rough.

Два фикса:

1. PeriodFilter → numberOfMonths={1}. Для audit-фильтрации одного
   месяца достаточно (presets покрывают «7д/30д/Этот месяц»; календарь
   нужен для arbitrary range). Простой layout, не зависит от ширины
   popover'а.

2. Полноценные стили `.ord-day-picker` в styles.css (~80 строк):
   - tight cells (2rem × 2rem)
   - hover state на surface-2
   - selected day → accent bg + on-accent text
   - range_middle → accent-bg, обнулённый radius
   - range_start/end → polo'асn'ое closure radius
   - today → 1px outline
   - outside-month days → mute opacity
   - nav buttons → accent цвет + hover
   - mono font на числа (JetBrains Mono), Inter на caption/weekday

Бенефит распространяется и на DateRangePicker (использует тот же класс).
flex-wrap: nowrap override — на случай если когда-то понадобится
numberOfMonths={2} в другом контексте, не будет вертикального стека.
2026-05-25 17:45:39 +03:00

555 lines
19 KiB
CSS
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.
@import 'tailwindcss';
@import 'tw-animate-css';
/* Google Fonts — Inter / JetBrains Mono / Tektur (handoff spec).
* Stage 3.9: @nstart/ui удалён, fonts/theme imports + @source directive
* убраны. Tailwind v4 теперь scan'ит только наш src/. */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Tektur:wght@500;600;700&display=swap');
/* === Dark mode trigger ===
* shadcn использует .dark class по умолчанию. Handoff просит [data-theme="dark"]
* на <html>. Custom variant связывает оба пути — компоненты с `dark:` modifier
* срабатывают при любом из условий. */
@custom-variant dark (&:where([data-theme='dark'], [data-theme='dark'] *));
/* === DESIGN TOKENS — Light theme (Earth, default :root) === */
:root {
/* Earthy light v3 — neutralized page bg per user preference.
* Прошлая версия #fbf8ee (handoff intent) казалась слишком жёлтой; теперь
* используем нейтральный warm white #faf9f5. Полную Earth cream версию
* можно вернуть через [data-theme="earth-cream"] (opt-in). */
--color-ink: #1a1714;
--color-ink-2: #52483d;
--color-mute: #8c8276;
--color-bg: #faf9f5;
--color-surface: #ffffff;
--color-surface-2: #f5f3ec;
--color-line: #e6e1d4;
--color-line-2: #efeae0;
--color-navy: #3a2818;
--color-accent: #b05a2e;
/* Alpha 12% across all themes per DESIGN.md §17 — was 11% (light) / 16%
* (dark); drift caused subtle saturation difference в info badges between
* themes. Visually negligible diff на light, slightly weaker tint в dark. */
--color-accent-bg: rgb(176 90 46 / 12%);
--color-on-accent: #ffffff;
--color-green: #4f7038;
--color-green-bg: #e4ead0;
--color-warn: #a8762a;
--color-warn-bg: #f6e8bc;
--color-pink: #a64636;
--color-pink-bg: #f4d9cd;
--color-purple: #7a4a2e;
/* Scope tint backgrounds (per ui-kit) — для PUBLIC/INTERNAL/TENANT chips */
--bg-public: rgb(176 90 46 / 13%);
--bg-internal: #f6e8bc;
--bg-tenant: #f4d9cd;
/* Workflow state backgrounds (DRAFT/LIVE/DEPRECATED) */
--bg-draft: var(--color-line-2);
--bg-live: #e4ead0;
--bg-deprec: #f4d9cd;
/* Alert variant tokens (info/warn/err/ok) — bg + border. shadcn Alert
* примет эти через variant overrides. */
--al-info-bg: rgb(176 90 46 / 9%);
--al-info-bd: rgb(176 90 46 / 28%);
--al-warn-bg: #f6e8bc;
--al-warn-bd: #e2c982;
--al-err-bg: #f4d9cd;
--al-err-bd: #dfb29e;
--al-ok-bg: #e4ead0;
--al-ok-bd: #bcc995;
/* Shadow tokens per ui-kit (used by drawer/modal/toast) */
--shadow-l: 0 12px 30px -16px rgba(50, 30, 15, 0.18);
--shadow-m: 0 12px 28px -12px rgba(50, 30, 15, 0.22);
--shadow-toast: 0 12px 30px -10px rgba(40, 25, 10, 0.38);
--shadow-drawer: 0 1px 2px rgba(50, 30, 12, 0.18);
/* Toast cap-style header color — orange-cream per handoff line 304 */
--toast-cap: #d4a07a;
/* Avatar background tones (success/error scope) */
--avt-pink-bg: #f4d9cd;
--avt-green-bg: #e4ead0;
/* shadcn semantic tokens — re-mapped onto наши значения */
--background: var(--color-bg);
--foreground: var(--color-ink);
--card: var(--color-surface);
--card-foreground: var(--color-ink);
--popover: var(--color-surface);
--popover-foreground: var(--color-ink);
--primary: var(--color-navy);
--primary-foreground: var(--color-on-accent);
--secondary: var(--color-surface-2);
--secondary-foreground: var(--color-ink);
--muted: var(--color-surface-2);
--muted-foreground: var(--color-mute);
--accent: var(--color-accent);
--accent-foreground: var(--color-on-accent);
--destructive: var(--color-pink);
--destructive-foreground: var(--color-on-accent);
--border: var(--color-line);
--input: var(--color-line);
--ring: var(--color-accent);
}
/* Claude-light theme — warm cream less saturated. Same accent/navy palette
* но bg более нейтральный (для users которые не любят жёлтый Earth). */
[data-theme='claude-light'] {
--color-ink: #2c2b27;
--color-ink-2: #5a564f;
--color-mute: #8a8474;
--color-bg: #faf9f5;
--color-surface: #ffffff;
--color-surface-2: #f5f3ec;
--color-line: #e6e1d4;
--color-line-2: #efeae0;
--color-navy: #d97757;
--color-accent: #d97757;
--color-accent-bg: rgb(217 119 87 / 12%);
--color-on-accent: #ffffff;
--color-green: #4a7d4f;
--color-green-bg: #dde9da;
--color-warn: #b97c2f;
--color-warn-bg: #fcecc7;
--color-pink: #b34d6a;
--color-pink-bg: #f8d8de;
--color-purple: #a85d3e;
--bg-public: rgb(217 119 87 / 14%);
--bg-internal: #fcecc7;
--bg-tenant: #f8d8de;
--bg-draft: #efeae0;
--bg-live: #dde9da;
--bg-deprec: #f8d8de;
--al-info-bg: rgb(217 119 87 / 10%);
--al-info-bd: rgb(217 119 87 / 30%);
--al-warn-bg: #fcecc7;
--al-warn-bd: #ecd393;
--al-err-bg: #f8d8de;
--al-err-bd: #ecb6c2;
--al-ok-bg: #dde9da;
--al-ok-bd: #bdd3b9;
--shadow-l: 0 12px 30px -16px rgba(89, 67, 40, 0.22);
--shadow-m: 0 12px 28px -12px rgba(89, 67, 40, 0.20);
--shadow-toast: 0 14px 36px -10px rgba(89, 67, 40, 0.30);
--shadow-drawer: 0 1px 2px rgba(89, 67, 40, 0.18);
--toast-cap: #f5c8b3;
--avt-pink-bg: #f8d8de;
--avt-green-bg: #dde9da;
--background: var(--color-bg);
--foreground: var(--color-ink);
--card: var(--color-surface);
--card-foreground: var(--color-ink);
--popover: var(--color-surface);
--popover-foreground: var(--color-ink);
--primary: var(--color-accent);
--primary-foreground: var(--color-on-accent);
--secondary: var(--color-surface-2);
--secondary-foreground: var(--color-ink);
--muted: var(--color-surface-2);
--muted-foreground: var(--color-mute);
--accent: var(--color-accent);
--accent-foreground: var(--color-on-accent);
--destructive: var(--color-pink);
--destructive-foreground: var(--color-on-accent);
--border: var(--color-line);
--input: var(--color-line);
--ring: var(--color-accent);
}
[data-theme='dark'] {
--color-ink: #f0ece4;
--color-ink-2: #c8c1b3;
--color-mute: #8a8474;
--color-bg: #1c1a17;
--color-surface: #262421;
--color-surface-2: #2e2b27;
--color-line: #3a3733;
--color-line-2: #2d2a27;
--color-navy: #d97757;
--color-accent: #d97757;
/* Alpha 12% normalized per DESIGN.md §17 — was 16% on dark theme. */
--color-accent-bg: rgb(217 119 87 / 12%);
--color-on-accent: #1c1a17;
--color-green: #6fa97d;
--color-green-bg: rgb(111 169 125 / 18%);
--color-warn: #d4a653;
--color-warn-bg: rgb(212 166 83 / 18%);
--color-pink: #d18272;
--color-pink-bg: rgb(209 130 114 / 20%);
--color-purple: #a991d4;
--bg-public: rgb(217 119 87 / 18%);
--bg-internal: rgb(212 166 83 / 18%);
--bg-tenant: rgb(209 130 114 / 20%);
--bg-draft: #2d2a27;
--bg-live: rgb(111 169 125 / 20%);
--bg-deprec: rgb(209 130 114 / 20%);
--al-info-bg: rgb(217 119 87 / 10%);
--al-info-bd: rgb(217 119 87 / 35%);
--al-warn-bg: rgb(212 166 83 / 10%);
--al-warn-bd: rgb(212 166 83 / 35%);
--al-err-bg: rgb(209 130 114 / 10%);
--al-err-bd: rgb(209 130 114 / 35%);
--al-ok-bg: rgb(111 169 125 / 10%);
--al-ok-bd: rgb(111 169 125 / 35%);
--shadow-l: 0 12px 30px -16px rgba(0, 0, 0, 0.6);
--shadow-m: 0 12px 28px -12px rgba(0, 0, 0, 0.55);
--shadow-toast: 0 14px 36px -10px rgba(0, 0, 0, 0.7);
--shadow-drawer: 0 1px 2px rgba(0, 0, 0, 0.5);
--toast-cap: #f0c3ab;
--avt-pink-bg: rgb(209 130 114 / 22%);
--avt-green-bg: rgb(111 169 125 / 22%);
--background: var(--color-bg);
--foreground: var(--color-ink);
--card: var(--color-surface);
--card-foreground: var(--color-ink);
--popover: var(--color-surface);
--popover-foreground: var(--color-ink);
--primary: var(--color-navy);
--primary-foreground: var(--color-on-accent);
--secondary: var(--color-surface-2);
--secondary-foreground: var(--color-ink);
--muted: var(--color-surface-2);
--muted-foreground: var(--color-mute);
--accent: var(--color-accent);
--accent-foreground: var(--color-on-accent);
--destructive: var(--color-pink);
--destructive-foreground: var(--color-on-accent);
--border: var(--color-line);
--input: var(--color-line);
--ring: var(--color-accent);
}
/* Tailwind v4 @theme inline — экспонирует CSS vars как utility classes
* (`bg-bg`, `text-ink`, `border-line`, `bg-accent` и т.д.). */
@theme inline {
--color-bg: var(--color-bg);
--color-surface: var(--color-surface);
--color-surface-2: var(--color-surface-2);
--color-ink: var(--color-ink);
--color-ink-2: var(--color-ink-2);
--color-mute: var(--color-mute);
--color-line: var(--color-line);
--color-line-2: var(--color-line-2);
--color-navy: var(--color-navy);
--color-accent: var(--color-accent);
--color-accent-bg: var(--color-accent-bg);
--color-on-accent: var(--color-on-accent);
--color-green: var(--color-green);
--color-green-bg: var(--color-green-bg);
--color-warn: var(--color-warn);
--color-warn-bg: var(--color-warn-bg);
--color-pink: var(--color-pink);
--color-pink-bg: var(--color-pink-bg);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: 'JetBrains Mono', 'SF Mono', monospace;
--font-display: 'Tektur', 'Inter', sans-serif;
/* Font scale — НЕ переопределяем Tailwind defaults. Все наши места
* используют семантические @utility ниже (text-body, text-cell, text-mono,
* text-title-*, text-cap). text-sm/text-xs/text-2xs не используются нигде
* после Stage 3.9 — @nstart/ui (который требовал --text-sm override)
* удалён. */
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
}
/* === Semantic typography utilities per design_handoff_ordinis_mdm/README.md ===
* Type system — 7 ролей, каждая соответствует месту в UI:
*
* text-title-xl — 22/600 modal title, section header (rare)
* text-title-lg — 17/600 page title в editor
* text-title-md — 15/600 dictionary card title
* text-body — 13/400 workhorse: body, buttons, tabs, inputs
* text-cell — 12.5/500 table cell text
* text-mono — 11/500 Mono: IDs, dates, FK refs
* text-cap — 10.5/600 Tektur UPPERCASE caption / section label
*
* Color и weight overrides поверх — добавлять отдельными utility classes
* (e.g. `text-body font-medium text-ink`). НЕ накладывать font-mono/
* font-display/uppercase/tracking — они baked в соответствующие utility. */
/* font-family: var(--font-sans) explicit — наследование может ломаться когда
* титлы используются внутри @nstart/ui Card/PageHeader которые sеtают
* font-family: Tektur на parent. После Stage 3.9 (nstart removed) можно
* убрать font-family из этих utilities — body inherit будет достаточно. */
@utility text-title-xl {
font-family: var(--font-sans);
font-size: 22px;
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.01em;
}
@utility text-title-lg {
font-family: var(--font-sans);
font-size: 17px;
font-weight: 600;
line-height: 1.25;
letter-spacing: -0.005em;
}
@utility text-title-md {
font-family: var(--font-sans);
font-size: 15px;
font-weight: 600;
line-height: 1.3;
}
@utility text-body {
font-family: var(--font-sans);
font-size: 13px;
font-weight: 400;
line-height: 1.45;
}
@utility text-cell {
font-family: var(--font-sans);
font-size: 12.5px;
font-weight: 500;
line-height: 1.35;
}
@utility text-mono {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 500;
line-height: 1.4;
}
@utility text-cap {
font-family: var(--font-display);
font-size: 10.5px;
font-weight: 600;
line-height: 1;
text-transform: uppercase;
letter-spacing: 0.10em;
}
html, body, #root {
height: 100%;
}
body {
background: var(--color-bg);
color: var(--color-ink);
font-family: var(--font-sans);
font-feature-settings: 'ss01', 'cv11', 'cv02', 'cv03', 'cv04';
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
/* Tektur uppercase caption helper — handoff .cap class */
.cap {
font-family: var(--font-display);
font-size: 10.5px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.10em;
color: var(--color-mute);
}
/* === GlobeLoader keyframes — orbital spinner (src/ui/components/globe-loader.tsx).
* Класс .globe-loader на wrapper'е активирует вложенные .globe-* animations
* через стандартные CSS animation rules. Transform-only — GPU-композитятся. */
@keyframes ord-globe-spin {
to { transform: rotate(360deg); }
}
@keyframes ord-globe-pulse {
0%, 100% { opacity: 0.18; }
50% { opacity: 0.42; }
}
/* transform-box default = view-box для SVG elements — transform-origin
* считается в SVG user coordinates (viewBox 0 0 200 200). 100px 100px =
* центр глобуса. Прошлая версия с transform-box: fill-box ломала орбиты
* (origin становился offset bounding-box'а каждой группы — арки летали
* вокруг неправильной точки). Прототип HTML использует default — copy. */
.globe-loader .globe-whirl {
transform-origin: 100px 100px;
animation: ord-globe-spin 3.6s linear infinite;
}
.globe-loader .globe-whirl-rev {
transform-origin: 100px 100px;
animation: ord-globe-spin 5.4s linear infinite reverse;
}
.globe-loader .globe-pulse {
transform-origin: 100px 100px;
animation: ord-globe-pulse 2.4s ease-in-out infinite;
}
/* Sphere/land внутри globe-loader rotates через d3 projection (rAF
* пересчитывает path d-attr), не через CSS transform — иначе land
* distort'илась бы при rotation (no projection math). */
@media (prefers-reduced-motion: reduce) {
.globe-loader .globe-whirl,
.globe-loader .globe-whirl-rev,
.globe-loader .globe-pulse {
animation: none;
}
}
/* === Legacy fallback: старые компоненты используют nstart token names
* (--color-carbon, --color-ultramarain etc). Маппим их на новые pending
* полная миграция. === */
:root,
[data-theme='dark'] {
--color-carbon: var(--color-ink);
--color-ultramarain: var(--color-accent);
--color-orbit: var(--color-warn);
--color-regolith: var(--color-line);
--color-asteroid: var(--color-mute);
}
/* === Graph edge flow — космо-анимация для активных связей.
* stroke-dashoffset animation создаёт ощущение потока частиц от source
* к target. Только на highlighted edges (hover state), default edges
* статичные dashed pattern. Respects prefers-reduced-motion. === */
@keyframes graph-edge-flow {
to { stroke-dashoffset: -28; }
}
.graph-edge-flow {
animation: graph-edge-flow 1.4s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.graph-edge-flow {
animation: none;
}
}
/* === react-day-picker — кастомизация под ordinis design system.
* Без этих overrides DayPicker рендерится дефолтными rdp-* стилями
* (большие квадратные ячейки, нет hover/selected состояний под наш
* accent). Класс `.ord-day-picker` ставится на корень DayPicker'а
* в DateRangePicker и PeriodFilter. === */
.ord-day-picker {
--rdp-day-width: 2rem;
--rdp-day-height: 2rem;
--rdp-day_button-width: 2rem;
--rdp-day_button-height: 2rem;
--rdp-day_button-border-radius: 6px;
--rdp-months-gap: 1rem;
--rdp-nav_button-height: 1.75rem;
--rdp-nav_button-width: 1.75rem;
--rdp-weekday-text-transform: lowercase;
--rdp-weekday-padding: 0.25rem 0;
--rdp-month_caption-font: 600 0.875rem/1.25 'Inter', sans-serif;
--rdp-day-font: 400 0.8125rem/1 'JetBrains Mono', monospace;
--rdp-weekday-font: 500 0.7rem/1 'Inter', sans-serif;
--rdp-accent-color: var(--color-accent);
--rdp-accent-background-color: var(--color-accent-bg);
font-family: 'Inter', sans-serif;
color: var(--color-ink);
}
/* Multi-month layout (если когда-то нужно): 2 столбца side-by-side
* вместо wrap'а вниз. */
.ord-day-picker .rdp-months {
flex-wrap: nowrap;
}
.ord-day-picker .rdp-month_caption {
padding: 0 0 0.5rem;
color: var(--color-ink);
}
.ord-day-picker .rdp-weekday {
color: var(--color-mute);
text-transform: lowercase;
font-weight: 500;
}
.ord-day-picker .rdp-day_button {
border-radius: 6px;
transition: background-color 120ms, color 120ms;
}
.ord-day-picker .rdp-day_button:hover:not(:disabled):not([aria-disabled='true']) {
background-color: var(--color-surface-2);
color: var(--color-ink);
}
.ord-day-picker .rdp-day.rdp-outside .rdp-day_button {
color: var(--color-mute);
opacity: 0.55;
}
.ord-day-picker .rdp-day.rdp-today .rdp-day_button {
font-weight: 600;
outline: 1px solid var(--color-line-2, var(--color-line));
outline-offset: -2px;
}
/* Selected range */
.ord-day-picker .rdp-day.rdp-selected .rdp-day_button {
background-color: var(--color-accent);
color: var(--color-on-accent, white);
font-weight: 500;
}
.ord-day-picker .rdp-day.rdp-range_middle .rdp-day_button {
background-color: var(--color-accent-bg);
color: var(--color-accent);
border-radius: 0;
}
.ord-day-picker .rdp-day.rdp-range_start .rdp-day_button {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.ord-day-picker .rdp-day.rdp-range_end .rdp-day_button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.ord-day-picker .rdp-day.rdp-range_start.rdp-range_end .rdp-day_button {
border-radius: 6px;
}
/* Nav buttons */
.ord-day-picker .rdp-button_previous,
.ord-day-picker .rdp-button_next {
border-radius: 6px;
color: var(--color-accent);
transition: background-color 120ms;
}
.ord-day-picker .rdp-button_previous:hover,
.ord-day-picker .rdp-button_next:hover {
background-color: var(--color-surface-2);
}
.ord-day-picker .rdp-button_previous:disabled,
.ord-day-picker .rdp-button_next:disabled {
opacity: 0.35;
cursor: not-allowed;
}