Implement M1.5 visual design foundation: tokens, IBM Plex, theme toggle, chart themes

This commit is contained in:
2026-06-05 01:29:10 +03:00
parent 547edb85e4
commit 22f0556ef8
31 changed files with 713 additions and 136 deletions
+116 -35
View File
@@ -1,47 +1,128 @@
/*
* Design tokens. Borrowed in spirit from Syto: a single source of truth for
* colors/spacing/typography, themable by overriding the custom properties on
* a [data-theme] root. Concrete values are placeholders pending the design pass.
* Design tokens — the settled values from docs/architecture/09-visual-design.md
* (ported from visual-specimen.html). A single source of truth for type, spacing,
* color, shape, and motion, themed by overriding the role tokens on a
* `[data-theme]` root. Components consume these tokens ONLY — never raw hexes.
*
* Inspired by the IBM Design Language / Carbon (structure borrowed, color free);
* Carbon is not a dependency — the values are transcribed. See doc §2 for the
* deliberate divergences (square chrome, open accent/theming).
*/
:root {
/* Palette */
--color-bg: #ffffff;
--color-surface: #f7f7fa;
--color-border: #e2e2ea;
--color-text: #1a1a2e;
--color-text-muted: #6b6b80;
--color-accent: #3b5bdb;
--color-accent-contrast: #ffffff;
/* Status */
--color-success: #2f9e44;
--color-error: #e03131;
--color-warning: #f08c00;
--color-info: #1971c2;
/* Typography */
--font-sans: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--font-mono: ui-monospace, 'SF Mono', 'Cascadia Code', Menlo, monospace;
/* Type — IBM Plex (self-hosted via @fontsource, wired in main.tsx) */
--font-sans: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
--font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', 'Cascadia Code', Menlo, monospace;
--font-size-base: 14px;
/* Spacing scale */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
/* Spacing — 8px base unit (2/4 as sub-steps) */
--space-1: 2px;
--space-2: 4px;
--space-3: 8px;
--space-4: 12px;
--space-5: 16px;
--space-6: 24px;
--space-7: 32px;
--space-8: 48px;
--space-9: 64px;
/* Shape — square chrome (icons keep their own rounded geometry) */
--radius: 0px;
--border-width: 1px;
/* Layout */
--header-height: 48px;
--radius: 6px;
/* Motion — productive only; neutralized under prefers-reduced-motion in base.css */
--dur-fast: 70ms;
--dur-fast-2: 110ms;
--dur-moderate: 150ms;
--ease: cubic-bezier(0.2, 0, 0.38, 0.9);
/* Status — color carries meaning only (light defaults; overridden in dark) */
--support-error: #da1e28;
--support-success: #198038;
--support-warning: #f1c21b;
--support-info: #0043ce;
--on-status: #ffffff;
/* Focus ring defaults to the accent. */
--focus: var(--accent);
}
[data-theme='experimental'] {
--color-bg: #16161f;
--color-surface: #1f1f2c;
--color-border: #2c2c3a;
--color-text: #e8e8f0;
--color-text-muted: #9a9ab0;
--color-accent: #748ffc;
--color-accent-contrast: #0b0b12;
/* --- Neutral roles: LIGHT theme (Carbon gray ramp) --- */
[data-theme='light'] {
--bg: #ffffff;
--layer-01: #f4f4f4;
--layer-02: #e0e0e0;
--border: #e0e0e0;
--border-strong: #c6c6c6;
--text: #161616;
--text-secondary: #525252;
--text-placeholder: #a8a8a8;
}
/* --- Neutral roles: DARK theme --- */
[data-theme='dark'] {
--bg: #161616;
--layer-01: #262626;
--layer-02: #393939;
--border: #393939;
--border-strong: #525252;
--text: #f4f4f4;
--text-secondary: #a8a8a8;
--text-placeholder: #6f6f6f;
--support-error: #fa4d56;
--support-success: #42be65;
--support-warning: #f1c21b;
--support-info: #78a9ff;
--on-status: #161616;
}
/*
* Accent — the FREE, swappable layer (doc §3.3). Indigo is the default and is
* defined on :root / [data-theme='dark'] so `--accent` always resolves even with
* no `[data-accent]` attribute. The alternates below are opt-in; an accent
* switcher arrives with Settings in M5. Brighter values on the dark canvas keep
* accent-on-bg and text-on-accent contrast within AA.
*/
:root {
--accent: #5b54e6;
--accent-hover: #4a43d6;
--accent-contrast: #ffffff;
}
[data-theme='dark'] {
--accent: #8b85f0;
--accent-hover: #a29bf5;
--accent-contrast: #161616;
}
[data-accent='teal'] {
--accent: #0f766e;
--accent-hover: #0c5f59;
--accent-contrast: #ffffff;
}
[data-accent='amber'] {
--accent: #b45309;
--accent-hover: #92400e;
--accent-contrast: #ffffff;
}
[data-accent='rose'] {
--accent: #be123c;
--accent-hover: #9f1239;
--accent-contrast: #ffffff;
}
[data-theme='dark'][data-accent='teal'] {
--accent: #2dd4bf;
--accent-hover: #5eead4;
--accent-contrast: #161616;
}
[data-theme='dark'][data-accent='amber'] {
--accent: #fbbf24;
--accent-hover: #fcd34d;
--accent-contrast: #161616;
}
[data-theme='dark'][data-accent='rose'] {
--accent: #fb7185;
--accent-hover: #fda4af;
--accent-contrast: #161616;
}