One of four divergent candidates for the operator/console theme family (successor to Skyrise for developer-product screens). Dense/pro type register, keyboard-first focus treatment, tabular numerals, coarse-pointer 44px touch floor, neutral code chips, dark card separation per house sweep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
441 lines
16 KiB
CSS
441 lines
16 KiB
CSS
/*
|
|
* Phosphor — terminal heritage, dark-first.
|
|
*
|
|
* The dark mode IS the theme: a deep charcoal with a barely-there green
|
|
* undertone (hue ~150, chroma 0.006 — never blue-gray, never pure black),
|
|
* the way a switched-off CRT still remembers its phosphor coat. Cards step
|
|
* LIGHTER off the background by a full 0.06 L, and hairline light-alpha
|
|
* borders do all the separation work — the elevation ladder exists but is
|
|
* whisper-quiet, because on a terminal, borders beat shadows. The single
|
|
* accent is phosphor green (~oklch(0.75 0.16 148)) carrying DARK ink on the
|
|
* bright fill (the ivrea dark-ink-on-bright pattern), spent on the primary
|
|
* action, focus, and selection only.
|
|
*
|
|
* Light mode is the "paper terminal": warm-white paper (hue ~120, faint) with
|
|
* deep green-charcoal ink, and the same green deepened to ~L0.45 so white
|
|
* labels clear the floor. Success is pulled to TEAL (hue 178, 30° from the
|
|
* primary's 148) so state-green never collides with action-green.
|
|
*
|
|
* Type is the signature: IBM Plex Mono for HEADINGS (500/600, slightly tight),
|
|
* IBM Plex Sans for body, Plex Mono for code. Dense/pro register — body
|
|
* 0.9375rem, modest display sizes (mono runs wide). Radius is a tight
|
|
* 0.375rem; motion is instant-feeling (100/180/280/400ms), springs aliased.
|
|
* Syntax colors are a muted-precise terminal scheme — no neon soup.
|
|
*
|
|
* Token contract matches lib-theme-caffe-florian/theme.css (Crema v1).
|
|
*/
|
|
|
|
/*
|
|
* Fonts: consuming apps load IBM Plex Sans + IBM Plex Mono at the top of
|
|
* their entry CSS (where @import statements are allowed), via the Google
|
|
* Fonts URL documented in this theme's README. Embedding the @import here —
|
|
* or smuggling the URL in a comment for a build script to lift — breaks
|
|
* PostCSS's ordering rules, so neither is done in this file.
|
|
*/
|
|
|
|
:root {
|
|
--font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
--font-heading: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
--font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
|
|
/* Tailwind utility families. Distinct names so a consuming app's
|
|
* `@theme inline` can indirect to these (--font-heading/--font-sans collide
|
|
* with Tailwind's own font-* namespace). Mirror --font-heading/--font-sans. */
|
|
--heading-family: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
--body-family: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
|
|
/* ── Colors ── paper terminal: warm-white paper, deep green-charcoal ink,
|
|
* one deepened phosphor-green accent carrying white labels. */
|
|
--background: oklch(0.975 0.004 120);
|
|
--foreground: oklch(0.22 0.015 150);
|
|
|
|
--card: oklch(0.99 0.002 120);
|
|
--card-foreground: oklch(0.22 0.015 150);
|
|
|
|
--popover: oklch(0.995 0.002 120);
|
|
--popover-foreground: oklch(0.22 0.015 150);
|
|
|
|
/* Primary — phosphor green deepened for paper (L0.45) so near-white
|
|
* labels sit well above the floor. The bright terminal green returns
|
|
* in dark mode, where it belongs. */
|
|
--primary: oklch(0.45 0.12 150);
|
|
--primary-foreground: oklch(0.98 0.008 140);
|
|
|
|
--secondary: oklch(0.93 0.008 140);
|
|
--secondary-foreground: oklch(0.3 0.015 150);
|
|
|
|
--muted: oklch(0.935 0.007 135);
|
|
--muted-foreground: oklch(0.44 0.018 150);
|
|
|
|
/* Accent — hover/selection wash: a faint green tint with deep green ink,
|
|
* not a second competing fill. */
|
|
--accent: oklch(0.93 0.028 148);
|
|
--accent-foreground: oklch(0.33 0.09 150);
|
|
|
|
/* ── Status ── success is TEAL (hue 178, 30° off the green primary) so
|
|
* "ok" never impersonates "act". Destructive/info carry light labels on
|
|
* deep fills; warning carries dark amber ink on a mid amber. */
|
|
--destructive: oklch(0.55 0.21 27);
|
|
--destructive-foreground: oklch(0.98 0.01 27);
|
|
--success: oklch(0.5 0.11 178);
|
|
--success-foreground: oklch(0.98 0.01 178);
|
|
--warning: oklch(0.66 0.15 80);
|
|
--warning-foreground: oklch(0.21 0.05 80);
|
|
--info: oklch(0.5 0.13 235);
|
|
--info-foreground: oklch(0.98 0.01 235);
|
|
|
|
/* Borders do the separation work — a touch stronger than house norm. */
|
|
--border: oklch(0.22 0.015 150 / 0.14);
|
|
--input: oklch(0.22 0.015 150 / 0.16);
|
|
--ring: oklch(0.45 0.12 150 / 0.6);
|
|
|
|
/* Chart palette — phosphor green lead, then blue / amber / violet / red.
|
|
* Hues spaced, adjacent-index lightness varied. */
|
|
--chart-1: oklch(0.52 0.13 150);
|
|
--chart-2: oklch(0.58 0.13 235);
|
|
--chart-3: oklch(0.68 0.14 80);
|
|
--chart-4: oklch(0.55 0.15 310);
|
|
--chart-5: oklch(0.6 0.17 25);
|
|
|
|
--sidebar: oklch(0.955 0.006 130);
|
|
--sidebar-foreground: oklch(0.22 0.015 150);
|
|
--sidebar-primary: oklch(0.45 0.12 150);
|
|
--sidebar-primary-foreground: oklch(0.98 0.008 140);
|
|
--sidebar-accent: oklch(0.92 0.028 148);
|
|
--sidebar-accent-foreground: oklch(0.33 0.09 150);
|
|
--sidebar-border: oklch(0.22 0.015 150 / 0.1);
|
|
--sidebar-ring: oklch(0.45 0.12 150 / 0.6);
|
|
|
|
/* Syntax — paper-terminal ink: precise, low-chroma, green-led. */
|
|
--syntax-keyword: oklch(0.45 0.11 150);
|
|
--syntax-string: oklch(0.5 0.09 80);
|
|
--syntax-number: oklch(0.48 0.09 178);
|
|
--syntax-comment: oklch(0.55 0.015 150);
|
|
--syntax-function: oklch(0.48 0.11 235);
|
|
--syntax-type: oklch(0.5 0.09 310);
|
|
|
|
/* ── Chat surfaces ──
|
|
* Contract for lib-chat-ui / lib-agent-ui / lib-agent-dock-ui. User bubble
|
|
* is the deep green-charcoal ink as a panel with paper text; assistant is
|
|
* a clean card behind a hairline border. */
|
|
--chat-user-bg: oklch(0.27 0.018 150);
|
|
--chat-user-fg: oklch(0.97 0.005 120);
|
|
--chat-assistant-bg: oklch(0.99 0.002 120);
|
|
--chat-assistant-fg: oklch(0.22 0.015 150);
|
|
--chat-assistant-border: oklch(0.22 0.015 150 / 0.14);
|
|
|
|
/* Tight console radius. */
|
|
--radius: 0.375rem;
|
|
|
|
/* ── Motion ── instant-feeling. Springs alias to the standard eases —
|
|
* a console does not bounce; personality lives in the speed. */
|
|
--duration-fast: 100ms;
|
|
--duration-base: 180ms;
|
|
--duration-slow: 280ms;
|
|
--duration-slower: 400ms;
|
|
--duration-spring: 180ms;
|
|
|
|
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
|
--ease-decelerate: cubic-bezier(0, 0, 0, 1);
|
|
--ease-accelerate: cubic-bezier(0.3, 0, 1, 1);
|
|
--ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
|
|
|
|
--ease-spring-gentle: var(--ease-standard);
|
|
--ease-spring-snappy: var(--ease-decelerate);
|
|
--ease-spring-bouncy: var(--ease-standard);
|
|
|
|
/* ── Typography ── dense/pro register, fixed rem. Display sizes run
|
|
* modest because mono headings run wide. */
|
|
--text-hero-size: 2.75rem;
|
|
--text-hero-lh: 1.1;
|
|
--text-display-size: 2rem;
|
|
--text-display-lh: 1.15;
|
|
--text-headline-size: 1.5rem;
|
|
--text-headline-lh: 1.25;
|
|
--text-title-size: 1.1875rem;
|
|
--text-title-lh: 1.35;
|
|
--text-subtitle-size: 1.0625rem;
|
|
--text-subtitle-lh: 1.45;
|
|
--text-body-size: 0.9375rem;
|
|
--text-body-lh: 1.5;
|
|
--text-label-size: 0.8125rem;
|
|
--text-label-lh: 1.25rem;
|
|
--text-caption-size: 0.75rem;
|
|
--text-caption-lh: 1.0625rem;
|
|
|
|
/* ── Elevation ── whisper-quiet: borders separate, shadows only hint.
|
|
* The ladder is present but nearly flat. */
|
|
--elevation-0: none;
|
|
--elevation-1: 0 1px 1px oklch(0.22 0.015 150 / 0.04);
|
|
--elevation-2: 0 1px 2px oklch(0.22 0.015 150 / 0.05);
|
|
--elevation-3: 0 2px 4px -1px oklch(0.22 0.015 150 / 0.06);
|
|
--elevation-4: 0 4px 10px -3px oklch(0.22 0.015 150 / 0.08);
|
|
--elevation-5: 0 8px 20px -6px oklch(0.22 0.015 150 / 0.1);
|
|
|
|
--spacing: 0.25rem;
|
|
|
|
--border-width-thin: 0.5px;
|
|
--border-width-base: 1px;
|
|
--border-width-thick: 2px;
|
|
--border-width-heavy: 4px;
|
|
|
|
/* No glass — inert values so consuming apps referencing these get no-ops. */
|
|
--glass-blur: 0px;
|
|
--glass-saturate: 100%;
|
|
--glass-brightness: 1;
|
|
--glass-contrast: 1;
|
|
}
|
|
|
|
/*
|
|
* Dark mode — the native identity. Deep charcoal with the faint phosphor
|
|
* undertone (hue 150, chroma 0.006 — not blue-gray, not black). Cards step
|
|
* a full 0.06 L LIGHTER; popover sits one step above card, below muted.
|
|
* Hairline light-alpha borders carry the structure; the bright phosphor
|
|
* green takes dark ink on top.
|
|
*/
|
|
.dark {
|
|
--background: oklch(0.17 0.006 150);
|
|
--foreground: oklch(0.93 0.008 150);
|
|
|
|
--card: oklch(0.23 0.007 150);
|
|
--card-foreground: oklch(0.93 0.008 150);
|
|
|
|
--popover: oklch(0.25 0.007 150);
|
|
--popover-foreground: oklch(0.93 0.008 150);
|
|
|
|
--primary: oklch(0.75 0.16 148);
|
|
--primary-foreground: oklch(0.18 0.045 148);
|
|
|
|
--secondary: oklch(0.27 0.008 150);
|
|
--secondary-foreground: oklch(0.9 0.008 150);
|
|
|
|
--muted: oklch(0.27 0.008 150);
|
|
--muted-foreground: oklch(0.71 0.012 150);
|
|
|
|
--accent: oklch(0.29 0.04 148);
|
|
--accent-foreground: oklch(0.86 0.12 148);
|
|
|
|
--destructive: oklch(0.68 0.19 25);
|
|
--destructive-foreground: oklch(0.16 0.045 25);
|
|
--success: oklch(0.78 0.13 178);
|
|
--success-foreground: oklch(0.18 0.04 178);
|
|
--warning: oklch(0.8 0.15 80);
|
|
--warning-foreground: oklch(0.2 0.05 80);
|
|
--info: oklch(0.72 0.12 235);
|
|
--info-foreground: oklch(0.16 0.04 235);
|
|
|
|
/* Light-alpha hairlines — a touch stronger than house norm; borders are
|
|
* the separation mechanism here, not shadows. */
|
|
--border: oklch(0.95 0.01 150 / 0.14);
|
|
--input: oklch(0.95 0.01 150 / 0.17);
|
|
--ring: oklch(0.75 0.16 148 / 0.6);
|
|
|
|
--chart-1: oklch(0.75 0.16 148);
|
|
--chart-2: oklch(0.66 0.13 235);
|
|
--chart-3: oklch(0.79 0.14 80);
|
|
--chart-4: oklch(0.64 0.14 310);
|
|
--chart-5: oklch(0.68 0.17 25);
|
|
|
|
/* Sidebar drops BELOW the background — the darkest rail on screen, a
|
|
* terminal gutter. */
|
|
--sidebar: oklch(0.15 0.006 150);
|
|
--sidebar-foreground: oklch(0.93 0.008 150);
|
|
--sidebar-primary: oklch(0.75 0.16 148);
|
|
--sidebar-primary-foreground: oklch(0.18 0.045 148);
|
|
--sidebar-accent: oklch(0.27 0.04 148);
|
|
--sidebar-accent-foreground: oklch(0.86 0.12 148);
|
|
--sidebar-border: oklch(0.95 0.01 150 / 0.1);
|
|
--sidebar-ring: oklch(0.75 0.16 148 / 0.6);
|
|
|
|
/* Syntax — a great terminal colorscheme: green keywords, warm strings,
|
|
* teal numbers, dim comments. Chroma capped ~0.12; muted-precise. */
|
|
--syntax-keyword: oklch(0.78 0.11 148);
|
|
--syntax-string: oklch(0.79 0.09 80);
|
|
--syntax-number: oklch(0.76 0.09 178);
|
|
--syntax-comment: oklch(0.58 0.015 150);
|
|
--syntax-function: oklch(0.77 0.08 235);
|
|
--syntax-type: oklch(0.74 0.08 310);
|
|
|
|
/* ── Chat surfaces (dark) ── user panel steps above muted; assistant sits
|
|
* on the card step behind a hairline. */
|
|
--chat-user-bg: oklch(0.31 0.015 150);
|
|
--chat-user-fg: oklch(0.96 0.006 150);
|
|
--chat-assistant-bg: oklch(0.23 0.007 150);
|
|
--chat-assistant-fg: oklch(0.93 0.008 150);
|
|
--chat-assistant-border: oklch(0.95 0.01 150 / 0.14);
|
|
|
|
--elevation-1: 0 1px 1px oklch(0 0 0 / 0.16);
|
|
--elevation-2: 0 1px 2px oklch(0 0 0 / 0.2);
|
|
--elevation-3: 0 2px 4px -1px oklch(0 0 0 / 0.24);
|
|
--elevation-4: 0 4px 10px -3px oklch(0 0 0 / 0.28);
|
|
--elevation-5: 0 8px 20px -6px oklch(0 0 0 / 0.34);
|
|
}
|
|
|
|
/*
|
|
* @layer base — Phosphor's dense, keyboard-first console philosophy.
|
|
*
|
|
* Mono headings, hairline-separated calm surfaces, tabular numerals in data
|
|
* contexts, and the phosphor green reserved for focus, selection, and the
|
|
* primary action. Everything else stays quiet.
|
|
*/
|
|
@layer base {
|
|
body {
|
|
min-height: 100vh;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
letter-spacing: -0.004em;
|
|
font-family: var(--font-sans);
|
|
font-size: var(--text-body-size);
|
|
line-height: var(--text-body-lh);
|
|
}
|
|
|
|
/* Headings — IBM Plex Mono, the signature. 600, slightly tight; mono
|
|
* already runs wide, so no extra tracking. */
|
|
h1, h2, h3, h4,
|
|
[data-slot="card-title"],
|
|
[data-slot="dialog-title"],
|
|
[data-slot="sheet-title"],
|
|
[data-slot="alert-dialog-title"] {
|
|
font-family: var(--font-heading);
|
|
font-weight: 600;
|
|
letter-spacing: -0.015em;
|
|
color: var(--foreground);
|
|
}
|
|
|
|
h4,
|
|
[data-slot="card-title"] {
|
|
font-weight: 500;
|
|
}
|
|
|
|
code, kbd, samp, pre,
|
|
[data-slot="code"],
|
|
[data-slot="code-block"] {
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
/* Inline code — neutral chip: faint foreground tint, foreground ink.
|
|
* Never accent-colored. */
|
|
:not(pre) > code {
|
|
padding: 0.1em 0.35em;
|
|
border-radius: calc(var(--radius) * 0.5);
|
|
background: color-mix(in oklch, var(--foreground) 6%, transparent);
|
|
color: var(--foreground);
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
/* Dense utility — data reads in columns. Tabular numerals for tables and
|
|
* an opt-in .num class for stat tiles, IDs, and metric rows. */
|
|
[data-slot="table"] {
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.num {
|
|
font-family: var(--font-mono);
|
|
font-variant-numeric: tabular-nums;
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
/* Selection — the phosphor spark. */
|
|
::selection {
|
|
background: color-mix(in oklch, var(--primary) 30%, transparent);
|
|
color: var(--foreground);
|
|
}
|
|
|
|
/* Links — green-leaning ink, underline-on-hover. */
|
|
a {
|
|
color: color-mix(in oklch, var(--primary) 70%, var(--foreground));
|
|
text-decoration-color: color-mix(in oklch, var(--primary) 40%, transparent);
|
|
text-underline-offset: 0.2em;
|
|
transition: text-decoration-color var(--duration-fast) var(--ease-standard);
|
|
}
|
|
a:hover {
|
|
text-decoration-color: var(--primary);
|
|
}
|
|
|
|
[data-slot="sidebar"] {
|
|
background: var(--sidebar);
|
|
border-right: var(--border-width-base) solid var(--sidebar-border);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Focus-visible — keyboard-first is a signature. Buttons get a crisp 2px
|
|
* phosphor outline, offset; inputs get a flush 1px ring plus a border-color
|
|
* change (see the input block below). Placed outside @layer base so it beats
|
|
* Tailwind's default ring utilities.
|
|
*/
|
|
[data-slot="button"]:focus-visible {
|
|
outline: 2px solid var(--primary);
|
|
outline-offset: 2px;
|
|
box-shadow: none;
|
|
}
|
|
[data-slot="input"]:focus-visible,
|
|
[data-slot="textarea"]:focus-visible,
|
|
[data-slot="select-trigger"]:focus-visible {
|
|
outline: none;
|
|
border-color: var(--primary);
|
|
box-shadow: 0 0 0 1px var(--primary);
|
|
}
|
|
|
|
/* Buttons — quiet weight; opacity absorb on press, instant. */
|
|
[data-slot="button"] {
|
|
font-weight: 500;
|
|
letter-spacing: -0.004em;
|
|
}
|
|
|
|
/* Card titles — pinned to the subtitle role so the scale is the theme's
|
|
* decision, not the card primitive's default. */
|
|
[data-slot="card-title"] {
|
|
font-size: var(--text-subtitle-size);
|
|
line-height: var(--text-subtitle-lh);
|
|
}
|
|
|
|
/* Touch targets — desktop keeps the dense console ladder; coarse pointers
|
|
* get the 44px floor on every control. */
|
|
@media (pointer: coarse) {
|
|
[data-slot="button"] {
|
|
min-height: 2.75rem;
|
|
min-width: 2.75rem;
|
|
}
|
|
[data-slot="input"],
|
|
[data-slot="select-trigger"] {
|
|
min-height: 2.75rem;
|
|
}
|
|
}
|
|
[data-slot="button"]:hover:not(:disabled) {
|
|
opacity: 0.9;
|
|
}
|
|
[data-slot="button"]:active:not(:disabled) {
|
|
opacity: 0.82;
|
|
transform: scale(0.99);
|
|
}
|
|
|
|
/* Inputs — card surface, hairline border, phosphor focus as a flush ring. */
|
|
[data-slot="input"],
|
|
[data-slot="textarea"],
|
|
[data-slot="select-trigger"] {
|
|
background: var(--card);
|
|
border: var(--border-width-base) solid var(--input);
|
|
border-radius: var(--radius);
|
|
color: var(--foreground);
|
|
transition:
|
|
border-color var(--duration-fast) var(--ease-standard),
|
|
box-shadow var(--duration-fast) var(--ease-standard);
|
|
}
|
|
[data-slot="input"]::placeholder,
|
|
[data-slot="textarea"]::placeholder {
|
|
color: var(--muted-foreground);
|
|
opacity: 0.8;
|
|
}
|
|
[data-slot="input"]:hover:not(:disabled),
|
|
[data-slot="textarea"]:hover:not(:disabled),
|
|
[data-slot="select-trigger"]:hover:not(:disabled) {
|
|
border-color: color-mix(in oklch, var(--foreground) 26%, transparent);
|
|
}
|
|
[data-slot="input"]:focus,
|
|
[data-slot="textarea"]:focus,
|
|
[data-slot="select-trigger"]:focus {
|
|
border-color: var(--primary);
|
|
box-shadow: 0 0 0 1px var(--primary);
|
|
outline: none;
|
|
}
|