From e9bb2a54444c56f4a0e81d02173aeb9fb32e3008 Mon Sep 17 00:00:00 2001 From: ginnoir Date: Mon, 1 Jun 2026 18:25:58 -0500 Subject: [PATCH] feat: expand theme system to 10 full-skin palettes with light and dark modes --- src/app/globals.css | 181 ++++++++++++++++++++++++++++++-- src/components/theme-picker.tsx | 55 ++++++---- src/modules/_core/themes.ts | 29 +++-- 3 files changed, 230 insertions(+), 35 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 20b1b8a..556afdf 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -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 { * { diff --git a/src/components/theme-picker.tsx b/src/components/theme-picker.tsx index eda6f39..1f22ec8 100644 --- a/src/components/theme-picker.tsx +++ b/src/components/theme-picker.tsx @@ -67,28 +67,39 @@ export function ThemePicker({ return (
- -
- {PALETTES.map((p) => ( -
+ +
diff --git a/src/modules/_core/themes.ts b/src/modules/_core/themes.ts index 97ebc1f..ea361a9 100644 --- a/src/modules/_core/themes.ts +++ b/src/modules/_core/themes.ts @@ -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 }> = [