lib-theme-hearth: initial import (warm-consumer set, 2026-07)
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>
This commit is contained in:
392
theme.css
Normal file
392
theme.css
Normal file
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Hearth — evening warmth, dark-first. The room after sunset: deep umber and
|
||||
* espresso surfaces that step lighter as they come toward you, the way
|
||||
* furniture catches firelight; a candle-glow amber primary; one slate-teal
|
||||
* counterpoint so the warmth never suffocates. Dark mode is the theme's
|
||||
* native identity — light mode is the morning after, a chroma-free off-white
|
||||
* with warm espresso ink and the same ember accent, deliberately outside the
|
||||
* saturated-AI cream band. Built for consumer evenings: reading, journaling,
|
||||
* the phone at 9pm.
|
||||
*
|
||||
* Palette rationale: every neutral lives on the umber axis (hue 40–70), never
|
||||
* blue-gray and never pure black — the darks are browns with the lights
|
||||
* turned down, so elevation can read as firelight (a faint warm rim plus a
|
||||
* soft umber ambient) instead of hard black drop shadows. The amber primary
|
||||
* carries dark ink, not white — dark-ink-on-bright is the contrast pattern
|
||||
* throughout.
|
||||
*
|
||||
* Token contract matches lib-theme-pristine/theme.css.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fonts: consuming apps load Bitter + Nunito Sans + JetBrains Mono at the top
|
||||
* of their entry CSS via the Google Fonts URL documented in this theme's
|
||||
* README. No @import here — PostCSS ordering forbids it once the theme is
|
||||
* imported after other statements.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--font-sans: "Nunito Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-heading: "Bitter", Georgia, "Times New Roman", 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. Mirror --font-heading/--font-sans. */
|
||||
--heading-family: "Bitter", Georgia, "Times New Roman", serif;
|
||||
--body-family: "Nunito Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
|
||||
/* ── Colors (light — the morning after) ──
|
||||
* True chroma-0 off-white body, warm espresso ink, ember primary.
|
||||
* The background deliberately avoids the warm-cream band; warmth is
|
||||
* carried by the ink and the ember, not the paper. */
|
||||
--background: oklch(0.975 0 0);
|
||||
--foreground: oklch(0.22 0.02 50);
|
||||
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.22 0.02 50);
|
||||
|
||||
--popover: oklch(0.99 0 0);
|
||||
--popover-foreground: oklch(0.22 0.02 50);
|
||||
|
||||
/* Ember — burnt orange deep enough for near-white ink by day. */
|
||||
--primary: oklch(0.48 0.15 40);
|
||||
--primary-foreground: oklch(0.98 0.008 80);
|
||||
|
||||
--secondary: oklch(0.93 0.008 60);
|
||||
--secondary-foreground: oklch(0.3 0.02 50);
|
||||
|
||||
--muted: oklch(0.94 0.006 60);
|
||||
--muted-foreground: oklch(0.45 0.02 55);
|
||||
|
||||
--accent: oklch(0.93 0.03 55);
|
||||
--accent-foreground: oklch(0.42 0.12 45);
|
||||
|
||||
--destructive: oklch(0.52 0.2 28);
|
||||
--destructive-foreground: oklch(0.98 0.008 80);
|
||||
--success: oklch(0.62 0.15 150);
|
||||
--success-foreground: oklch(0.15 0.03 150);
|
||||
--warning: oklch(0.75 0.13 75);
|
||||
--warning-foreground: oklch(0.22 0.05 75);
|
||||
/* Info — the one cool counterpoint: slate-teal. */
|
||||
--info: oklch(0.48 0.09 210);
|
||||
--info-foreground: oklch(0.97 0.008 210);
|
||||
|
||||
--border: oklch(0.22 0.02 50 / 0.12);
|
||||
--input: oklch(0.22 0.02 50 / 0.12);
|
||||
--ring: oklch(0.48 0.15 40 / 0.5);
|
||||
|
||||
/* Chart palette — ember / slate-teal / sage / heather / brick.
|
||||
* Hues ≥60° apart, lightness staggered between neighbours. */
|
||||
--chart-1: oklch(0.65 0.13 60);
|
||||
--chart-2: oklch(0.55 0.09 200);
|
||||
--chart-3: oklch(0.6 0.11 135);
|
||||
--chart-4: oklch(0.58 0.1 295);
|
||||
--chart-5: oklch(0.55 0.12 20);
|
||||
|
||||
--sidebar: oklch(0.95 0.003 60);
|
||||
--sidebar-foreground: oklch(0.22 0.02 50);
|
||||
--sidebar-primary: oklch(0.48 0.15 40);
|
||||
--sidebar-primary-foreground: oklch(0.98 0.008 80);
|
||||
--sidebar-accent: oklch(0.92 0.025 55);
|
||||
--sidebar-accent-foreground: oklch(0.42 0.12 45);
|
||||
--sidebar-border: oklch(0.22 0.02 50 / 0.08);
|
||||
--sidebar-ring: oklch(0.48 0.15 40 / 0.5);
|
||||
|
||||
--syntax-keyword: oklch(0.48 0.15 40);
|
||||
--syntax-string: oklch(0.5 0.11 150);
|
||||
--syntax-number: oklch(0.52 0.13 60);
|
||||
--syntax-comment: oklch(0.55 0.015 60);
|
||||
--syntax-function: oklch(0.48 0.09 230);
|
||||
--syntax-type: oklch(0.5 0.08 195);
|
||||
|
||||
/* ── Chat surfaces ──
|
||||
* User bubble is a filled deep-ember panel with near-white ink;
|
||||
* assistant bubble is a card surface with a hairline border. */
|
||||
--chat-user-bg: oklch(0.46 0.14 40);
|
||||
--chat-user-fg: oklch(0.98 0.008 80);
|
||||
--chat-assistant-bg: oklch(1 0 0);
|
||||
--chat-assistant-fg: oklch(0.22 0.02 50);
|
||||
--chat-assistant-border: oklch(0.22 0.02 50 / 0.12);
|
||||
|
||||
/* Generous-cozy radius — armchair, not app-store icon. */
|
||||
--radius: 1.0625rem;
|
||||
|
||||
/* ── Motion ── slow-blink calm: unhurried base, decelerate-heavy.
|
||||
* Springs alias to the gentle standard eases — personality lives in
|
||||
* the values, never in absence. */
|
||||
--duration-fast: 150ms;
|
||||
--duration-base: 350ms;
|
||||
--duration-slow: 500ms;
|
||||
--duration-slower: 700ms;
|
||||
--duration-spring: 550ms;
|
||||
|
||||
--ease-standard: cubic-bezier(0.25, 0.05, 0.2, 1);
|
||||
--ease-decelerate: cubic-bezier(0.05, 0.7, 0.1, 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-standard);
|
||||
--ease-spring-bouncy: var(--ease-decelerate);
|
||||
|
||||
/* ── Typography ── product register: fixed rem, full ramp.
|
||||
* Bitter for headings (warm slab-serif), Nunito Sans for body.
|
||||
* Body leans slightly large with generous leading — evening reading. */
|
||||
--text-hero-size: 3.25rem;
|
||||
--text-hero-lh: 1.08;
|
||||
--text-display-size: 2.75rem;
|
||||
--text-display-lh: 1.1;
|
||||
--text-headline-size: 2rem;
|
||||
--text-headline-lh: 1.15;
|
||||
--text-title-size: 1.5rem;
|
||||
--text-title-lh: 1.25;
|
||||
--text-subtitle-size: 1.1875rem;
|
||||
--text-subtitle-lh: 1.5;
|
||||
--text-body-size: 1.0625rem;
|
||||
--text-body-lh: 1.65;
|
||||
--text-label-size: 0.875rem;
|
||||
--text-label-lh: 1.25rem;
|
||||
--text-caption-size: 0.75rem;
|
||||
--text-caption-lh: 1rem;
|
||||
|
||||
/* ── Elevation (light) ── soft warm-umber paper shadows. */
|
||||
--elevation-0: none;
|
||||
--elevation-1: 0 1px 2px oklch(0.22 0.02 50 / 0.06);
|
||||
--elevation-2: 0 1px 3px oklch(0.22 0.02 50 / 0.08), 0 1px 2px oklch(0.22 0.02 50 / 0.05);
|
||||
--elevation-3: 0 4px 10px -2px oklch(0.22 0.02 50 / 0.09), 0 2px 4px -2px oklch(0.22 0.02 50 / 0.05);
|
||||
--elevation-4: 0 12px 26px -4px oklch(0.22 0.02 50 / 0.11), 0 4px 8px -4px oklch(0.22 0.02 50 / 0.05);
|
||||
--elevation-5: 0 22px 44px -8px oklch(0.22 0.02 50 / 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 hero. Deep espresso umber (never blue-gray, never black);
|
||||
* surfaces step LIGHTER as they come forward, like objects catching
|
||||
* firelight. Primary flips to candle-glow amber carrying dark umber ink.
|
||||
*/
|
||||
.dark {
|
||||
--background: oklch(0.17 0.018 55);
|
||||
--foreground: oklch(0.93 0.02 75);
|
||||
|
||||
--card: oklch(0.23 0.02 55);
|
||||
--card-foreground: oklch(0.93 0.02 75);
|
||||
|
||||
--popover: oklch(0.25 0.022 55);
|
||||
--popover-foreground: oklch(0.93 0.02 75);
|
||||
|
||||
/* Candle-glow amber; dark-ink-on-bright beats white-on-mid-tone. */
|
||||
--primary: oklch(0.78 0.145 65);
|
||||
--primary-foreground: oklch(0.22 0.05 55);
|
||||
|
||||
--secondary: oklch(0.28 0.025 55);
|
||||
--secondary-foreground: oklch(0.9 0.02 75);
|
||||
|
||||
--muted: oklch(0.26 0.02 55);
|
||||
--muted-foreground: oklch(0.74 0.03 70);
|
||||
|
||||
--accent: oklch(0.3 0.045 60);
|
||||
--accent-foreground: oklch(0.9 0.07 72);
|
||||
|
||||
--destructive: oklch(0.68 0.19 30);
|
||||
--destructive-foreground: oklch(0.15 0.04 30);
|
||||
--success: oklch(0.72 0.15 145);
|
||||
--success-foreground: oklch(0.16 0.03 145);
|
||||
--warning: oklch(0.8 0.14 80);
|
||||
--warning-foreground: oklch(0.2 0.05 80);
|
||||
/* Slate-teal counterpoint, dark ink on top. */
|
||||
--info: oklch(0.72 0.1 200);
|
||||
--info-foreground: oklch(0.15 0.03 210);
|
||||
|
||||
--border: oklch(0.95 0.03 75 / 0.12);
|
||||
--input: oklch(0.95 0.03 75 / 0.12);
|
||||
--ring: oklch(0.78 0.145 65 / 0.5);
|
||||
|
||||
--chart-1: oklch(0.78 0.13 65);
|
||||
--chart-2: oklch(0.7 0.09 200);
|
||||
--chart-3: oklch(0.74 0.11 135);
|
||||
--chart-4: oklch(0.72 0.09 295);
|
||||
--chart-5: oklch(0.68 0.1 20);
|
||||
|
||||
/* Sidebar sits a step further from the fire — darker than the page. */
|
||||
--sidebar: oklch(0.15 0.016 55);
|
||||
--sidebar-foreground: oklch(0.9 0.02 75);
|
||||
--sidebar-primary: oklch(0.78 0.145 65);
|
||||
--sidebar-primary-foreground: oklch(0.22 0.05 55);
|
||||
--sidebar-accent: oklch(0.26 0.04 60);
|
||||
--sidebar-accent-foreground: oklch(0.9 0.07 72);
|
||||
--sidebar-border: oklch(0.95 0.03 75 / 0.08);
|
||||
--sidebar-ring: oklch(0.78 0.145 65 / 0.5);
|
||||
|
||||
--syntax-keyword: oklch(0.8 0.13 65);
|
||||
--syntax-string: oklch(0.76 0.12 145);
|
||||
--syntax-number: oklch(0.82 0.12 80);
|
||||
--syntax-comment: oklch(0.6 0.02 65);
|
||||
--syntax-function: oklch(0.74 0.09 210);
|
||||
--syntax-type: oklch(0.76 0.07 180);
|
||||
|
||||
/* ── Chat surfaces (dark) ── */
|
||||
--chat-user-bg: oklch(0.44 0.11 50);
|
||||
--chat-user-fg: oklch(0.97 0.015 80);
|
||||
--chat-assistant-bg: oklch(0.26 0.022 55);
|
||||
--chat-assistant-fg: oklch(0.93 0.02 75);
|
||||
--chat-assistant-border: oklch(0.95 0.03 75 / 0.1);
|
||||
|
||||
/* ── Elevation (dark) — firelight, not drop shadow ──
|
||||
* Each level pairs a faint warm rim on the top edge (the surface
|
||||
* catching the glow) with a soft deep-umber ambient — never pure black.
|
||||
* Lift is mostly conveyed by the lighter surface itself. */
|
||||
--elevation-1: inset 0 1px 0 oklch(0.9 0.06 70 / 0.05), 0 1px 2px oklch(0.08 0.015 50 / 0.45);
|
||||
--elevation-2: inset 0 1px 0 oklch(0.9 0.06 70 / 0.07), 0 2px 6px oklch(0.08 0.015 50 / 0.5);
|
||||
--elevation-3: inset 0 1px 0 oklch(0.9 0.06 70 / 0.09), 0 6px 14px -2px oklch(0.08 0.015 50 / 0.55);
|
||||
--elevation-4: inset 0 1px 0 oklch(0.9 0.06 70 / 0.1), 0 12px 28px -4px oklch(0.08 0.015 50 / 0.6);
|
||||
--elevation-5: inset 0 1px 0 oklch(0.9 0.06 70 / 0.12), 0 22px 48px -8px oklch(0.08 0.015 50 / 0.7);
|
||||
}
|
||||
|
||||
/*
|
||||
* @layer base — Hearth's evening philosophy.
|
||||
*
|
||||
* No glass, no hard chrome. Slab-serif headings, generous body leading,
|
||||
* surfaces separated by warmth (lighter = closer) rather than heavy shadow.
|
||||
* Interactions settle slowly — nothing snaps in a room lit by a fire.
|
||||
*/
|
||||
@layer base {
|
||||
body {
|
||||
min-height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-body-size);
|
||||
line-height: var(--text-body-lh);
|
||||
letter-spacing: 0.002em;
|
||||
}
|
||||
|
||||
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.01em;
|
||||
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; the ember stays
|
||||
* reserved for actions, links and focus. */
|
||||
:not(pre) > code {
|
||||
padding: 0.1em 0.35em;
|
||||
border-radius: calc(var(--radius) * 0.35);
|
||||
background: color-mix(in oklch, var(--foreground) 7%, transparent);
|
||||
color: var(--foreground);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* Selection — an ember wash, like lamplight across the line. */
|
||||
::selection {
|
||||
background: color-mix(in oklch, var(--primary) 26%, transparent);
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent-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);
|
||||
}
|
||||
|
||||
/* Cards & overlay surfaces — a hairline plus the firelight elevation.
|
||||
* In dark mode the lighter surface does most of the lifting; the shadow
|
||||
* is an accent, not the mechanism. */
|
||||
[data-slot="card"],
|
||||
[data-slot="popover-content"],
|
||||
[data-slot="dropdown-menu-content"],
|
||||
[data-slot="context-menu-content"],
|
||||
[data-slot="hover-card-content"],
|
||||
[data-slot="select-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-2);
|
||||
}
|
||||
|
||||
[data-slot="dialog-content"],
|
||||
[data-slot="alert-dialog-content"],
|
||||
[data-slot="sheet-content"],
|
||||
[data-slot="drawer-content"] {
|
||||
box-shadow: var(--elevation-4);
|
||||
}
|
||||
|
||||
[data-slot="sidebar"] {
|
||||
background: var(--sidebar);
|
||||
border-right: var(--border-width-base) solid var(--sidebar-border);
|
||||
}
|
||||
|
||||
/* Buttons — settle, don't snap. */
|
||||
[data-slot="button"] {
|
||||
font-weight: 600;
|
||||
transition:
|
||||
opacity var(--duration-fast) var(--ease-standard),
|
||||
transform var(--duration-fast) var(--ease-standard);
|
||||
}
|
||||
[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(--ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlayered control tuning — outside @layer base so it beats Tailwind's
|
||||
* utility defaults shipped with shadcn primitives.
|
||||
*/
|
||||
|
||||
/* 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 its density; 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user