Files
lib-theme-phosphor/theme.css
jules caee7f6672 lib-theme-phosphor: initial import (calm-pro/dense-utility set, 2026-07)
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>
2026-07-07 22:56:29 +10:00

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;
}