/* ============================================================================ Frosty design tokens (canonical) - revision ds-r2 ---------------------------------------------------------------------------- Product: Frosty Control Plane, the control plane for a Deno-native LLM gateway. Dark is the default theme: applications mount with class="dark" on the root element. Light theme is the :root base per shadcn/ui v4 convention. "Auto" behavior: toggle the .dark class from prefers-color-scheme in app JS. ds-r2 SUPERSEDES ds-r1 (glacier-blue). The register is a dense, near-neutral monochrome developer console (shadcn "new-york"/neutral). Neutrals carry a whisper of cool (hue 265, chroma <= 0.006), never pure gray. Primary is the shadcn-neutral inversion: near-white in dark (ink label), near-black in light (paper label). A single restrained cool-blue accent (hue ~252 to 255) is used ONLY for the focus ring, links, and the active-nav indicator, never as a fill. Semantic status colors (success/warning/destructive/info) stay a functional vocabulary. A --chart-1..5 family is added for the analytics dashboard. Format: shadcn/ui v4 compatible CSS custom properties in OKLCH, plus an @theme inline mapping block for Tailwind CSS v4. Hex fallbacks are noted in comments; every text/surface pair is contrast-verified in both themes (see docs/design/DESIGN.md, Accessibility section: 0 failures, computed WCAG 2.x). Source of truth: docs/design/DESIGN.md. Do not hand-edit component CSS to diverge from these values; change the token here and let it propagate. Token NAMES are stable across ds-r1 and ds-r2 so the app re-themes by swapping values, not names. ========================================================================== */ /* ------------------------------------------------------------------------ */ /* Light theme (base) + theme-independent tokens */ /* ------------------------------------------------------------------------ */ :root { color-scheme: light; /* --- color: surfaces ------------------------------------------------- */ --background: oklch(0.99 0.002 265); /* #fbfcfd canvas */ --foreground: oklch(0.2 0.006 265); /* #151619 body text */ --card: oklch(1 0 0); /* #ffffff raised surface */ --card-foreground: oklch(0.2 0.006 265); /* #151619 */ --popover: oklch(1 0 0); /* #ffffff dialogs, menus */ --popover-foreground: oklch(0.2 0.006 265); /* #151619 */ /* --- color: primary (shadcn-neutral inversion: near-black fill) ------- */ --primary: oklch(0.24 0.006 265); /* #1e1f22 ink */ --primary-foreground: oklch(0.985 0.001 265); /* #fafafb paper label */ /* --- color: supporting surfaces --------------------------------------- */ --secondary: oklch(0.965 0.003 265); /* #f2f3f5 */ --secondary-foreground: oklch(0.24 0.006 265); /* #1e1f22 */ --muted: oklch(0.965 0.003 265); /* #f2f3f5 */ --muted-foreground: oklch(0.475 0.008 265); /* #5a5c61 */ --accent: oklch(0.965 0.004 265); /* #f2f3f6 hover wash */ --accent-foreground: oklch(0.24 0.006 265); /* #1e1f22 */ /* --- color: semantic status ------------------------------------------- */ --destructive: oklch(0.52 0.2 25); /* #c21725 */ --destructive-foreground: oklch(0.985 0.005 25); /* #fdf9f8 */ --success: oklch(0.48 0.13 155); /* #00723b */ --success-foreground: oklch(0.985 0.005 155); /* #f8fbf9 */ --warning: oklch(0.52 0.11 70); /* #8f5d14 */ --warning-foreground: oklch(0.985 0.005 80); /* #fcfaf6 */ --info: oklch(0.5 0.15 255); /* #1762b6 */ --info-foreground: oklch(0.985 0.005 250); /* #f8fafd */ /* --- color: lines and focus ------------------------------------------- */ --border: oklch(0.92 0.004 265); /* #e3e4e7 hairline */ --input: oklch(0.89 0.004 265); /* #d9dbdd field border */ --ring: oklch(0.55 0.15 255); /* #2971c6 accent blue */ /* --- color: chart family (data marks; AA >= 3:1 non-text on card) ------ */ --chart-1: oklch(0.52 0.15 255); /* #1f68bc blue */ --chart-2: oklch(0.52 0.14 155); /* #007f43 green */ --chart-3: oklch(0.6 0.12 70); /* #ad721c amber */ --chart-4: oklch(0.52 0.2 25); /* #c21725 red */ --chart-5: oklch(0.5 0.18 300); /* #7541b8 violet */ /* --- color: sidebar family -------------------------------------------- */ --sidebar: oklch(0.975 0.003 265); /* #f6f7f9 */ --sidebar-foreground: oklch(0.24 0.006 265); /* #1e1f22 */ --sidebar-primary: oklch(0.52 0.15 255); /* #1f68bc accent blue */ --sidebar-primary-foreground: oklch(0.985 0.001 265); /* #fafafb */ --sidebar-accent: oklch(0.955 0.005 265); /* #eef0f4 active item */ --sidebar-accent-foreground: oklch(0.24 0.006 265); /* #1e1f22 */ --sidebar-border: oklch(0.91 0.004 265); /* #e0e1e4 */ --sidebar-ring: oklch(0.55 0.15 255); /* #2971c6 */ /* --- shadow ramp (near-neutral, never glacier) ------------------------- */ --shadow-sm: 0 1px 2px 0 oklch(0.2 0.01 265 / 0.06); --shadow-md: 0 2px 8px -1px oklch(0.2 0.01 265 / 0.1), 0 1px 2px 0 oklch(0.2 0.01 265 / 0.06); --shadow-lg: 0 8px 24px -4px oklch(0.2 0.01 265 / 0.16), 0 2px 6px 0 oklch(0.2 0.01 265 / 0.08); /* --- typography -------------------------------------------------------- */ /* JetBrains Mono (OFL-1.1, free) is an optional progressive enhancement: it renders only where locally installed. No font files are shipped. */ --font-family-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; --font-family-mono: "JetBrains Mono", ui-monospace, "Cascadia Code", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; /* compact scale, px-snapped; 13.5px body for high dashboard density */ --font-size-2xs: 0.6875rem; --line-height-2xs: 1rem; /* 11/16 micro */ --font-size-xs: 0.75rem; --line-height-xs: 1rem; /* 12/16 caption */ --font-size-sm: 0.8125rem; --line-height-sm: 1.125rem; /* 13/18 secondary */ --font-size-base: 0.84375rem; --line-height-base: 1.25rem; /* 13.5/20 body */ --font-size-lg: 1rem; --line-height-lg: 1.375rem; /* 16/22 emphasis */ --font-size-xl: 1.125rem; --line-height-xl: 1.5rem; /* 18/24 section */ --font-size-2xl: 1.3125rem; --line-height-2xl: 1.625rem; /* 21/26 page title */ --font-size-3xl: 1.6875rem; --line-height-3xl: 2rem; /* 27/32 display, stat */ --font-weight-regular: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --tracking-tight: -0.01em; /* titles 2xl and up */ --tracking-wide: 0.02em; /* tiny mono labels */ /* --- spacing (base unit 4px) ------------------------------------------- */ --space-1: 0.25rem; /* 4px */ --space-2: 0.5rem; /* 8px */ --space-3: 0.75rem; /* 12px */ --space-4: 1rem; /* 16px */ --space-5: 1.25rem; /* 20px */ --space-6: 1.5rem; /* 24px */ --space-8: 2rem; /* 32px */ --space-10: 2.5rem; /* 40px */ --space-12: 3rem; /* 48px */ --space-16: 4rem; /* 64px */ /* --- radius (one family, derived from a single 6px base) --------------- */ --radius: 0.375rem; /* 6px base */ --radius-sm: calc(var(--radius) - 2px); /* 4px badges, small controls */ --radius-md: var(--radius); /* 6px buttons, inputs */ --radius-lg: calc(var(--radius) + 2px); /* 8px cards, panels */ --radius-xl: calc(var(--radius) + 6px); /* 12px dialogs, sheets */ --radius-full: 9999px; /* pills, switch */ /* --- layout and control metrics (denser than ds-r1) -------------------- */ --border-w: 1px; --ring-w: 2px; --ring-offset: 2px; --control-h-sm: 1.875rem; /* 30px dense-row controls */ --control-h: 2.125rem; /* 34px default control height */ --control-h-lg: 2.625rem; /* 42px prominent controls */ --tap-target: 2.75rem; /* 44px minimum hit area (extended if visual < 44) */ --sidebar-width: 15rem; --sidebar-width-icon: 3rem; /* Outer content cap. Was 78rem (1248px), which left ~1250px of a 2560px monitor unused while tables scrolled horizontally inside it. 110rem (1760px) is wide enough for dense telemetry tables and the two-pane layouts without letting a page become a single ungrouped expanse. */ --container-max: 110rem; /* Inner cap for prose and single-column forms. A control row stretched to 1760px puts its label a screen away from its input; text past ~75ch stops being scannable. Views opt into this for text-heavy panels while their tables use the full --container-max. */ --measure-max: 60rem; /* Page gutter, tightening on small screens so a tablet does not spend a quarter of its width on padding. */ --gutter: 1.5rem; --opacity-disabled: 0.5; /* --- motion ------------------------------------------------------------- */ --motion-fast: 100ms; /* hover, focus, pressed feedback */ --motion-default: 150ms; /* menus, popovers, toggles, tabs */ --motion-slow: 220ms; /* dialogs, sheets, page-level */ --motion-spin: 800ms; /* continuous loading spinner */ --motion-ease-out: cubic-bezier(0.2, 0, 0, 1); --motion-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); --motion-rise: -2px; /* hover lift distance */ --motion-enter: 8px; /* enter-toward travel distance */ /* --- z-index ------------------------------------------------------------ */ --z-sticky: 20; --z-overlay: 40; --z-modal: 50; --z-toast: 60; } /* ------------------------------------------------------------------------ */ /* Dark theme (Frosty default: mount with class="dark") */ /* ------------------------------------------------------------------------ */ .dark { color-scheme: dark; --background: oklch(0.15 0.004 265); /* #0a0b0d canvas */ --foreground: oklch(0.985 0.001 265); /* #fafafb body text */ --card: oklch(0.185 0.004 265); /* #121314 raised surface */ --card-foreground: oklch(0.985 0.001 265); /* #fafafb */ --popover: oklch(0.205 0.004 265); /* #161719 dialogs, menus */ --popover-foreground: oklch(0.985 0.001 265); /* #fafafb */ --primary: oklch(0.92 0.004 265); /* #e3e4e7 near-white */ --primary-foreground: oklch(0.205 0.006 265); /* #16171a ink label */ --secondary: oklch(0.255 0.004 265); /* #222325 */ --secondary-foreground: oklch(0.985 0.001 265); /* #fafafb */ --muted: oklch(0.235 0.004 265); /* #1d1e20 */ --muted-foreground: oklch(0.712 0.008 265); /* #a0a2a7 */ --accent: oklch(0.255 0.006 265); /* #212326 hover wash */ --accent-foreground: oklch(0.985 0.001 265); /* #fafafb */ --destructive: oklch(0.665 0.19 25); /* #f25855 */ --destructive-foreground: oklch(0.205 0.04 25); /* #280e0c ink label */ --success: oklch(0.72 0.15 155); /* #43c07a */ --success-foreground: oklch(0.18 0.04 155); /* #021709 */ --warning: oklch(0.8 0.13 82); /* #e7b551 */ --warning-foreground: oklch(0.24 0.04 82); /* #291d07 */ --info: oklch(0.68 0.13 250); /* #549de5 */ --info-foreground: oklch(0.17 0.04 250); /* #021020 */ --border: oklch(0.27 0.006 265); /* #252629 */ --input: oklch(0.3 0.006 265); /* #2c2e31 */ --ring: oklch(0.62 0.13 252); /* #4589d2 accent blue */ --chart-1: oklch(0.66 0.14 252); /* #4b95e5 blue */ --chart-2: oklch(0.72 0.15 155); /* #43c07a green */ --chart-3: oklch(0.8 0.13 82); /* #e7b551 amber */ --chart-4: oklch(0.665 0.19 25); /* #f25855 red */ --chart-5: oklch(0.62 0.16 300); /* #966cd7 violet */ --sidebar: oklch(0.13 0.004 265); /* #070709 */ --sidebar-foreground: oklch(0.8 0.006 265); /* #bcbec2 */ --sidebar-primary: oklch(0.62 0.13 252); /* #4589d2 accent blue */ --sidebar-primary-foreground: oklch(0.985 0.001 265); /* #fafafb */ --sidebar-accent: oklch(0.235 0.006 265); /* #1d1e21 active item */ --sidebar-accent-foreground: oklch(0.985 0.001 265); /* #fafafb */ --sidebar-border: oklch(0.24 0.006 265); /* #1e1f22 */ --sidebar-ring: oklch(0.62 0.13 252); /* #4589d2 */ --shadow-sm: 0 1px 2px 0 oklch(0.03 0.006 265 / 0.5); --shadow-md: 0 2px 8px -1px oklch(0.03 0.006 265 / 0.6), 0 1px 2px 0 oklch(0.03 0.006 265 / 0.5); --shadow-lg: 0 10px 30px -5px oklch(0.03 0.006 265 / 0.7), 0 4px 8px -2px oklch(0.03 0.006 265 / 0.5); } /* ------------------------------------------------------------------------ */ /* Reduced motion: durations collapse, travel distances zero out */ /* ------------------------------------------------------------------------ */ @media (prefers-reduced-motion: reduce) { :root { --motion-fast: 0ms; --motion-default: 0ms; --motion-slow: 0ms; --motion-rise: 0px; --motion-enter: 0px; } } /* ------------------------------------------------------------------------ */ /* Tailwind CSS v4 mapping (shadcn/ui v4 convention) */ /* Import this file in the app stylesheet after `@import "tailwindcss";`. */ /* ------------------------------------------------------------------------ */ @theme inline { /* colors */ --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: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-destructive-foreground: var(--destructive-foreground); --color-success: var(--success); --color-success-foreground: var(--success-foreground); --color-warning: var(--warning); --color-warning-foreground: var(--warning-foreground); --color-info: var(--info); --color-info-foreground: var(--info-foreground); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); --color-sidebar: var(--sidebar); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-primary: var(--sidebar-primary); --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); --color-sidebar-accent: var(--sidebar-accent); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); /* typography */ --font-sans: var(--font-family-sans); --font-mono: var(--font-family-mono); --text-2xs: var(--font-size-2xs); --text-2xs--line-height: var(--line-height-2xs); --text-xs: var(--font-size-xs); --text-xs--line-height: var(--line-height-xs); --text-sm: var(--font-size-sm); --text-sm--line-height: var(--line-height-sm); --text-base: var(--font-size-base); --text-base--line-height: var(--line-height-base); --text-lg: var(--font-size-lg); --text-lg--line-height: var(--line-height-lg); --text-xl: var(--font-size-xl); --text-xl--line-height: var(--line-height-xl); --text-2xl: var(--font-size-2xl); --text-2xl--line-height: var(--line-height-2xl); --text-3xl: var(--font-size-3xl); --text-3xl--line-height: var(--line-height-3xl); /* radius */ --radius-sm: calc(var(--radius) - 2px); --radius-md: var(--radius); --radius-lg: calc(var(--radius) + 2px); --radius-xl: calc(var(--radius) + 6px); /* shadows */ --shadow-sm: var(--shadow-sm); --shadow-md: var(--shadow-md); --shadow-lg: var(--shadow-lg); /* easing */ --ease-out: var(--motion-ease-out); --ease-in-out: var(--motion-ease-in-out); }