Files
lib-theme-sorbet/theme.css
jules e1a33a3da7 lib-theme-sorbet: 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>
2026-07-07 22:35:56 +10:00

463 lines
17 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Sorbet — sherbet pop, playful-first. A toybox for adults with the
* discipline of a real product theme: chunky corners, springy motion, and a
* full sherbet palette (peach-coral, butter, mint, sky, berry) spent with
* intent, not confetti. The paper stays a clean cool near-white — the warmth
* lives entirely in the accents — and the ink is a soft berry-charcoal that
* never goes harsh black.
*
* Palette rationale: every sherbet hue owns a semantic role. Peach-coral is
* the hero and only the hero (--primary, chat-user, ring). Butter = warning,
* mint = success, sky = info, raspberry = destructive. Fills are bright and
* carry dark berry ink on top (the ivrea pattern) because pastel + white
* text always fails the contrast floor. Muted surfaces get a barely-there
* peach tint (chroma ≤ 0.015) so the neutrals lean toward the brand without
* turning cream. Dark mode is "sorbet at midnight": deep plum-charcoal with
* a berry undertone — never blue-gray — with the sherbets luminous on top.
*
* Token contract matches lib-theme-pristine/theme.css.
*/
/*
* Fonts: consuming apps should load Bricolage Grotesque + Figtree +
* 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 inside this file — 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 here.
*/
:root {
--font-sans: "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-heading: "Bricolage Grotesque", "Figtree", -apple-system, sans-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: "Bricolage Grotesque", "Figtree", -apple-system, sans-serif;
--body-family: "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
/* ── Colors ── clean cool near-white paper, berry-charcoal ink,
* peach-coral hero. */
--background: oklch(0.985 0.004 330);
--foreground: oklch(0.25 0.022 340);
--card: oklch(1 0 0);
--card-foreground: oklch(0.25 0.022 340);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.25 0.022 340);
/* Peach-coral hero — bright fill, dark berry ink on top. */
--primary: oklch(0.72 0.16 38);
--primary-foreground: oklch(0.2 0.05 30);
/* Soft peach chip — quiet sibling of primary. */
--secondary: oklch(0.94 0.03 45);
--secondary-foreground: oklch(0.32 0.04 30);
/* Muted — barely-there peach tint, never cream. */
--muted: oklch(0.955 0.012 40);
--muted-foreground: oklch(0.48 0.025 340);
/* Berry tint accent — hover washes, selected rows. */
--accent: oklch(0.93 0.035 350);
--accent-foreground: oklch(0.38 0.12 350);
/* Status quartet — each sherbet owns a meaning. Bright fills, dark ink. */
--destructive: oklch(0.64 0.2 12);
--destructive-foreground: oklch(0.16 0.05 12);
--success: oklch(0.8 0.14 160);
--success-foreground: oklch(0.2 0.05 160);
--warning: oklch(0.86 0.13 90);
--warning-foreground: oklch(0.28 0.06 80);
--info: oklch(0.76 0.1 230);
--info-foreground: oklch(0.18 0.04 240);
--border: oklch(0.25 0.022 340 / 0.12);
--input: oklch(0.25 0.022 340 / 0.14);
--ring: oklch(0.72 0.16 38 / 0.55);
/* Chart palette — the five sherbets straight from the tub:
* coral 38° / butter 98° / mint 160° / sky 230° / berry 330°.
* Hues ≥60° apart, adjacent lightness deliberately staggered. */
--chart-1: oklch(0.72 0.16 38);
--chart-2: oklch(0.85 0.13 98);
--chart-3: oklch(0.76 0.13 160);
--chart-4: oklch(0.68 0.11 230);
--chart-5: oklch(0.58 0.16 330);
--sidebar: oklch(0.97 0.006 330);
--sidebar-foreground: oklch(0.25 0.022 340);
--sidebar-primary: oklch(0.72 0.16 38);
--sidebar-primary-foreground: oklch(0.2 0.05 30);
--sidebar-accent: oklch(0.93 0.035 350);
--sidebar-accent-foreground: oklch(0.38 0.12 350);
--sidebar-border: oklch(0.25 0.022 340 / 0.1);
--sidebar-ring: oklch(0.72 0.16 38 / 0.55);
--syntax-keyword: oklch(0.52 0.16 350);
--syntax-string: oklch(0.5 0.11 160);
--syntax-number: oklch(0.55 0.14 55);
--syntax-comment: oklch(0.55 0.02 340);
--syntax-function: oklch(0.5 0.12 230);
--syntax-type: oklch(0.55 0.12 38);
/* ── Chat surfaces ──
* Contract for lib-agent-chat-ui / lib-agent-dock-ui. User bubble is the
* coral hero with berry ink; assistant is a card surface with hairline. */
--chat-user-bg: oklch(0.72 0.16 38);
--chat-user-fg: oklch(0.2 0.05 30);
--chat-assistant-bg: oklch(1 0 0);
--chat-assistant-fg: oklch(0.25 0.022 340);
--chat-assistant-border: oklch(0.25 0.022 340 / 0.12);
/* Chunky, toy-like corners — buttons verge on pill without rounded-full. */
--radius: 1.375rem;
/* ── Motion ── the springy one of the family: fast durations, genuine
* spring curves with real overshoot. These springs are NOT aliases. */
--duration-fast: 110ms;
--duration-base: 200ms;
--duration-slow: 300ms;
--duration-slower: 420ms;
--duration-spring: 480ms;
--ease-standard: cubic-bezier(0.3, 0, 0.2, 1);
--ease-decelerate: cubic-bezier(0.05, 0.7, 0.1, 1);
--ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
/* Gentle: settles with a whisper of overshoot (~2%). */
--ease-spring-gentle: linear(0, 0.1 9%, 0.29 18%, 0.52 27%, 0.72 36%, 0.87 45%, 0.97 54%, 1.02 63%, 1.025 70%, 1.015 79%, 1.005 88%, 1);
/* Snappy: quick attack, ~9% overshoot, one crisp settle. */
--ease-spring-snappy: linear(0, 0.22 6%, 0.53 12%, 0.8 19%, 0.99 26%, 1.09 34%, 1.1 41%, 1.06 50%, 1.015 61%, 0.996 72%, 0.999 84%, 1);
/* Bouncy: the signature — 18% overshoot with a visible second bounce. */
--ease-spring-bouncy: linear(0, 0.26 6%, 0.61 12%, 0.93 19%, 1.13 26%, 1.18 32%, 1.13 40%, 1.03 48%, 0.955 57%, 0.945 64%, 0.975 73%, 1.005 82%, 1.01 90%, 1);
/* Emphasized: overshoot bezier for hero moments that can't take linear(). */
--ease-emphasized: cubic-bezier(0.22, 1.36, 0.32, 1);
/* ── Typography ── product register, fixed rems. Headings carry real
* weight (700800 via @layer base) — the chunk is part of the voice. */
--text-hero-size: 3.5rem;
--text-hero-lh: 1.03;
--text-display-size: 2.75rem;
--text-display-lh: 1.08;
--text-headline-size: 2rem;
--text-headline-lh: 1.15;
--text-title-size: 1.5rem;
--text-title-lh: 1.25;
--text-subtitle-size: 1.125rem;
--text-subtitle-lh: 1.5;
--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: 1rem;
/* ── Elevation ── playful but flat-ish: crisp small offsets, tight blur,
* shadow color leaning berry rather than gray mist. */
--elevation-0: none;
--elevation-1: 0 1px 0 oklch(0.3 0.05 340 / 0.06), 0 2px 4px -2px oklch(0.3 0.05 340 / 0.08);
--elevation-2: 0 2px 0 oklch(0.3 0.05 340 / 0.05), 0 3px 6px -3px oklch(0.3 0.05 340 / 0.12);
--elevation-3: 0 3px 0 oklch(0.3 0.05 340 / 0.05), 0 6px 12px -6px oklch(0.3 0.05 340 / 0.16);
--elevation-4: 0 4px 0 oklch(0.3 0.05 340 / 0.04), 0 10px 20px -8px oklch(0.3 0.05 340 / 0.2);
--elevation-5: 0 6px 0 oklch(0.3 0.05 340 / 0.04), 0 16px 32px -12px oklch(0.3 0.05 340 / 0.26);
--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 — "sorbet at midnight". Deep plum-charcoal with a berry
* undertone (never blue-gray); the sherbet accents go luminous against it.
*/
.dark {
--background: oklch(0.2 0.022 330);
--foreground: oklch(0.94 0.012 340);
--card: oklch(0.26 0.024 330);
--card-foreground: oklch(0.94 0.012 340);
--popover: oklch(0.275 0.026 330);
--popover-foreground: oklch(0.94 0.012 340);
--primary: oklch(0.76 0.15 38);
--primary-foreground: oklch(0.2 0.05 30);
--secondary: oklch(0.3 0.03 330);
--secondary-foreground: oklch(0.92 0.02 40);
--muted: oklch(0.28 0.026 330);
--muted-foreground: oklch(0.73 0.02 340);
--accent: oklch(0.34 0.06 350);
--accent-foreground: oklch(0.89 0.06 350);
--destructive: oklch(0.68 0.19 12);
--destructive-foreground: oklch(0.14 0.04 12);
--success: oklch(0.79 0.14 160);
--success-foreground: oklch(0.2 0.05 160);
--warning: oklch(0.86 0.13 90);
--warning-foreground: oklch(0.28 0.06 80);
--info: oklch(0.76 0.1 230);
--info-foreground: oklch(0.18 0.04 240);
--border: oklch(0.96 0.01 340 / 0.12);
--input: oklch(0.96 0.01 340 / 0.14);
--ring: oklch(0.76 0.15 38 / 0.55);
--chart-1: oklch(0.76 0.15 38);
--chart-2: oklch(0.87 0.12 98);
--chart-3: oklch(0.79 0.13 160);
--chart-4: oklch(0.72 0.11 230);
--chart-5: oklch(0.68 0.15 330);
--sidebar: oklch(0.22 0.022 330);
--sidebar-foreground: oklch(0.94 0.012 340);
--sidebar-primary: oklch(0.76 0.15 38);
--sidebar-primary-foreground: oklch(0.2 0.05 30);
--sidebar-accent: oklch(0.34 0.06 350);
--sidebar-accent-foreground: oklch(0.89 0.06 350);
--sidebar-border: oklch(0.96 0.01 340 / 0.1);
--sidebar-ring: oklch(0.76 0.15 38 / 0.55);
--syntax-keyword: oklch(0.78 0.12 350);
--syntax-string: oklch(0.78 0.12 160);
--syntax-number: oklch(0.8 0.13 55);
--syntax-comment: oklch(0.6 0.02 340);
--syntax-function: oklch(0.76 0.1 230);
--syntax-type: oklch(0.78 0.12 38);
/* ── Chat surfaces (dark) ── */
--chat-user-bg: oklch(0.76 0.15 38);
--chat-user-fg: oklch(0.2 0.05 30);
--chat-assistant-bg: oklch(0.28 0.026 330);
--chat-assistant-fg: oklch(0.94 0.012 340);
--chat-assistant-border: oklch(0.96 0.01 340 / 0.1);
/* Crisp offsets survive dark mode as plain black — the berry tint
* would vanish against plum anyway. */
--elevation-1: 0 1px 0 oklch(0 0 0 / 0.25), 0 2px 4px -2px oklch(0 0 0 / 0.35);
--elevation-2: 0 2px 0 oklch(0 0 0 / 0.22), 0 3px 6px -3px oklch(0 0 0 / 0.4);
--elevation-3: 0 3px 0 oklch(0 0 0 / 0.2), 0 6px 12px -6px oklch(0 0 0 / 0.45);
--elevation-4: 0 4px 0 oklch(0 0 0 / 0.18), 0 10px 20px -8px oklch(0 0 0 / 0.5);
--elevation-5: 0 6px 0 oklch(0 0 0 / 0.16), 0 16px 32px -12px oklch(0 0 0 / 0.55);
}
/*
* @layer base — Sorbet's playful register.
*
* Chunky headings (700800), springy press feedback on buttons, coral
* selection. No glass, no gradients-as-ideas: the pop comes from color
* roles, corner radius, and motion that genuinely settles.
*/
@layer base {
body {
min-height: 100vh;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
letter-spacing: -0.008em;
font-family: var(--font-sans);
font-size: var(--text-body-size);
line-height: var(--text-body-lh);
}
/* Chunky friendly geometric display — real weight is part of the voice. */
h1, h2,
[data-slot="dialog-title"],
[data-slot="sheet-title"],
[data-slot="alert-dialog-title"] {
font-family: var(--font-heading);
font-weight: 800;
letter-spacing: -0.02em;
color: var(--foreground);
}
h3, h4,
[data-slot="card-title"] {
font-family: var(--font-heading);
font-weight: 700;
letter-spacing: -0.015em;
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 quiet chip; the coral hero stays
* reserved for actions, links and focus. */
:not(pre) > code {
padding: 0.1em 0.4em;
border-radius: calc(var(--radius) * 0.35);
background: color-mix(in oklch, var(--foreground) 7%, transparent);
color: var(--foreground);
font-size: 0.9em;
}
/* Selection — a scoop of coral. */
::selection {
background: color-mix(in oklch, var(--primary) 35%, transparent);
color: var(--foreground);
}
a {
color: var(--accent-foreground);
text-decoration-color: color-mix(in oklch, var(--primary) 55%, transparent);
text-underline-offset: 0.2em;
transition: text-decoration-color var(--duration-fast) var(--ease-standard);
}
a:hover {
text-decoration-color: var(--primary);
}
/* Cards — flat-ish with a crisp resting shadow; hover gets a springy
* micro-lift rather than a mist cloud. */
[data-slot="card"] {
background: var(--card);
border: var(--border-width-base) solid var(--border);
border-radius: var(--radius);
box-shadow: var(--elevation-1);
transition:
box-shadow var(--duration-fast) var(--ease-standard),
transform var(--duration-spring) var(--ease-spring-snappy);
}
[data-slot="card"]:hover {
box-shadow: var(--elevation-2);
}
/* Overlays — altitude via the elevation ladder, corners stay chunky. */
[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"] {
border: var(--border-width-base) solid var(--border);
border-radius: calc(var(--radius) * 0.8);
box-shadow: var(--elevation-3);
}
[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-4);
}
[data-slot="sidebar"] {
background: var(--sidebar);
border-right: var(--border-width-base) solid var(--sidebar-border);
}
}
/*
* Unlayered interaction feel — outside @layer base so it beats Tailwind's
* utilities. The signature Sorbet move: buttons squash on press and spring
* back, chips and toggles settle with a bounce. Fast, physical, never slidey.
*/
[data-slot="button"] {
font-weight: 600;
transition:
transform var(--duration-spring) var(--ease-spring-snappy),
opacity var(--duration-fast) var(--ease-standard),
background-color var(--duration-fast) var(--ease-standard);
}
[data-slot="button"]:hover:not(:disabled) {
opacity: 0.92;
}
[data-slot="button"]:active:not(:disabled) {
transform: scale(0.96);
transition-duration: var(--duration-fast);
transition-timing-function: var(--ease-standard);
}
[data-slot="button"]:focus-visible {
outline: var(--border-width-thick) solid var(--primary);
outline-offset: 2px;
box-shadow: none;
}
[data-slot="chip"],
[data-slot="switch"],
[data-slot="checkbox"] {
transition: transform var(--duration-spring) var(--ease-spring-bouncy);
}
[data-slot="chip"]:active:not(:disabled) {
transform: scale(0.94);
}
/* Inputs — chunky corners at a calmer sub-radius, coral focus. */
[data-slot="input"],
[data-slot="textarea"],
[data-slot="select-trigger"] {
border-radius: calc(var(--radius) * 0.6);
background: var(--card);
border: var(--border-width-base) solid var(--input);
transition:
border-color var(--duration-fast) var(--ease-standard),
box-shadow var(--duration-fast) var(--ease-standard);
}
[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 3px color-mix(in oklch, var(--primary) 25%, transparent);
outline: none;
}
@media (prefers-reduced-motion: reduce) {
[data-slot="button"],
[data-slot="chip"],
[data-slot="switch"],
[data-slot="checkbox"],
[data-slot="card"] {
transition-duration: 0.01ms;
}
[data-slot="button"]:active:not(:disabled),
[data-slot="chip"]:active:not(:disabled) {
transform: none;
}
}
/* 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;
}
}