From bb4e8b55582706c5e888820e9d7dacb4fbeb6122 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Fri, 28 Aug 2026 14:50:06 -0600 Subject: [PATCH] Updated Theme Colors --- components/board/group-card.tsx | 13 +++-- components/theme/use-dark-theme.ts | 17 ++++++ lib/colors.ts | 94 ++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 6 deletions(-) diff --git a/components/board/group-card.tsx b/components/board/group-card.tsx index 385a397..0a97b67 100644 --- a/components/board/group-card.tsx +++ b/components/board/group-card.tsx @@ -28,9 +28,8 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { getBrighterColor, getComplementaryColor, getGroupColor } from "@/lib/colors"; -import { isDarkTheme, isCyberpunkTheme } from "@/components/theme/use-dark-theme"; -import { getCyberpunkGroupColor } from "@/lib/colors"; +import { getBrighterColor, getComplementaryColor } from "@/lib/colors"; +import { isDarkTheme, useGroupColor } from "@/components/theme/use-dark-theme"; import { useBoard } from "@/components/board/board-context"; import { useBoardView } from "@/components/board/board-view-provider"; import { useHoldOnComplete } from "@/components/hold-on-complete"; @@ -122,9 +121,11 @@ export function GroupCard({ group }: { group: GroupDTO }) { // card ever ringing itself while it's the one being moved.) const isDropTarget = isOver && !isDragging; - const color = isCyberpunkTheme() - ? getCyberpunkGroupColor(group.color) - : getGroupColor(group.color); + // The group's color for the ACTIVE theme: themes with their own palette + // (cyberpunk, blueprint, vaporwave, notebook, starfield) retint the card, + // every other theme keeps the base GROUP_COLORS. Falls back to the base + // color pre-mount so the server render matches. + const color = useGroupColor(group.color); const borderColor = isDark ? color.dark : color.light; // The card fill is a calm, hand-picked tint of the same stroke color // (`soft` / `softDark` in lib/colors.ts), blended a little into the theme's diff --git a/components/theme/use-dark-theme.ts b/components/theme/use-dark-theme.ts index 42b7347..864134d 100644 --- a/components/theme/use-dark-theme.ts +++ b/components/theme/use-dark-theme.ts @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; import { useTheme, resolveTheme, DARK_SURFACES } from "./theme-provider"; +import { getGroupColor, getThemedGroupColor, type GroupColor } from "@/lib/colors"; // The themes whose *surfaces* are dark (see app/globals.css) come from the // provider's single source of truth -- currently Dark, Ocean, Pine, Plum, @@ -48,6 +49,22 @@ export function isCyberpunkTheme(): boolean { return resolveTheme(theme) === "cyberpunk"; } +/** + * The group color for the active theme. Themes with their own palette + * (cyberpunk, blueprint, vaporwave, notebook, starfield -- see + * lib/colors.ts) get theirs; everything else gets the base color. Same + * contract as isDarkTheme: reactive to theme switches (derived from the + * provider's state, so a setTheme re-renders every consumer in the same + * commit) and hydration-safe (until mounted we report the base color, + * matching the server render). + */ +export function useGroupColor(key: string): GroupColor { + const mounted = useMounted(); + const { theme } = useTheme(); + if (!mounted) return getGroupColor(key); + return getThemedGroupColor(resolveTheme(theme), key); +} + /** * The `data-color-mode` value the markdown editor/preview widgets expect. * They only understand light/dark, so the light looks (default, sunset, diff --git a/lib/colors.ts b/lib/colors.ts index be3b036..b17d786 100644 --- a/lib/colors.ts +++ b/lib/colors.ts @@ -66,6 +66,80 @@ const CYBERPUNK_PALETTE: Record = { pink: { key: "pink", label: "Deep Violet", light: "#8a2be2", dark: "#8a2be2", soft: "#e8d6ff", softDark: "#261236" }, }; +// --- Blueprint drafting-ink palette --- +// Used ONLY when the "blueprint" theme is active. The cards are white +// drafting paper on a cobalt board, so the strokes read as technical pen +// inks -- cobalt and ultramarine for the primary lines, with the classic +// annotation inks (proof red, sepia, green, cerulean) for the rest. Calm, +// even-saturation inks, not neon: this is a drafting table, not a nightclub. + +const BLUEPRINT_PALETTE: Record = { + coral: { key: "coral", label: "Proof Red", light: "#b83a4a", dark: "#b83a4a", soft: "#fbe5e7", softDark: "#3a2320" }, + amber: { key: "amber", label: "Sepia Ink", light: "#a06325", dark: "#a06325", soft: "#f5ecda", softDark: "#3a301c" }, + lime: { key: "lime", label: "Draft Green", light: "#3d7a46", dark: "#3d7a46", soft: "#e3efe1", softDark: "#25331d" }, + teal: { key: "teal", label: "Cerulean Ink", light: "#1d6f74", dark: "#1d6f74", soft: "#ddefee", softDark: "#16332f" }, + sky: { key: "sky", label: "Cobalt Ink", light: "#2456b4", dark: "#2456b4", soft: "#e0e9f8", softDark: "#1a2b40" }, + indigo: { key: "indigo", label: "Ultramarine", light: "#43489f", dark: "#43489f", soft: "#e4e5f5", softDark: "#23233c" }, + violet: { key: "violet", label: "Violet Ink", light: "#6d43a8", dark: "#6d43a8", soft: "#ece2f7", softDark: "#2e2136" }, + pink: { key: "pink", label: "Magenta Ink", light: "#b03586", dark: "#b03586", soft: "#f7e1ef", softDark: "#37202e" }, +}; + +// --- Vaporwave retro palette --- +// Used ONLY when the "vaporwave" theme is active. Pastel synthwave sky with +// a glossy 90s-desktop gloss, so the strokes are soft retro-neon rather than +// full cyberpunk brightness: Miami pink, sunset gold, CRT mint, electric +// aqua, chrome blue, laser purple. Saturated enough to read on the pale +// card, muted enough to sit in the pastel sky. + +const VAPORWAVE_PALETTE: Record = { + coral: { key: "coral", label: "Miami Pink", light: "#e64f9e", dark: "#e64f9e", soft: "#fbe0ef", softDark: "#3d1433" }, + amber: { key: "amber", label: "Sunset Gold", light: "#e08a1f", dark: "#e08a1f", soft: "#fbeed6", softDark: "#3a301c" }, + lime: { key: "lime", label: "CRT Mint", light: "#2fb584", dark: "#2fb584", soft: "#dcf3e9", softDark: "#163d14" }, + teal: { key: "teal", label: "Electric Aqua", light: "#14a9c4", dark: "#14a9c4", soft: "#daf0f5", softDark: "#0f3d3a" }, + sky: { key: "sky", label: "Chrome Blue", light: "#3f6fe0", dark: "#3f6fe0", soft: "#e0e8fb", softDark: "#1a2b40" }, + indigo: { key: "indigo", label: "Laser Purple", light: "#7c4fe0", dark: "#7c4fe0", soft: "#e8e1fa", softDark: "#23233c" }, + violet: { key: "violet", label: "Vapor Magenta", light: "#b455d4", dark: "#b455d4", soft: "#f3e2fa", softDark: "#2e2136" }, + pink: { key: "pink", label: "Pastel Orchid", light: "#cb6ca8", dark: "#cb6ca8", soft: "#f9e4f1", softDark: "#37202e" }, +}; + +// --- Notebook stationery palette --- +// Used ONLY when the "notebook" theme is active. The cards are index cards +// on a warm desk, so the strokes read like the pens and markers in the +// desk drawer: blue ballpoint, red pencil, green pen, teal marker, purple +// pen, rose highlighter. Muted, papery, hand-inked -- the opposite of +// glowing; these are inks that dry on paper. + +const NOTEBOOK_PALETTE: Record = { + coral: { key: "coral", label: "Red Pencil", light: "#c04a45", dark: "#c04a45", soft: "#fae6e2", softDark: "#3a2320" }, + amber: { key: "amber", label: "Orange Marker", light: "#d9822b", dark: "#d9822b", soft: "#f9ecd7", softDark: "#3a301c" }, + lime: { key: "lime", label: "Green Pen", light: "#46905b", dark: "#46905b", soft: "#e3f0e6", softDark: "#25331d" }, + teal: { key: "teal", label: "Teal Marker", light: "#2a8f8b", dark: "#2a8f8b", soft: "#dcefec", softDark: "#16332f" }, + sky: { key: "sky", label: "Blue Ballpoint", light: "#2f5cb8", dark: "#2f5cb8", soft: "#e2e9fa", softDark: "#1a2b40" }, + indigo: { key: "indigo", label: "Purple Pen", light: "#5b4bb0", dark: "#5b4bb0", soft: "#e6e3f6", softDark: "#23233c" }, + violet: { key: "violet", label: "Violet Ink", light: "#8552b8", dark: "#8552b8", soft: "#ede4f7", softDark: "#2e2136" }, + pink: { key: "pink", label: "Rose Highlighter", light: "#c25a93", dark: "#c25a93", soft: "#f8e5f0", softDark: "#37202e" }, +}; + +// --- Starfield starlight palette --- +// Used ONLY when the "starfield" theme is active. The cards float in a +// near-black indigo void, so the strokes read as starlight: bright but +// soft, like distant stars and console glows -- star gold, aurora, ion +// cyan, nebula violet. Deliberately gentler than cyberpunk's neon; these +// are lights seen across a galaxy, not tubes in a night city. softDark +// fills are low-light tints so they read as faint nebulae against the +// void instead of washed-out pastels. + +const STARFIELD_PALETTE: Record = { + coral: { key: "coral", label: "Red Giant", light: "#ff8592", dark: "#ff8592", soft: "#fadfee", softDark: "#3a2026" }, + amber: { key: "amber", label: "Star Gold", light: "#ffc86b", dark: "#ffc86b", soft: "#eeffe0", softDark: "#3a3018" }, + lime: { key: "lime", label: "Aurora", light: "#5fd6a0", dark: "#5fd6a0", soft: "#e0fde0", softDark: "#173a2b" }, + teal: { key: "teal", label: "Ion Cyan", light: "#55c8f0", dark: "#55c8f0", soft: "#dffffa", softDark: "#14303d" }, + sky: { key: "sky", label: "Polar Blue", light: "#7b9df5", dark: "#7b9df5", soft: "#e0f0ff", softDark: "#1b2542" }, + indigo: { key: "indigo", label: "Nebula Violet", light: "#a48bfa", dark: "#a48bfa", soft: "#f0e0ff", softDark: "#271f42" }, + violet: { key: "violet", label: "Nebula Pink", light: "#d18ff0", dark: "#d18ff0", soft: "#ffe0f5", softDark: "#33203a" }, + pink: { key: "pink", label: "Rose Star", light: "#f591bb", dark: "#f591bb", soft: "#e8d6ff", softDark: "#3a2230" }, +}; + const GROUP_COLOR_MAP: Record = Object.fromEntries( GROUP_COLORS.map((c) => [c.key, c]) ); @@ -83,6 +157,26 @@ export function getCyberpunkGroupColor(key: string): GroupColor { return CYBERPUNK_PALETTE[key] ?? CYBERPUNK_PALETTE["coral"]; } +// Every theme-specific palette in one place, keyed by theme name (the +// values in components/theme/theme-provider's THEMES). Themes without an +// entry keep the base GROUP_COLORS above. +const THEME_PALETTES: Partial>> = { + cyberpunk: CYBERPUNK_PALETTE, + blueprint: BLUEPRINT_PALETTE, + vaporwave: VAPORWAVE_PALETTE, + notebook: NOTEBOOK_PALETTE, + starfield: STARFIELD_PALETTE, +}; + +/** The group color for a given theme: that theme's palette if it has one + * (cyberpunk, blueprint, vaporwave, notebook, starfield), otherwise the + * base GROUP_COLORS entry. Used by the themed group cards so each + * character's board carries the theme's own ink. + */ +export function getThemedGroupColor(theme: string, key: string): GroupColor { + return THEME_PALETTES[theme]?.[key] ?? GROUP_COLOR_MAP[key] ?? GROUP_COLORS[0]; +} + // --- Complementary accent (for the to-do progress pie on a Group card) --- // // Derived at runtime by rotating a base color's hue 180° and pulling the