Files
iistwin/client-di2/src/index.css
Ильяс Султанов 968010c411 DI2: визуальный рестайл — графики и цвета по токенам скина, таблицы по UI Style Guide, мобильная нижняя панель, переключатель темы
- recharts: все hex/hsl палитры → var(--chart-1..5), семантика → var(--info/success/warning/destructive)
- палитровые Tailwind-классы (red/blue/green/amber и т.д.) → семантические классы через color-mix (Tailwind v3 не генерирует alpha для var-цветов)
- ui/table.tsx: шапка по гайду (bg-muted/50, text-[12px]), строки h-[var(--table-row-height,40px)], ячейки px-3, hover muted 40%
- lib/field-styles.ts (порт из основного клиента) + fieldControlClass в column-filter
- app-sidebar: брендинг «ОмегаПром / Финансы», компактные отступы
- bottom-nav.tsx: мобильная нижняя панель (Обзор/Разделы/Настройки) по референсу FinanceBottomNav, safe-area-bottom в index.css
- переключатель темы Sun/Moon в хедере (useDi2Theme), SidebarTrigger скрыт на мобильном
- заголовки страниц text-lg font-semibold
2026-09-07 00:04:49 +03:00

290 lines
9.8 KiB
CSS

/* Общий CSS встроенных скинов iistwin (generateAllSkinsCss → [data-skin][data-theme]).
Путь относительно этого файла (client-di2/src/index.css). */
@import '../../client/src/themes/styles/generated-skins.css';
/* Конфиг Tailwind субаппа DI2 — иначе postcss-плагин возьмёт корневой конфиг iistwin */
@config "../tailwind.config.ts";
@tailwind base;
@tailwind components;
@tailwind utilities;
/*
* Fallback переменные для случая, когда data-skin/data-theme ещё не применены
* (до монтирования Di2ThemeProvider). Основные значения берутся из generated-skins.css.
* Блоки скопированы из client/src/index.css — DI2 теперь на тех же токенах, что iistwin.
*/
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(220 10% 15%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(220 10% 15%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(220 10% 15%);
--primary: hsl(220 10% 15%);
--primary-foreground: hsl(0 0% 100%);
--secondary: hsl(220 5% 96%);
--secondary-foreground: hsl(220 10% 15%);
--muted: hsl(220 5% 96%);
--muted-foreground: hsl(220 5% 45%);
--accent: hsl(220 5% 93%);
--accent-foreground: hsl(220 10% 15%);
--destructive: hsl(0 72% 51%);
--destructive-foreground: hsl(0 0% 100%);
--border: hsl(220 5% 90%);
--input: hsl(0 0% 100%);
--ring: hsl(220 10% 15%);
--status-done: hsl(142 50% 45%);
--status-progress: hsl(45 90% 50%);
--status-overdue: hsl(0 70% 55%);
--status-paused: hsl(220 5% 60%);
--warning: hsl(48 96% 89%);
--warning-foreground: hsl(25 83% 31%);
--success: hsl(141 84% 93%);
--success-foreground: hsl(142 72% 24%);
--info: hsl(214 95% 93%);
--info-foreground: hsl(224 71% 40%);
--chart-1: hsl(220 10% 25%);
--chart-2: hsl(142 50% 45%);
--chart-3: hsl(45 90% 50%);
--chart-4: hsl(200 70% 50%);
--chart-5: hsl(0 70% 55%);
--sidebar: hsl(0 0% 100%);
--sidebar-foreground: hsl(220 10% 15%);
--sidebar-primary: hsl(220 10% 15%);
--sidebar-primary-foreground: hsl(0 0% 100%);
--sidebar-accent: hsl(220 5% 96%);
--sidebar-accent-foreground: hsl(220 10% 15%);
--sidebar-border: hsl(220 5% 92%);
--sidebar-ring: hsl(220 10% 15%);
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-mono: "DM Mono", "SF Mono", "Monaco", "Inconsolata", monospace;
--radius: 0.25rem;
--shadow-2xs: none;
--shadow-xs: none;
--shadow-sm: none;
--shadow: none;
--shadow-md: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
--shadow-lg: 0 1px 3px 0 rgba(0, 0, 0, 0.05);
--shadow-xl: 0 2px 4px 0 rgba(0, 0, 0, 0.05);
--shadow-2xl: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
--tracking-normal: -0.01em;
--spacing: 0.25rem;
--elevate-1: rgba(0, 0, 0, 0.05);
--elevate-2: rgba(0, 0, 0, 0.08);
/* DI2-only переменные: теперь производные от семантических токенов скинов.
Раньше считались relative-color из голых HSL-троек — больше не нужно. */
--card-border: var(--border);
--popover-border: var(--border);
--primary-border: color-mix(in oklab, var(--primary) 82%, black);
--secondary-border: color-mix(in oklab, var(--secondary) 82%, black);
--muted-border: color-mix(in oklab, var(--muted) 82%, black);
--accent-border: color-mix(in oklab, var(--accent) 82%, black);
--destructive-border: color-mix(in oklab, var(--destructive) 82%, black);
--sidebar-primary-border: color-mix(in oklab, var(--sidebar-primary) 82%, black);
--sidebar-accent-border: color-mix(in oklab, var(--sidebar-accent) 82%, black);
--button-outline: color-mix(in oklab, var(--primary) 35%, transparent);
--badge-outline: color-mix(in oklab, var(--muted-foreground) 35%, transparent);
}
.dark {
--elevate-1: rgba(255, 255, 255, 0.05);
--elevate-2: rgba(255, 255, 255, 0.08);
--background: hsl(220 10% 8%);
--foreground: hsl(220 5% 92%);
--card: hsl(220 10% 10%);
--card-foreground: hsl(220 5% 92%);
--popover: hsl(220 10% 10%);
--popover-foreground: hsl(220 5% 92%);
--primary: hsl(0 0% 100%);
--primary-foreground: hsl(220 10% 8%);
--secondary: hsl(220 10% 14%);
--secondary-foreground: hsl(220 5% 92%);
--muted: hsl(220 10% 14%);
--muted-foreground: hsl(220 5% 55%);
--accent: hsl(220 10% 16%);
--accent-foreground: hsl(220 5% 92%);
--destructive: hsl(0 70% 55%);
--destructive-foreground: hsl(0 0% 100%);
--border: hsl(220 10% 18%);
--input: hsl(220 10% 12%);
--ring: hsl(0 0% 100%);
--status-done: hsl(142 50% 55%);
--status-progress: hsl(45 90% 55%);
--status-overdue: hsl(0 70% 60%);
--status-paused: hsl(220 5% 55%);
--warning: hsl(32 92% 14%);
--warning-foreground: hsl(43 96% 56%);
--success: hsl(144 80% 10%);
--success-foreground: hsl(142 71% 58%);
--info: hsl(224 64% 21%);
--info-foreground: hsl(213 97% 81%);
--chart-1: hsl(220 5% 70%);
--chart-2: hsl(142 50% 55%);
--chart-3: hsl(45 90% 55%);
--chart-4: hsl(200 70% 55%);
--chart-5: hsl(0 70% 60%);
--sidebar: hsl(220 10% 8%);
--sidebar-foreground: hsl(220 5% 92%);
--sidebar-primary: hsl(0 0% 100%);
--sidebar-primary-foreground: hsl(220 10% 8%);
--sidebar-accent: hsl(220 10% 14%);
--sidebar-accent-foreground: hsl(220 5% 92%);
--sidebar-border: hsl(220 10% 16%);
--sidebar-ring: hsl(0 0% 100%);
--radius: 0.25rem;
--shadow-md: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
--shadow-lg: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
--shadow-xl: 0 4px 6px -1px rgba(0, 0, 0, 0.2);
--shadow-2xl: 0 8px 10px -2px rgba(0, 0, 0, 0.25);
}
@layer base {
* {
@apply border-border;
}
body {
@apply font-sans antialiased bg-background text-foreground;
}
}
/**
* Using the elevate system.
* Automatic contrast adjustment.
*
* <element className="hover-elevate" />
* <element className="active-elevate-2" />
*
* // Using the tailwind utility when a data attribute is "on"
* <element className="toggle-elevate data-[state=on]:toggle-elevated" />
* // Or manually controlling the toggle state
* <element className="toggle-elevate toggle-elevated" />
*
* Elevation systems have to handle many states.
* - not-hovered, vs. hovered vs. active (three mutually exclusive states)
* - toggled or not
* - focused or not (this is not handled with these utilities)
*
* Even without handling focused or not, this is six possible combinations that
* need to be distinguished from eachother visually.
*/
@layer utilities {
/* Hide ugly search cancel button in Chrome until we can style it properly */
input[type="search"]::-webkit-search-cancel-button {
@apply hidden;
}
/* Placeholder styling for contentEditable div */
[contenteditable][data-placeholder]:empty::before {
content: attr(data-placeholder);
color: var(--muted-foreground);
pointer-events: none;
}
/* .no-default-hover-elevate/no-default-active-elevate is an escape hatch so consumers of
* buttons/badges can remove the automatic brightness adjustment on interactions
* and program their own. */
.no-default-hover-elevate {}
.no-default-active-elevate {}
/**
* Toggleable backgrounds go behind the content. Hoverable/active goes on top.
* This way they can stack/compound. Both will overlap the parent's borders!
* So borders will be automatically adjusted both on toggle, and hover/active,
* and they will be compounded.
*/
.toggle-elevate::before,
.toggle-elevate-2::before {
content: "";
pointer-events: none;
position: absolute;
inset: 0px;
/*border-radius: inherit; match rounded corners */
border-radius: inherit;
z-index: -1;
/* sits behind content but above backdrop */
}
.toggle-elevate.toggle-elevated::before {
background-color: var(--elevate-2);
}
/* If there's a 1px border, adjust the inset so that it covers that parent's border */
.border.toggle-elevate::before {
inset: -1px;
}
/* Does not work on elements with overflow:hidden! */
.hover-elevate:not(.no-default-hover-elevate),
.active-elevate:not(.no-default-active-elevate),
.hover-elevate-2:not(.no-default-hover-elevate),
.active-elevate-2:not(.no-default-active-elevate) {
position: relative;
z-index: 0;
}
.hover-elevate:not(.no-default-hover-elevate)::after,
.active-elevate:not(.no-default-active-elevate)::after,
.hover-elevate-2:not(.no-default-hover-elevate)::after,
.active-elevate-2:not(.no-default-active-elevate)::after {
content: "";
pointer-events: none;
position: absolute;
inset: 0px;
/*border-radius: inherit; match rounded corners */
border-radius: inherit;
z-index: 999;
/* sits in front of content */
}
.hover-elevate:hover:not(.no-default-hover-elevate)::after,
.active-elevate:active:not(.no-default-active-elevate)::after {
background-color: var(--elevate-1);
}
.hover-elevate-2:hover:not(.no-default-hover-elevate)::after,
.active-elevate-2:active:not(.no-default-active-elevate)::after {
background-color: var(--elevate-2);
}
/* If there's a 1px border, adjust the inset so that it covers that parent's border */
.border.hover-elevate:not(.no-hover-interaction-elevate)::after,
.border.active-elevate:not(.no-active-interaction-elevate)::after,
.border.hover-elevate-2:not(.no-hover-interaction-elevate)::after,
.border.active-elevate-2:not(.no-active-interaction-elevate)::after,
.border.hover-elevate:not(.no-hover-interaction-elevate)::after {
inset: -1px;
}
}
/* Нижняя панель на мобильном: учёт safe-area inset (паттерн основного клиента) */
.safe-area-bottom {
padding-bottom: env(safe-area-inset-bottom, 0);
}
@supports (padding-bottom: env(safe-area-inset-bottom)) {
.safe-area-bottom {
padding-bottom: max(12px, env(safe-area-inset-bottom));
}
}