lib-theme-vermilion: initial import (bold/identity-forward set, 2026-07)
One of four divergent candidates for the bold consumer theme family (committed saturated color at 30-60% surface coverage, strong display type). Includes the post-diverge fixes: heading color:inherit so brand slabs keep their foreground pairing, plus per-theme refinements. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
429
theme.css
Normal file
429
theme.css
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Vermilion — poster chrome.
|
||||
*
|
||||
* A bold, identity-forward consumer theme. One hot vermilion red-orange is THE
|
||||
* brand, and it is carried by the CHROME — the primary action, hero panels,
|
||||
* the entire sidebar, selection — in big committed slabs, editorial-poster
|
||||
* style. Roughly 30–40% of a typical screen is drenched vermilion. The content
|
||||
* well stays out of the fight: warm paper with near-black warm ink, so the
|
||||
* poster color reads as identity, not noise. Massive condensed display type
|
||||
* (Anton) is the second half of the voice; the vermilion and the type do the
|
||||
* pointing together.
|
||||
*
|
||||
* Destructive is deliberately NOT a sibling of the brand: pulled to a deeper
|
||||
* crimson (hue ~15, L≈0.48, light label) so a destructive button and a primary
|
||||
* button never read as the same family.
|
||||
*
|
||||
* Dark mode is the poster at night: near-black warm charcoal with the same
|
||||
* vermilion glowing slightly brighter, carrying dark ink on top
|
||||
* (bright-swatch/dark-ink) — the chrome stays vermilion.
|
||||
*
|
||||
* Type: Anton (single weight 400 — condensed poster caps, it IS the voice) for
|
||||
* headings, Archivo for body, JetBrains Mono for code. Consumer register with
|
||||
* a tall display end of the ramp (hero 4rem — Anton is condensed, big sizes
|
||||
* stay narrow). Radius is a modest 0.5rem — poster edges, not toys. Motion is
|
||||
* snappy (120/240/360/500ms); springs alias to the standard eases.
|
||||
*
|
||||
* Token contract matches lib-theme-caffe-florian/theme.css (Crema v1).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fonts: consuming apps load Anton + Archivo + JetBrains 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: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-heading: "Anton", "Archivo", -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
--font-mono: "JetBrains 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: "Anton", "Archivo", -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
--body-family: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
|
||||
/* ── Colors ── warm paper content well, near-black warm ink, and the hot
|
||||
* vermilion drenching the chrome. The paper carries a faint vermilion
|
||||
* tint (chroma 0.012, hue pulled toward the brand) so the brand reaches
|
||||
* the content well without gambling its legibility. */
|
||||
--background: oklch(0.97 0.012 45);
|
||||
--foreground: oklch(0.21 0.012 40);
|
||||
|
||||
--card: oklch(0.995 0.004 60);
|
||||
--card-foreground: oklch(0.21 0.012 40);
|
||||
|
||||
--popover: oklch(0.99 0.005 60);
|
||||
--popover-foreground: oklch(0.21 0.012 40);
|
||||
|
||||
/* Primary — the poster vermilion, deepened from the nominal 0.60 to L0.55
|
||||
* so near-white ink clears the 4.5:1 floor with room to spare. This is the
|
||||
* same slab the sidebar is drenched in — one color, big commitments. */
|
||||
--primary: oklch(0.55 0.21 30);
|
||||
--primary-foreground: oklch(0.985 0.006 40);
|
||||
|
||||
--secondary: oklch(0.93 0.014 48);
|
||||
--secondary-foreground: oklch(0.3 0.014 40);
|
||||
|
||||
--muted: oklch(0.93 0.014 48);
|
||||
--muted-foreground: oklch(0.45 0.014 40);
|
||||
|
||||
/* Accent — a pale vermilion wash with deep vermilion ink: the hover /
|
||||
* selection tint that echoes the brand without adding a second fill. */
|
||||
--accent: oklch(0.94 0.035 40);
|
||||
--accent-foreground: oklch(0.42 0.16 31);
|
||||
|
||||
/* ── Status ── destructive must never look like the brand's sibling:
|
||||
* crimson hue ~15 AND visibly deeper (L0.48) with a light label, vs the
|
||||
* brighter orange-leaning primary (hue 30, L0.55). Success/warning carry
|
||||
* dark ink on bright swatches; warning sits at amber 70 — far from the
|
||||
* vermilion — so alerts never blend into the chrome. */
|
||||
--destructive: oklch(0.48 0.19 15);
|
||||
--destructive-foreground: oklch(0.98 0.01 15);
|
||||
--success: oklch(0.62 0.14 150);
|
||||
--success-foreground: oklch(0.16 0.03 150);
|
||||
--warning: oklch(0.72 0.15 70);
|
||||
--warning-foreground: oklch(0.22 0.05 60);
|
||||
--info: oklch(0.52 0.13 250);
|
||||
--info-foreground: oklch(0.98 0.01 250);
|
||||
|
||||
--border: oklch(0.21 0.012 40 / 0.12);
|
||||
--input: oklch(0.21 0.012 40 / 0.14);
|
||||
--ring: oklch(0.55 0.21 30 / 0.55);
|
||||
|
||||
/* Chart palette — vermilion leads, then a spaced spectrum (blue / teal /
|
||||
* violet / gold), hues ≥60° apart, adjacent lightness varied. */
|
||||
--chart-1: oklch(0.58 0.2 32);
|
||||
--chart-2: oklch(0.5 0.13 250);
|
||||
--chart-3: oklch(0.68 0.11 185);
|
||||
--chart-4: oklch(0.55 0.15 305);
|
||||
--chart-5: oklch(0.72 0.13 95);
|
||||
|
||||
/* ── Sidebar ── DRENCHED. The whole rail is the poster slab: vermilion
|
||||
* field, near-white ink, white-alpha hairlines. The active item is a paper
|
||||
* chip punched out of the slab (deep vermilion ink on near-white). */
|
||||
--sidebar: oklch(0.52 0.2 31);
|
||||
--sidebar-foreground: oklch(0.985 0.006 40);
|
||||
--sidebar-primary: oklch(0.985 0.008 60);
|
||||
--sidebar-primary-foreground: oklch(0.42 0.17 31);
|
||||
--sidebar-accent: oklch(0.46 0.185 31);
|
||||
--sidebar-accent-foreground: oklch(0.985 0.006 40);
|
||||
--sidebar-border: oklch(1 0 0 / 0.22);
|
||||
--sidebar-ring: oklch(0.985 0.008 60 / 0.6);
|
||||
|
||||
/* Syntax — warm-ink base; the vermilion takes the function seat. */
|
||||
--syntax-keyword: oklch(0.48 0.15 305);
|
||||
--syntax-string: oklch(0.5 0.12 150);
|
||||
--syntax-number: oklch(0.52 0.14 55);
|
||||
--syntax-comment: oklch(0.52 0.012 40);
|
||||
--syntax-function: oklch(0.5 0.18 30);
|
||||
--syntax-type: oklch(0.5 0.1 200);
|
||||
|
||||
/* ── Chat surfaces ──
|
||||
* Contract for lib-chat-ui / lib-agent-ui / lib-agent-dock-ui. The user
|
||||
* bubble is a vermilion slab with near-white ink — the poster color owns
|
||||
* the human's voice; the assistant answers from clean paper. */
|
||||
--chat-user-bg: oklch(0.55 0.21 30);
|
||||
--chat-user-fg: oklch(0.985 0.006 40);
|
||||
--chat-assistant-bg: oklch(0.995 0.004 60);
|
||||
--chat-assistant-fg: oklch(0.21 0.012 40);
|
||||
--chat-assistant-border: oklch(0.21 0.012 40 / 0.1);
|
||||
|
||||
/* Poster edges, not toys. */
|
||||
--radius: 0.5rem;
|
||||
|
||||
/* ── Motion ── snappy poster energy. Springs alias to the standard eases —
|
||||
* the personality is speed and commitment, not bounce. */
|
||||
--duration-fast: 120ms;
|
||||
--duration-base: 240ms;
|
||||
--duration-slow: 360ms;
|
||||
--duration-slower: 500ms;
|
||||
--duration-spring: 240ms;
|
||||
|
||||
--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);
|
||||
/* One real spring — a bold consumer theme earns a snappy press. */
|
||||
--ease-spring-snappy: cubic-bezier(0.2, 1.4, 0.3, 1);
|
||||
--ease-spring-bouncy: var(--ease-standard);
|
||||
|
||||
/* ── Typography ── consumer register with a big condensed display end.
|
||||
* Anton is condensed, so the tall sizes stay narrow on screen; line
|
||||
* heights tighten hard at the poster end of the ramp. */
|
||||
--text-hero-size: 4rem;
|
||||
--text-hero-lh: 1.02;
|
||||
--text-display-size: 3rem;
|
||||
--text-display-lh: 1.05;
|
||||
--text-headline-size: 2rem;
|
||||
--text-headline-lh: 1.12;
|
||||
--text-title-size: 1.375rem;
|
||||
--text-title-lh: 1.25;
|
||||
--text-subtitle-size: 1.125rem;
|
||||
--text-subtitle-lh: 1.4;
|
||||
--text-body-size: 1rem;
|
||||
--text-body-lh: 1.55;
|
||||
--text-label-size: 0.875rem;
|
||||
--text-label-lh: 1.25rem;
|
||||
--text-caption-size: 0.75rem;
|
||||
--text-caption-lh: 1.0625rem;
|
||||
|
||||
/* ── Elevation ── low, warm-black shadows — print-flat until something
|
||||
* genuinely lifts. */
|
||||
--elevation-0: none;
|
||||
--elevation-1: 0 1px 2px oklch(0.18 0.02 40 / 0.07);
|
||||
--elevation-2: 0 1px 3px oklch(0.18 0.02 40 / 0.09), 0 1px 2px oklch(0.18 0.02 40 / 0.05);
|
||||
--elevation-3: 0 4px 8px -2px oklch(0.18 0.02 40 / 0.1), 0 2px 4px -2px oklch(0.18 0.02 40 / 0.05);
|
||||
--elevation-4: 0 12px 24px -6px oklch(0.18 0.02 40 / 0.12), 0 4px 8px -4px oklch(0.18 0.02 40 / 0.06);
|
||||
--elevation-5: 0 20px 40px -8px oklch(0.18 0.02 40 / 0.18);
|
||||
|
||||
--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 poster at night. Near-black warm charcoal (low chroma, hue
|
||||
* ~40, never blue-slate), cards stepping clearly lighter, and the vermilion
|
||||
* glowing slightly brighter with dark ink on top (bright-swatch/dark-ink).
|
||||
* The chrome stays vermilion — the sidebar does not go grey at night.
|
||||
*/
|
||||
.dark {
|
||||
--background: oklch(0.18 0.01 40);
|
||||
--foreground: oklch(0.95 0.008 55);
|
||||
|
||||
--card: oklch(0.23 0.012 40);
|
||||
--card-foreground: oklch(0.95 0.008 55);
|
||||
|
||||
--popover: oklch(0.25 0.012 40);
|
||||
--popover-foreground: oklch(0.95 0.008 55);
|
||||
|
||||
--primary: oklch(0.64 0.2 33);
|
||||
--primary-foreground: oklch(0.13 0.018 35);
|
||||
|
||||
--secondary: oklch(0.27 0.012 40);
|
||||
--secondary-foreground: oklch(0.92 0.01 55);
|
||||
|
||||
--muted: oklch(0.27 0.012 40);
|
||||
--muted-foreground: oklch(0.73 0.014 50);
|
||||
|
||||
--accent: oklch(0.32 0.075 33);
|
||||
--accent-foreground: oklch(0.89 0.08 40);
|
||||
|
||||
--destructive: oklch(0.52 0.2 15);
|
||||
--destructive-foreground: oklch(0.98 0.01 15);
|
||||
--success: oklch(0.7 0.15 150);
|
||||
--success-foreground: oklch(0.16 0.03 150);
|
||||
--warning: oklch(0.76 0.15 70);
|
||||
--warning-foreground: oklch(0.22 0.05 60);
|
||||
--info: oklch(0.66 0.13 250);
|
||||
--info-foreground: oklch(0.15 0.03 250);
|
||||
|
||||
--border: oklch(0.95 0.008 55 / 0.13);
|
||||
--input: oklch(0.95 0.008 55 / 0.15);
|
||||
--ring: oklch(0.64 0.2 33 / 0.55);
|
||||
|
||||
--chart-1: oklch(0.66 0.19 33);
|
||||
--chart-2: oklch(0.62 0.13 250);
|
||||
--chart-3: oklch(0.74 0.11 185);
|
||||
--chart-4: oklch(0.66 0.15 305);
|
||||
--chart-5: oklch(0.8 0.13 95);
|
||||
|
||||
/* Sidebar — still drenched, tuned a step deeper so near-white ink glows. */
|
||||
--sidebar: oklch(0.48 0.185 30);
|
||||
--sidebar-foreground: oklch(0.985 0.006 40);
|
||||
--sidebar-primary: oklch(0.97 0.01 55);
|
||||
--sidebar-primary-foreground: oklch(0.4 0.165 31);
|
||||
--sidebar-accent: oklch(0.4 0.16 30);
|
||||
--sidebar-accent-foreground: oklch(0.985 0.006 40);
|
||||
--sidebar-border: oklch(1 0 0 / 0.2);
|
||||
--sidebar-ring: oklch(0.97 0.01 55 / 0.6);
|
||||
|
||||
--syntax-keyword: oklch(0.72 0.13 305);
|
||||
--syntax-string: oklch(0.74 0.12 150);
|
||||
--syntax-number: oklch(0.78 0.13 60);
|
||||
--syntax-comment: oklch(0.62 0.012 45);
|
||||
--syntax-function: oklch(0.72 0.16 33);
|
||||
--syntax-type: oklch(0.72 0.1 200);
|
||||
|
||||
/* ── Chat surfaces (dark) ── user bubble a step deeper than the glowing
|
||||
* primary so near-white ink stays comfortably legible. */
|
||||
--chat-user-bg: oklch(0.5 0.19 30);
|
||||
--chat-user-fg: oklch(0.985 0.006 40);
|
||||
--chat-assistant-bg: oklch(0.23 0.012 40);
|
||||
--chat-assistant-fg: oklch(0.95 0.008 55);
|
||||
--chat-assistant-border: oklch(0.95 0.008 55 / 0.13);
|
||||
|
||||
--elevation-1: 0 1px 2px oklch(0 0 0 / 0.3);
|
||||
--elevation-2: 0 1px 3px oklch(0 0 0 / 0.4), 0 1px 2px oklch(0 0 0 / 0.25);
|
||||
--elevation-3: 0 4px 8px -2px oklch(0 0 0 / 0.4), 0 2px 4px -2px oklch(0 0 0 / 0.25);
|
||||
--elevation-4: 0 12px 24px -6px oklch(0 0 0 / 0.45), 0 4px 8px -4px oklch(0 0 0 / 0.25);
|
||||
--elevation-5: 0 20px 40px -8px oklch(0 0 0 / 0.55);
|
||||
}
|
||||
|
||||
/*
|
||||
* @layer base — Vermilion's poster philosophy.
|
||||
*
|
||||
* Condensed display type in committed sizes, warm paper for reading, and the
|
||||
* one hot color spent in slabs — chrome, primary, selection — never scattered.
|
||||
* Everything that isn't the poster stays quiet ink-on-paper.
|
||||
*/
|
||||
@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 — Anton, the single-weight poster voice. It ships at 400 only;
|
||||
* it is already black-condensed, so weight stays 400 and tracking opens a
|
||||
* hair (condensed faces need air, not squeeze). Case is the author's call —
|
||||
* the face carries the register without forced uppercase. */
|
||||
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: 400;
|
||||
text-transform: none;
|
||||
letter-spacing: 0.01em;
|
||||
/* inherit, not var(--foreground): identity themes put headings on
|
||||
* colored slabs (bg-primary heroes) — forcing the field ink there
|
||||
* breaks the slab's own foreground pairing. */
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
code, kbd, samp, pre,
|
||||
[data-slot="code"],
|
||||
[data-slot="code-block"] {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* Inline code — neutral ink chip, no brand color spent here. */
|
||||
: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;
|
||||
}
|
||||
|
||||
/* Selection — the poster color claims what you grab. */
|
||||
::selection {
|
||||
background: color-mix(in oklch, var(--primary) 30%, transparent);
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
/* Links — vermilion, underline-on-hover. */
|
||||
a {
|
||||
color: color-mix(in oklch, var(--primary) 75%, 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 — the vermilion ring, offset (not inset), for keyboard users.
|
||||
* Placed outside @layer base so it beats Tailwind's default ring utilities.
|
||||
*/
|
||||
[data-slot="button"]:focus-visible,
|
||||
[data-slot="input"]:focus-visible,
|
||||
[data-slot="textarea"]:focus-visible,
|
||||
[data-slot="select-trigger"]:focus-visible {
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Buttons — a touch of poster weight; opacity absorb on press, snappy. */
|
||||
[data-slot="button"] {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
/* Card titles — pinned to the subtitle role so the scale is the theme's
|
||||
* decision, not the card primitive's default. (Anton at title sizes inside a
|
||||
* card would shout; the subtitle size keeps the poster voice measured.) */
|
||||
[data-slot="card-title"] {
|
||||
font-size: var(--text-subtitle-size);
|
||||
line-height: var(--text-subtitle-lh);
|
||||
}
|
||||
|
||||
/* Touch targets — desktop keeps the crisp density 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 — clean paper surface, hairline border, vermilion 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(--border);
|
||||
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) 22%, 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;
|
||||
}
|
||||
Reference in New Issue
Block a user