feat: expand theme system to 10 full-skin palettes with light and dark modes
This commit is contained in:
+175
-6
@@ -205,33 +205,117 @@
|
||||
--sans: var(--sans-inter, "Inter", system-ui, sans-serif);
|
||||
}
|
||||
|
||||
/* ── Palettes — the only thing that varies is the accent + soft +
|
||||
household-calendar color. Everything else stays paper-and-ink. */
|
||||
/* ── Palettes — each theme sets surfaces, accent, and household color. */
|
||||
[data-theme="clay"] {
|
||||
--paper: #fbf9f4;
|
||||
--paper-2: #f4f0e8;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(90, 40, 20, 0.09);
|
||||
--hair-2: rgba(90, 40, 20, 0.16);
|
||||
--shade: rgba(90, 40, 20, 0.04);
|
||||
--accent: #b85c3c;
|
||||
--accent-soft: #ebd8ce;
|
||||
--c-household: #b85c3c;
|
||||
}
|
||||
[data-theme="indigo"] {
|
||||
--paper: #f4f7fb;
|
||||
--paper-2: #eaeff7;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(22, 40, 74, 0.09);
|
||||
--hair-2: rgba(22, 40, 74, 0.16);
|
||||
--shade: rgba(22, 40, 74, 0.04);
|
||||
--accent: #3e5b8a;
|
||||
--accent-soft: #d5dde8;
|
||||
--c-household: #3e5b8a;
|
||||
}
|
||||
[data-theme="sage"] {
|
||||
--paper: #f5f8f2;
|
||||
--paper-2: #eaf0e4;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(30, 55, 22, 0.09);
|
||||
--hair-2: rgba(30, 55, 22, 0.16);
|
||||
--shade: rgba(30, 55, 22, 0.04);
|
||||
--accent: #6f8b5e;
|
||||
--accent-soft: #dbe3d2;
|
||||
--c-household: #6f8b5e;
|
||||
}
|
||||
[data-theme="plum"] {
|
||||
--paper: #faf5fb;
|
||||
--paper-2: #f1e8f2;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(60, 25, 65, 0.09);
|
||||
--hair-2: rgba(60, 25, 65, 0.16);
|
||||
--shade: rgba(60, 25, 65, 0.04);
|
||||
--accent: #7b4f6e;
|
||||
--accent-soft: #e2d5dd;
|
||||
--c-household: #7b4f6e;
|
||||
}
|
||||
[data-theme="ink"] {
|
||||
--paper: #f8f7f5;
|
||||
--paper-2: #edecea;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(31, 27, 22, 0.09);
|
||||
--hair-2: rgba(31, 27, 22, 0.16);
|
||||
--shade: rgba(31, 27, 22, 0.04);
|
||||
--accent: #1f1b16;
|
||||
--accent-soft: #e8e4dc;
|
||||
--c-household: #5c5145;
|
||||
}
|
||||
[data-theme="rose"] {
|
||||
--paper: #fdf5f7;
|
||||
--paper-2: #f7e8ec;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(100, 20, 40, 0.09);
|
||||
--hair-2: rgba(100, 20, 40, 0.16);
|
||||
--shade: rgba(100, 20, 40, 0.04);
|
||||
--accent: #b5485e;
|
||||
--accent-soft: #f2d4da;
|
||||
--c-household: #b5485e;
|
||||
}
|
||||
[data-theme="amber"] {
|
||||
--paper: #fdf9f0;
|
||||
--paper-2: #f5eed8;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(90, 60, 5, 0.09);
|
||||
--hair-2: rgba(90, 60, 5, 0.16);
|
||||
--shade: rgba(90, 60, 5, 0.04);
|
||||
--accent: #b07d1e;
|
||||
--accent-soft: #f0e2b8;
|
||||
--c-household: #b07d1e;
|
||||
}
|
||||
[data-theme="ocean"] {
|
||||
--paper: #f2f9fb;
|
||||
--paper-2: #e4f2f5;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(10, 65, 80, 0.09);
|
||||
--hair-2: rgba(10, 65, 80, 0.16);
|
||||
--shade: rgba(10, 65, 80, 0.04);
|
||||
--accent: #2e7a8a;
|
||||
--accent-soft: #cce8ed;
|
||||
--c-household: #2e7a8a;
|
||||
}
|
||||
[data-theme="slate"] {
|
||||
--paper: #f4f6f9;
|
||||
--paper-2: #e8ecf0;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(20, 40, 60, 0.09);
|
||||
--hair-2: rgba(20, 40, 60, 0.16);
|
||||
--shade: rgba(20, 40, 60, 0.04);
|
||||
--accent: #4a657a;
|
||||
--accent-soft: #d0dae2;
|
||||
--c-household: #4a657a;
|
||||
}
|
||||
[data-theme="forest"] {
|
||||
--paper: #f2f7f3;
|
||||
--paper-2: #e4eee6;
|
||||
--card: #ffffff;
|
||||
--hair: rgba(15, 55, 25, 0.09);
|
||||
--hair-2: rgba(15, 55, 25, 0.16);
|
||||
--shade: rgba(15, 55, 25, 0.04);
|
||||
--accent: #3a6645;
|
||||
--accent-soft: #c8dece;
|
||||
--c-household: #3a6645;
|
||||
}
|
||||
|
||||
/* ── Dark variant — every palette switches to deep ink surfaces.
|
||||
Functional accents lift slightly so they stay readable. ───── */
|
||||
@@ -277,7 +361,7 @@
|
||||
--border: var(--hair);
|
||||
--input: var(--hair-2);
|
||||
--ring: var(--ink-soft);
|
||||
--sidebar: #1a1712;
|
||||
--sidebar: var(--paper-2);
|
||||
--sidebar-foreground: var(--ink-2);
|
||||
--sidebar-primary: var(--ink);
|
||||
--sidebar-primary-foreground: var(--paper);
|
||||
@@ -288,30 +372,115 @@
|
||||
}
|
||||
|
||||
[data-theme="clay"].dark {
|
||||
--paper: #16140f;
|
||||
--paper-2: #1e1b16;
|
||||
--card: #24201a;
|
||||
--hair: rgba(244, 210, 180, 0.08);
|
||||
--hair-2: rgba(244, 210, 180, 0.16);
|
||||
--shade: rgba(244, 210, 180, 0.04);
|
||||
--accent: #d88468;
|
||||
--accent-soft: #3a2e26;
|
||||
--c-household: #d88468;
|
||||
}
|
||||
[data-theme="indigo"].dark {
|
||||
--paper: #0f1319;
|
||||
--paper-2: #15202e;
|
||||
--card: #1c2a3a;
|
||||
--hair: rgba(184, 212, 244, 0.08);
|
||||
--hair-2: rgba(184, 212, 244, 0.16);
|
||||
--shade: rgba(184, 212, 244, 0.04);
|
||||
--accent: #7e9dd0;
|
||||
--accent-soft: #232c3c;
|
||||
--accent-soft: #1a2540;
|
||||
--c-household: #7e9dd0;
|
||||
}
|
||||
[data-theme="sage"].dark {
|
||||
--paper: #0f1710;
|
||||
--paper-2: #162018;
|
||||
--card: #1c2a1e;
|
||||
--hair: rgba(196, 228, 186, 0.08);
|
||||
--hair-2: rgba(196, 228, 186, 0.16);
|
||||
--shade: rgba(196, 228, 186, 0.04);
|
||||
--accent: #a0bc8e;
|
||||
--accent-soft: #25302a;
|
||||
--accent-soft: #1e2e1c;
|
||||
--c-household: #a0bc8e;
|
||||
}
|
||||
[data-theme="plum"].dark {
|
||||
--paper: #160f1a;
|
||||
--paper-2: #1e1522;
|
||||
--card: #261a2e;
|
||||
--hair: rgba(230, 180, 240, 0.08);
|
||||
--hair-2: rgba(230, 180, 240, 0.16);
|
||||
--shade: rgba(230, 180, 240, 0.04);
|
||||
--accent: #be8bad;
|
||||
--accent-soft: #321f30;
|
||||
--accent-soft: #2a1630;
|
||||
--c-household: #be8bad;
|
||||
}
|
||||
[data-theme="ink"].dark {
|
||||
--paper: #0e0e0e;
|
||||
--paper-2: #181818;
|
||||
--card: #222222;
|
||||
--hair: rgba(240, 240, 240, 0.08);
|
||||
--hair-2: rgba(240, 240, 240, 0.16);
|
||||
--shade: rgba(240, 240, 240, 0.04);
|
||||
--accent: #f4efe5;
|
||||
--accent-soft: #2a2620;
|
||||
--c-household: #c4bbaa;
|
||||
}
|
||||
[data-theme="rose"].dark {
|
||||
--paper: #1a0e10;
|
||||
--paper-2: #221418;
|
||||
--card: #2c1a1e;
|
||||
--hair: rgba(248, 188, 200, 0.08);
|
||||
--hair-2: rgba(248, 188, 200, 0.16);
|
||||
--shade: rgba(248, 188, 200, 0.04);
|
||||
--accent: #d87f94;
|
||||
--accent-soft: #36181e;
|
||||
--c-household: #d87f94;
|
||||
}
|
||||
[data-theme="amber"].dark {
|
||||
--paper: #18130a;
|
||||
--paper-2: #201a0e;
|
||||
--card: #2a2214;
|
||||
--hair: rgba(240, 210, 140, 0.08);
|
||||
--hair-2: rgba(240, 210, 140, 0.16);
|
||||
--shade: rgba(240, 210, 140, 0.04);
|
||||
--accent: #d4a84a;
|
||||
--accent-soft: #342410;
|
||||
--c-household: #d4a84a;
|
||||
}
|
||||
[data-theme="ocean"].dark {
|
||||
--paper: #091518;
|
||||
--paper-2: #0f2028;
|
||||
--card: #142c38;
|
||||
--hair: rgba(170, 230, 240, 0.08);
|
||||
--hair-2: rgba(170, 230, 240, 0.16);
|
||||
--shade: rgba(170, 230, 240, 0.04);
|
||||
--accent: #5ab8c8;
|
||||
--accent-soft: #0f2c36;
|
||||
--c-household: #5ab8c8;
|
||||
}
|
||||
[data-theme="slate"].dark {
|
||||
--paper: #0e1420;
|
||||
--paper-2: #151e2e;
|
||||
--card: #1c2840;
|
||||
--hair: rgba(160, 190, 218, 0.08);
|
||||
--hair-2: rgba(160, 190, 218, 0.16);
|
||||
--shade: rgba(160, 190, 218, 0.04);
|
||||
--accent: #7ea8c4;
|
||||
--accent-soft: #182032;
|
||||
--c-household: #7ea8c4;
|
||||
}
|
||||
[data-theme="forest"].dark {
|
||||
--paper: #0c1710;
|
||||
--paper-2: #122018;
|
||||
--card: #162c20;
|
||||
--hair: rgba(152, 210, 170, 0.08);
|
||||
--hair-2: rgba(152, 210, 170, 0.16);
|
||||
--shade: rgba(152, 210, 170, 0.04);
|
||||
--accent: #74b888;
|
||||
--accent-soft: #142618;
|
||||
--c-household: #74b888;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
|
||||
@@ -67,28 +67,39 @@ export function ThemePicker({
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<div className="space-y-1.5">
|
||||
<Label>Palette</Label>
|
||||
<div className="flex gap-2">
|
||||
{PALETTES.map((p) => (
|
||||
<button
|
||||
key={p.id}
|
||||
type="button"
|
||||
onClick={() => t.setPalette(p.id)}
|
||||
aria-pressed={t.palette === p.id}
|
||||
title={p.label}
|
||||
className={cn(
|
||||
"size-7 rounded-md border-[0.5px] cursor-pointer transition-transform",
|
||||
t.palette === p.id ? "scale-110" : "hover:scale-105",
|
||||
)}
|
||||
style={{
|
||||
background: p.hex,
|
||||
borderColor: "var(--hair-2)",
|
||||
outline: t.palette === p.id ? "2px solid var(--ink)" : "none",
|
||||
outlineOffset: 2,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<Label>Theme</Label>
|
||||
<Select value={t.palette} onValueChange={(v) => t.setPalette(v as Palette)}>
|
||||
<SelectTrigger className="w-56">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<span
|
||||
className="inline-block w-5 h-4 rounded-sm shrink-0 overflow-hidden border-[0.5px]"
|
||||
style={{
|
||||
background: `linear-gradient(to bottom, ${
|
||||
PALETTES.find((p) => p.id === t.palette)?.paper ?? "#fff"
|
||||
} 55%, ${PALETTES.find((p) => p.id === t.palette)?.hex ?? "#888"} 55%)`,
|
||||
borderColor: "var(--hair-2)",
|
||||
}}
|
||||
/>
|
||||
<SelectValue />
|
||||
</div>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{PALETTES.map((p) => (
|
||||
<SelectItem key={p.id} value={p.id}>
|
||||
<div className="flex items-center justify-between gap-6 w-40">
|
||||
<span>{p.label}</span>
|
||||
<span
|
||||
className="inline-block w-5 h-4 rounded-sm shrink-0 overflow-hidden border-[0.5px]"
|
||||
style={{
|
||||
background: `linear-gradient(to bottom, ${p.paper} 55%, ${p.hex} 55%)`,
|
||||
borderColor: "var(--hair-2)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
|
||||
@@ -1,18 +1,33 @@
|
||||
export type ThemeMode = "light" | "dark" | "system";
|
||||
|
||||
export type Palette = "clay" | "indigo" | "sage" | "plum" | "ink";
|
||||
export type Palette =
|
||||
| "clay"
|
||||
| "indigo"
|
||||
| "sage"
|
||||
| "plum"
|
||||
| "ink"
|
||||
| "rose"
|
||||
| "amber"
|
||||
| "ocean"
|
||||
| "slate"
|
||||
| "forest";
|
||||
export type FontPair = "serif-sans" | "newsreader" | "fraunces" | "sans-only";
|
||||
export type Density = "compact" | "regular" | "comfy";
|
||||
export type DashLayout = "classic" | "split" | "glance";
|
||||
export type CalView = "month" | "week" | "day";
|
||||
export type NavStyle = "rail-desktop" | "compact-rail" | "top-nav" | "fab-only";
|
||||
|
||||
export const PALETTES: ReadonlyArray<{ id: Palette; label: string; hex: string }> = [
|
||||
{ id: "clay", label: "Clay", hex: "#B85C3C" },
|
||||
{ id: "indigo", label: "Indigo ink", hex: "#3E5B8A" },
|
||||
{ id: "sage", label: "Sage", hex: "#6F8B5E" },
|
||||
{ id: "plum", label: "Plum", hex: "#7B4F6E" },
|
||||
{ id: "ink", label: "Ink (mono)", hex: "#1F1B16" },
|
||||
export const PALETTES: ReadonlyArray<{ id: Palette; label: string; hex: string; paper: string }> = [
|
||||
{ id: "clay", label: "Clay", hex: "#B85C3C", paper: "#fbf9f4" },
|
||||
{ id: "indigo", label: "Indigo", hex: "#3E5B8A", paper: "#f4f7fb" },
|
||||
{ id: "sage", label: "Sage", hex: "#6F8B5E", paper: "#f5f8f2" },
|
||||
{ id: "plum", label: "Plum", hex: "#7B4F6E", paper: "#faf5fb" },
|
||||
{ id: "ink", label: "Ink", hex: "#1F1B16", paper: "#f8f7f5" },
|
||||
{ id: "rose", label: "Rose", hex: "#B5485E", paper: "#fdf5f7" },
|
||||
{ id: "amber", label: "Amber", hex: "#B07D1E", paper: "#fdf9f0" },
|
||||
{ id: "ocean", label: "Ocean", hex: "#2E7A8A", paper: "#f2f9fb" },
|
||||
{ id: "slate", label: "Slate", hex: "#4A657A", paper: "#f4f6f9" },
|
||||
{ id: "forest", label: "Forest", hex: "#3A6645", paper: "#f2f7f3" },
|
||||
];
|
||||
|
||||
export const FONT_PAIRS: ReadonlyArray<{ id: FontPair; label: string }> = [
|
||||
|
||||
Reference in New Issue
Block a user