Includes the 2026-07-07 UX sweep: neutral inline-code chips (accent reserved for actions/links/focus), dark-mode card/popover lightness lift for visible surface separation, 44px touch-target floor on coarse pointers, card-title pinned to a named ramp role. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
471 lines
17 KiB
CSS
471 lines
17 KiB
CSS
/*
|
||
* Oat — warm greige, cozy-neutral theme. Oat, driftwood, mushroom, raw linen.
|
||
*
|
||
* The thesis: a genuinely NEUTRAL, calm consumer surface that still has warmth
|
||
* and material character. The neutral is a real GREIGE mid-tone (grey-warm),
|
||
* not a pale near-white and not a drenched color field. Built for an everyday
|
||
* personal-cloud utility that should feel grounded and tactile, never loud.
|
||
*
|
||
* Escape from the "AI cream" band (L 0.84–0.97, warm chroma, hue 40–100) is by
|
||
* DEPTH + restraint, not by picking a different hue: the body background sits at
|
||
* a noticeably lower lightness (L≈0.91) with very low warm chroma (≈0.009, hue
|
||
* ~78), so it reads as a soft grey-warm "oat" surface rather than a bright cream.
|
||
* Cards step UP to a warm near-white (L≈0.975) so the card-on-field separation
|
||
* is clearly visible and does real hierarchy work — that visible step is the
|
||
* theme's premium tell. Ink is a warm charcoal-taupe. The accent is a soft,
|
||
* muted clay-rose coral (hue ~35), spent surgically on the primary action,
|
||
* selection and focus; a muted slate-blue is the single cool counterpoint (info).
|
||
*
|
||
* Dark mode is "oats at night": a warm dark taupe-charcoal (low chroma, warm
|
||
* hue ~70, deep but not pure black and never blue-gray), cards stepping lighter
|
||
* like firelight on material, the coral brightened, dark-ink-on-bright per the
|
||
* contract's contrast pattern.
|
||
*
|
||
* Token contract: Crema v1 (create-crema-app/TOKEN_CONTRACT.md).
|
||
*/
|
||
|
||
/*
|
||
* Fonts: consuming apps load Hanken Grotesk + Fraunces + 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 when the theme is imported after other statements, so neither
|
||
* is done in this file.
|
||
*/
|
||
|
||
:root {
|
||
--font-sans: "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||
--font-heading: "Fraunces", "Hanken Grotesk", Georgia, serif;
|
||
--font-mono: "JetBrains Mono", ui-monospace, 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: "Fraunces", "Hanken Grotesk", Georgia, serif;
|
||
--body-family: "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||
|
||
/* ── Colors ── greige mid-tone field, warm near-white cards, warm
|
||
* charcoal-taupe ink, muted clay-rose coral accent. */
|
||
|
||
/* Body field: a genuine grey-warm "oat", noticeably below cream lightness. */
|
||
--background: oklch(0.91 0.009 78);
|
||
--foreground: oklch(0.24 0.012 55);
|
||
|
||
/* Cards step UP toward warm near-white — the visible card-on-field step. */
|
||
--card: oklch(0.975 0.006 82);
|
||
--card-foreground: oklch(0.24 0.012 55);
|
||
|
||
--popover: oklch(0.98 0.005 82);
|
||
--popover-foreground: oklch(0.24 0.012 55);
|
||
|
||
/* Primary: muted clay-rose coral. Deep enough that warm-white text clears
|
||
* the 4.5:1 floor (white-on-mid-coral usually misses; this is L≈0.55). */
|
||
--primary: oklch(0.55 0.15 35);
|
||
--primary-foreground: oklch(0.99 0.01 60);
|
||
|
||
/* Secondary / muted: a soft greige a step below the field. */
|
||
--secondary: oklch(0.86 0.014 75);
|
||
--secondary-foreground: oklch(0.3 0.012 55);
|
||
|
||
--muted: oklch(0.86 0.012 78);
|
||
--muted-foreground: oklch(0.44 0.012 60);
|
||
|
||
/* Accent: a pale coral wash carrying deep-coral ink. */
|
||
--accent: oklch(0.88 0.03 32);
|
||
--accent-foreground: oklch(0.4 0.12 32);
|
||
|
||
/* Status quartet — kept clearly distinct from the muted coral primary:
|
||
* destructive is a hotter, higher-chroma true red; success sage-green;
|
||
* warning amber; info the cool slate-blue counterpoint. */
|
||
--destructive: oklch(0.55 0.21 25);
|
||
--destructive-foreground: oklch(0.99 0.01 60);
|
||
--success: oklch(0.57 0.12 150);
|
||
--success-foreground: oklch(0.14 0.04 150);
|
||
--warning: oklch(0.76 0.13 75);
|
||
--warning-foreground: oklch(0.24 0.04 75);
|
||
--info: oklch(0.52 0.12 245);
|
||
--info-foreground: oklch(0.99 0.01 60);
|
||
|
||
--border: oklch(0.24 0.012 55 / 0.14);
|
||
--input: oklch(0.24 0.012 55 / 0.14);
|
||
--ring: oklch(0.55 0.15 35 / 0.5);
|
||
|
||
/* Chart palette — cozy-neutral swatches: clay coral / sage / slate-blue /
|
||
* amber / mauve. Hues ≥60° apart, adjacent-index lightness alternated. */
|
||
--chart-1: oklch(0.62 0.13 35);
|
||
--chart-2: oklch(0.68 0.09 150);
|
||
--chart-3: oklch(0.6 0.1 245);
|
||
--chart-4: oklch(0.78 0.1 75);
|
||
--chart-5: oklch(0.7 0.07 320);
|
||
|
||
--sidebar: oklch(0.89 0.011 78);
|
||
--sidebar-foreground: oklch(0.24 0.012 55);
|
||
--sidebar-primary: oklch(0.55 0.15 35);
|
||
--sidebar-primary-foreground: oklch(0.99 0.01 60);
|
||
--sidebar-accent: oklch(0.88 0.03 32);
|
||
--sidebar-accent-foreground: oklch(0.4 0.12 32);
|
||
--sidebar-border: oklch(0.24 0.012 55 / 0.1);
|
||
--sidebar-ring: oklch(0.55 0.15 35 / 0.5);
|
||
|
||
/* Syntax (Tier 2) — warm-neutral code palette. */
|
||
--syntax-keyword: oklch(0.5 0.14 35);
|
||
--syntax-string: oklch(0.5 0.1 150);
|
||
--syntax-number: oklch(0.54 0.12 75);
|
||
--syntax-comment: oklch(0.55 0.008 78);
|
||
--syntax-function: oklch(0.5 0.12 245);
|
||
--syntax-type: oklch(0.52 0.08 200);
|
||
|
||
/* ── Chat surfaces ──
|
||
* Contract for lib-agent-chat-ui / lib-agent-dock-ui. User bubble is a
|
||
* filled deep-coral panel with warm-white text; assistant bubble is a
|
||
* card-like warm-white surface with a hairline border. */
|
||
--chat-user-bg: oklch(0.5 0.14 35);
|
||
--chat-user-fg: oklch(0.99 0.01 60);
|
||
--chat-assistant-bg: oklch(0.975 0.006 82);
|
||
--chat-assistant-fg: oklch(0.24 0.012 55);
|
||
--chat-assistant-border: oklch(0.24 0.012 55 / 0.12);
|
||
|
||
/* Soft-friendly radius. */
|
||
--radius: 1rem;
|
||
|
||
/* ── Motion ── calm-buoyant, base ~300ms. Springs are real, gently damped. */
|
||
--duration-fast: 160ms;
|
||
--duration-base: 300ms;
|
||
--duration-slow: 450ms;
|
||
--duration-slower: 600ms;
|
||
--duration-spring: 500ms;
|
||
|
||
--ease-standard: cubic-bezier(0.25, 0.1, 0.25, 1);
|
||
--ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
|
||
--ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
|
||
|
||
--ease-spring-gentle: linear(0, 0.04 8%, 0.12 15%, 0.23 22%, 0.37 30%, 0.51 38%, 0.63 46%, 0.74 54%, 0.83 62%, 0.89 70%, 0.94 78%, 0.97 86%, 0.99 94%, 1);
|
||
--ease-spring-snappy: linear(0, 0.06 6%, 0.18 12%, 0.33 18%, 0.5 25%, 0.65 32%, 0.8 40%, 0.9 48%, 0.98 56%, 1.015 64%, 1.02 72%, 1.015 80%, 1.005 88%, 1);
|
||
--ease-spring-bouncy: linear(0, 0.08 8%, 0.22 16%, 0.4 24%, 0.58 32%, 0.75 40%, 0.88 48%, 0.98 56%, 1.04 64%, 1.06 72%, 1.04 80%, 1.01 86%, 0.995 92%, 1);
|
||
|
||
/* Emphasized moments settle with the gentle spring. */
|
||
--ease-emphasized: var(--ease-spring-gentle);
|
||
|
||
/* ── Typography ── product register, fixed rem, full role ramp.
|
||
* Warm-humanist and cozy but crisp: Fraunces headings, Hanken Grotesk body. */
|
||
--text-hero-size: 3.5rem;
|
||
--text-hero-lh: 1.05;
|
||
--text-display-size: 3rem;
|
||
--text-display-lh: 1.1;
|
||
--text-headline-size: 2rem;
|
||
--text-headline-lh: 1.2;
|
||
--text-title-size: 1.5rem;
|
||
--text-title-lh: 1.3;
|
||
--text-subtitle-size: 1.25rem;
|
||
--text-subtitle-lh: 1.5;
|
||
--text-body-size: 1.0625rem;
|
||
--text-body-lh: 1.65;
|
||
--text-label-size: 0.9375rem;
|
||
--text-label-lh: 1.25rem;
|
||
--text-caption-size: 0.8125rem;
|
||
--text-caption-lh: 1.125rem;
|
||
|
||
/* ── Elevation ── soft warm-grey diffuse shadows, derived from the warm
|
||
* ink at low alpha. Gentle, tactile, never a hard drop. */
|
||
--elevation-0: none;
|
||
--elevation-1: 0 1px 2px oklch(0.24 0.012 55 / 0.05);
|
||
--elevation-2: 0 1px 3px oklch(0.24 0.012 55 / 0.07), 0 1px 2px oklch(0.24 0.012 55 / 0.04);
|
||
--elevation-3: 0 4px 12px -3px oklch(0.24 0.012 55 / 0.08), 0 2px 6px -3px oklch(0.24 0.012 55 / 0.05);
|
||
--elevation-4: 0 12px 28px -6px oklch(0.24 0.012 55 / 0.1), 0 6px 12px -6px oklch(0.24 0.012 55 / 0.05);
|
||
--elevation-5: 0 24px 48px -10px oklch(0.24 0.012 55 / 0.16);
|
||
|
||
--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 — "oats at night". Warm dark taupe-charcoal (L≈0.20, hue ~70), deep
|
||
* but not pure black and not blue-gray. Cards step LIGHTER (firelight on
|
||
* material). Coral brightens; status foregrounds go dark-ink-on-bright.
|
||
*/
|
||
.dark {
|
||
--background: oklch(0.2 0.008 70);
|
||
--foreground: oklch(0.92 0.01 82);
|
||
|
||
--card: oklch(0.26 0.009 70);
|
||
--card-foreground: oklch(0.92 0.01 82);
|
||
|
||
--popover: oklch(0.275 0.009 70);
|
||
--popover-foreground: oklch(0.92 0.01 82);
|
||
|
||
--primary: oklch(0.68 0.14 38);
|
||
--primary-foreground: oklch(0.2 0.03 38);
|
||
|
||
--secondary: oklch(0.28 0.008 70);
|
||
--secondary-foreground: oklch(0.9 0.01 82);
|
||
|
||
--muted: oklch(0.28 0.008 70);
|
||
--muted-foreground: oklch(0.71 0.011 80);
|
||
|
||
--accent: oklch(0.32 0.05 35);
|
||
--accent-foreground: oklch(0.85 0.08 38);
|
||
|
||
--destructive: oklch(0.65 0.2 28);
|
||
--destructive-foreground: oklch(0.18 0.03 28);
|
||
--success: oklch(0.68 0.14 150);
|
||
--success-foreground: oklch(0.18 0.03 150);
|
||
--warning: oklch(0.8 0.13 75);
|
||
--warning-foreground: oklch(0.22 0.04 75);
|
||
--info: oklch(0.7 0.12 245);
|
||
--info-foreground: oklch(0.16 0.03 245);
|
||
|
||
--border: oklch(0.92 0.01 82 / 0.12);
|
||
--input: oklch(0.92 0.01 82 / 0.12);
|
||
--ring: oklch(0.68 0.14 38 / 0.5);
|
||
|
||
--chart-1: oklch(0.72 0.13 38);
|
||
--chart-2: oklch(0.74 0.09 150);
|
||
--chart-3: oklch(0.7 0.1 245);
|
||
--chart-4: oklch(0.82 0.1 75);
|
||
--chart-5: oklch(0.76 0.07 320);
|
||
|
||
--sidebar: oklch(0.22 0.008 70);
|
||
--sidebar-foreground: oklch(0.92 0.01 82);
|
||
--sidebar-primary: oklch(0.68 0.14 38);
|
||
--sidebar-primary-foreground: oklch(0.2 0.03 38);
|
||
--sidebar-accent: oklch(0.32 0.05 35);
|
||
--sidebar-accent-foreground: oklch(0.85 0.08 38);
|
||
--sidebar-border: oklch(0.92 0.01 82 / 0.08);
|
||
--sidebar-ring: oklch(0.68 0.14 38 / 0.5);
|
||
|
||
--syntax-keyword: oklch(0.75 0.13 38);
|
||
--syntax-string: oklch(0.74 0.12 150);
|
||
--syntax-number: oklch(0.8 0.12 75);
|
||
--syntax-comment: oklch(0.55 0.008 78);
|
||
--syntax-function: oklch(0.72 0.12 245);
|
||
--syntax-type: oklch(0.72 0.08 200);
|
||
|
||
/* ── Chat surfaces (dark) ── */
|
||
--chat-user-bg: oklch(0.55 0.15 38);
|
||
--chat-user-fg: oklch(0.99 0.01 60);
|
||
--chat-assistant-bg: oklch(0.26 0.009 70);
|
||
--chat-assistant-fg: oklch(0.92 0.01 82);
|
||
--chat-assistant-border: oklch(0.92 0.01 82 / 0.12);
|
||
|
||
/* Deep warm shadows for the night palette. */
|
||
--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 12px -3px oklch(0 0 0 / 0.4), 0 2px 6px -3px oklch(0 0 0 / 0.28);
|
||
--elevation-4: 0 12px 28px -6px oklch(0 0 0 / 0.45), 0 6px 12px -6px oklch(0 0 0 / 0.3);
|
||
--elevation-5: 0 24px 48px -10px oklch(0 0 0 / 0.55);
|
||
}
|
||
|
||
/*
|
||
* @layer base — Oat's cozy-neutral philosophy.
|
||
*
|
||
* No glass, no brutalist offsets. Warm-humanist type, soft-round corners,
|
||
* gentle diffuse shadows. Cards sit on the greige field with a hairline border
|
||
* AND a visible lightness step so hierarchy reads even before the shadow does.
|
||
*/
|
||
@layer base {
|
||
body {
|
||
min-height: 100vh;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
letter-spacing: -0.006em;
|
||
font-family: var(--font-sans);
|
||
font-size: var(--text-body-size);
|
||
line-height: var(--text-body-lh);
|
||
}
|
||
|
||
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.012em;
|
||
color: var(--foreground);
|
||
}
|
||
|
||
code, kbd, samp, pre,
|
||
[data-slot="code"],
|
||
[data-slot="code-block"] {
|
||
font-family: var(--font-mono);
|
||
}
|
||
|
||
/* Inline code — neutral ink on a warm tinted chip. Coral stays reserved
|
||
* for actions, links and focus so the accent keeps pointing at one thing. */
|
||
:not(pre) > code {
|
||
padding: 0.1em 0.35em;
|
||
border-radius: calc(var(--radius) * 0.4);
|
||
background: color-mix(in oklch, var(--foreground) 7%, transparent);
|
||
color: var(--foreground);
|
||
font-size: 0.9em;
|
||
}
|
||
|
||
/* Selection — soft coral wash. */
|
||
::selection {
|
||
background: color-mix(in oklch, var(--primary) 20%, transparent);
|
||
color: var(--foreground);
|
||
}
|
||
|
||
/* Anchor links — coral, underline warms on hover. */
|
||
a {
|
||
color: var(--primary);
|
||
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);
|
||
}
|
||
|
||
/* Card & overlay surfaces — soft warm-grey diffuse shadow so surfaces feel
|
||
* lifted off the oat field, tactile rather than hard-edged. */
|
||
[data-slot="card"],
|
||
[data-slot="popover-content"],
|
||
[data-slot="dropdown-menu-content"],
|
||
[data-slot="context-menu-content"],
|
||
[data-slot="menubar-content"],
|
||
[data-slot="hover-card-content"],
|
||
[data-slot="select-content"],
|
||
[data-slot="combobox-content"],
|
||
[data-slot="command"],
|
||
[data-slot="tooltip-content"],
|
||
[data-slot="sheet-content"],
|
||
[data-slot="dialog-content"],
|
||
[data-slot="alert-dialog-content"],
|
||
[data-slot="drawer-content"] {
|
||
border: var(--border-width-base) solid var(--border);
|
||
box-shadow: var(--elevation-3);
|
||
}
|
||
|
||
.dark [data-slot="card"],
|
||
.dark [data-slot="popover-content"],
|
||
.dark [data-slot="dropdown-menu-content"],
|
||
.dark [data-slot="context-menu-content"],
|
||
.dark [data-slot="menubar-content"],
|
||
.dark [data-slot="hover-card-content"],
|
||
.dark [data-slot="select-content"],
|
||
.dark [data-slot="combobox-content"],
|
||
.dark [data-slot="command"],
|
||
.dark [data-slot="tooltip-content"],
|
||
.dark [data-slot="sheet-content"],
|
||
.dark [data-slot="dialog-content"],
|
||
.dark [data-slot="alert-dialog-content"],
|
||
.dark [data-slot="drawer-content"] {
|
||
box-shadow: var(--elevation-4);
|
||
}
|
||
|
||
[data-slot="button"] {
|
||
font-weight: 500;
|
||
}
|
||
|
||
[data-slot="sidebar"] {
|
||
background: var(--sidebar);
|
||
border-right: var(--border-width-base) solid var(--sidebar-border);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Unlayered control tuning — placed outside @layer base so it beats Tailwind's
|
||
* default utility classes shipped with shadcn primitives. Oat's controls are
|
||
* comfortable with soft-round corners and a flush coral focus ring rather than
|
||
* Tailwind's 3px halo. Inputs are ≥44px everywhere; buttons keep their density
|
||
* ladder under a mouse and clear the 44px touch floor on coarse pointers.
|
||
*/
|
||
@media (pointer: coarse) {
|
||
[data-slot="button"] {
|
||
min-height: 2.75rem;
|
||
min-width: 2.75rem;
|
||
}
|
||
}
|
||
|
||
[data-slot="button"]:hover:not(:disabled) {
|
||
opacity: 0.9;
|
||
}
|
||
[data-slot="button"]:active:not(:disabled) {
|
||
opacity: 0.82;
|
||
transform: scale(0.99);
|
||
}
|
||
[data-slot="button"]:focus-visible {
|
||
outline: 2px solid var(--primary);
|
||
outline-offset: 2px;
|
||
box-shadow: none;
|
||
}
|
||
|
||
[data-slot="input"],
|
||
[data-slot="textarea"],
|
||
[data-slot="select-trigger"] {
|
||
min-height: 2.75rem;
|
||
padding-inline: 1rem;
|
||
font-size: 0.9375rem;
|
||
background: var(--card);
|
||
border: 1px 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="textarea"] {
|
||
min-height: 6.25rem;
|
||
padding-block: 0.75rem;
|
||
line-height: 1.55;
|
||
resize: vertical;
|
||
}
|
||
[data-slot="input"]::placeholder,
|
||
[data-slot="textarea"]::placeholder {
|
||
color: var(--muted-foreground);
|
||
opacity: 0.7;
|
||
}
|
||
[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) 25%, transparent);
|
||
}
|
||
[data-slot="input"]:focus,
|
||
[data-slot="input"]:focus-visible,
|
||
[data-slot="textarea"]:focus,
|
||
[data-slot="textarea"]:focus-visible,
|
||
[data-slot="select-trigger"]:focus,
|
||
[data-slot="select-trigger"]:focus-visible {
|
||
border-color: var(--primary);
|
||
box-shadow: 0 0 0 1px var(--primary);
|
||
outline: none;
|
||
}
|
||
|
||
/* ─── Cards ───
|
||
* Warm near-white surface a clear step above the oat field, soft-round 1rem
|
||
* corners, hairline border, gentle lift on hover. */
|
||
[data-slot="card"] {
|
||
background: var(--card);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
overflow: hidden;
|
||
transition:
|
||
border-color var(--duration-base) var(--ease-standard),
|
||
box-shadow var(--duration-base) var(--ease-standard);
|
||
}
|
||
[data-slot="card"]:hover {
|
||
box-shadow: var(--elevation-4);
|
||
}
|
||
[data-slot="card-title"] {
|
||
font-family: var(--font-heading);
|
||
font-weight: 600;
|
||
letter-spacing: -0.012em;
|
||
font-size: var(--text-title-size);
|
||
line-height: var(--text-title-lh);
|
||
}
|
||
[data-slot="card-description"] {
|
||
color: var(--muted-foreground);
|
||
font-size: 0.9375rem;
|
||
}
|