theme update
This commit is contained in:
+144
@@ -89,6 +89,78 @@
|
||||
--reading-text-align: start;
|
||||
}
|
||||
|
||||
/* ── Light palette — "Tulasī Garden" ──
|
||||
A fresh, distinctly GREEN palette — noticeably green background
|
||||
(not just a green-tinted white), with deep forest text and primary. */
|
||||
:root:not(.dark)[data-light-palette="tulasi-garden"] {
|
||||
--background: oklch(0.92 0.07 152);
|
||||
--foreground: oklch(0.22 0.09 155);
|
||||
--card: oklch(0.95 0.055 150);
|
||||
--card-foreground: oklch(0.22 0.09 155);
|
||||
--popover: oklch(0.95 0.055 150);
|
||||
--popover-foreground: oklch(0.22 0.09 155);
|
||||
--primary: oklch(0.45 0.19 150);
|
||||
--primary-foreground: oklch(0.97 0.04 150);
|
||||
--secondary: oklch(0.85 0.1 152);
|
||||
--secondary-foreground: oklch(0.22 0.09 155);
|
||||
--muted: oklch(0.87 0.09 152);
|
||||
--muted-foreground: oklch(0.42 0.11 150);
|
||||
--accent: oklch(0.87 0.09 152);
|
||||
--accent-foreground: oklch(0.22 0.09 155);
|
||||
--border: oklch(0.76 0.12 150);
|
||||
--input: oklch(0.76 0.12 150);
|
||||
--ring: oklch(0.56 0.17 150);
|
||||
--chart-1: oklch(0.45 0.19 150);
|
||||
--chart-2: oklch(0.42 0.11 150);
|
||||
--chart-3: oklch(0.56 0.17 150);
|
||||
--chart-4: oklch(0.34 0.12 145);
|
||||
--chart-5: oklch(0.22 0.09 155);
|
||||
--sidebar: oklch(0.95 0.055 150);
|
||||
--sidebar-foreground: oklch(0.22 0.09 155);
|
||||
--sidebar-primary: oklch(0.45 0.19 150);
|
||||
--sidebar-primary-foreground: oklch(0.97 0.04 150);
|
||||
--sidebar-accent: oklch(0.87 0.09 152);
|
||||
--sidebar-accent-foreground: oklch(0.22 0.09 155);
|
||||
--sidebar-border: oklch(0.76 0.12 150);
|
||||
--sidebar-ring: oklch(0.56 0.17 150);
|
||||
}
|
||||
|
||||
/* ── Light palette — "Yamunā Mist" ──
|
||||
A distinctly BLUE palette inspired by the sacred Yamunā river —
|
||||
noticeably blue background (not just a blue-tinted white). */
|
||||
:root:not(.dark)[data-light-palette="yamuna-mist"] {
|
||||
--background: oklch(0.92 0.05 230);
|
||||
--foreground: oklch(0.24 0.09 240);
|
||||
--card: oklch(0.95 0.04 230);
|
||||
--card-foreground: oklch(0.24 0.09 240);
|
||||
--popover: oklch(0.95 0.04 230);
|
||||
--popover-foreground: oklch(0.24 0.09 240);
|
||||
--primary: oklch(0.52 0.2 240);
|
||||
--primary-foreground: oklch(0.97 0.03 230);
|
||||
--secondary: oklch(0.85 0.08 232);
|
||||
--secondary-foreground: oklch(0.24 0.09 240);
|
||||
--muted: oklch(0.87 0.07 232);
|
||||
--muted-foreground: oklch(0.44 0.11 235);
|
||||
--accent: oklch(0.87 0.07 232);
|
||||
--accent-foreground: oklch(0.24 0.09 240);
|
||||
--border: oklch(0.76 0.1 232);
|
||||
--input: oklch(0.76 0.1 232);
|
||||
--ring: oklch(0.62 0.15 235);
|
||||
--chart-1: oklch(0.52 0.2 240);
|
||||
--chart-2: oklch(0.44 0.11 235);
|
||||
--chart-3: oklch(0.62 0.15 235);
|
||||
--chart-4: oklch(0.38 0.13 240);
|
||||
--chart-5: oklch(0.24 0.09 240);
|
||||
--sidebar: oklch(0.95 0.04 230);
|
||||
--sidebar-foreground: oklch(0.24 0.09 240);
|
||||
--sidebar-primary: oklch(0.52 0.2 240);
|
||||
--sidebar-primary-foreground: oklch(0.97 0.03 230);
|
||||
--sidebar-accent: oklch(0.87 0.07 232);
|
||||
--sidebar-accent-foreground: oklch(0.24 0.09 240);
|
||||
--sidebar-border: oklch(0.76 0.1 232);
|
||||
--sidebar-ring: oklch(0.62 0.15 235);
|
||||
}
|
||||
|
||||
/* ── Dark mode — "Ancient Manuscript" ── */
|
||||
.dark {
|
||||
--background: oklch(0.197 0.013 55.5);
|
||||
@@ -124,6 +196,78 @@
|
||||
--sidebar-ring: oklch(0.636 0.08 86.9);
|
||||
}
|
||||
|
||||
/* ── Dark palette — "Nīlamaṇi" ──
|
||||
"Blue jewel" — Kṛṣṇa's sapphire complexion. Deep navy with
|
||||
bright sky-blue accents — cool and distinctly BLUE. */
|
||||
.dark[data-dark-palette="nilamani"] {
|
||||
--background: oklch(0.16 0.05 255);
|
||||
--foreground: oklch(0.92 0.04 220);
|
||||
--card: oklch(0.21 0.06 252);
|
||||
--card-foreground: oklch(0.92 0.04 220);
|
||||
--popover: oklch(0.21 0.06 252);
|
||||
--popover-foreground: oklch(0.92 0.04 220);
|
||||
--primary: oklch(0.72 0.18 235);
|
||||
--primary-foreground: oklch(0.16 0.05 255);
|
||||
--secondary: oklch(0.28 0.06 248);
|
||||
--secondary-foreground: oklch(0.92 0.04 220);
|
||||
--muted: oklch(0.28 0.06 248);
|
||||
--muted-foreground: oklch(0.7 0.07 230);
|
||||
--accent: oklch(0.28 0.06 248);
|
||||
--accent-foreground: oklch(0.92 0.04 220);
|
||||
--border: oklch(0.7 0.12 235 / 22%);
|
||||
--input: oklch(0.7 0.12 235 / 26%);
|
||||
--ring: oklch(0.66 0.14 235);
|
||||
--chart-1: oklch(0.72 0.18 235);
|
||||
--chart-2: oklch(0.7 0.07 230);
|
||||
--chart-3: oklch(0.66 0.14 235);
|
||||
--chart-4: oklch(0.46 0.1 240);
|
||||
--chart-5: oklch(0.28 0.06 248);
|
||||
--sidebar: oklch(0.21 0.06 252);
|
||||
--sidebar-foreground: oklch(0.92 0.04 220);
|
||||
--sidebar-primary: oklch(0.72 0.18 235);
|
||||
--sidebar-primary-foreground: oklch(0.16 0.05 255);
|
||||
--sidebar-accent: oklch(0.28 0.06 248);
|
||||
--sidebar-accent-foreground: oklch(0.92 0.04 220);
|
||||
--sidebar-border: oklch(0.7 0.12 235 / 22%);
|
||||
--sidebar-ring: oklch(0.66 0.14 235);
|
||||
}
|
||||
|
||||
/* ── Dark palette — "Vraja Twilight" ──
|
||||
The crimson dusk over Vraja — deep wine background with warm
|
||||
rose-red accents. Distinctly RED/WARM, no overlap with the others. */
|
||||
.dark[data-dark-palette="vraja-twilight"] {
|
||||
--background: oklch(0.18 0.05 15);
|
||||
--foreground: oklch(0.92 0.03 25);
|
||||
--card: oklch(0.22 0.06 12);
|
||||
--card-foreground: oklch(0.92 0.03 25);
|
||||
--popover: oklch(0.22 0.06 12);
|
||||
--popover-foreground: oklch(0.92 0.03 25);
|
||||
--primary: oklch(0.72 0.18 25);
|
||||
--primary-foreground: oklch(0.18 0.05 15);
|
||||
--secondary: oklch(0.28 0.06 15);
|
||||
--secondary-foreground: oklch(0.92 0.03 25);
|
||||
--muted: oklch(0.28 0.06 15);
|
||||
--muted-foreground: oklch(0.7 0.07 22);
|
||||
--accent: oklch(0.28 0.06 15);
|
||||
--accent-foreground: oklch(0.92 0.03 25);
|
||||
--border: oklch(0.7 0.12 25 / 22%);
|
||||
--input: oklch(0.7 0.12 25 / 26%);
|
||||
--ring: oklch(0.66 0.14 25);
|
||||
--chart-1: oklch(0.72 0.18 25);
|
||||
--chart-2: oklch(0.7 0.07 22);
|
||||
--chart-3: oklch(0.66 0.14 25);
|
||||
--chart-4: oklch(0.46 0.1 20);
|
||||
--chart-5: oklch(0.28 0.06 15);
|
||||
--sidebar: oklch(0.22 0.06 12);
|
||||
--sidebar-foreground: oklch(0.92 0.03 25);
|
||||
--sidebar-primary: oklch(0.72 0.18 25);
|
||||
--sidebar-primary-foreground: oklch(0.18 0.05 15);
|
||||
--sidebar-accent: oklch(0.28 0.06 15);
|
||||
--sidebar-accent-foreground: oklch(0.92 0.03 25);
|
||||
--sidebar-border: oklch(0.7 0.12 25 / 22%);
|
||||
--sidebar-ring: oklch(0.66 0.14 25);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
|
||||
@@ -117,16 +117,16 @@ export default async function TranscriptionPage({
|
||||
<TranscriptionNav prev={prev} next={next} />
|
||||
|
||||
{/* Audio + Participants row */}
|
||||
<div className="w-full flex flex-col md:flex-row items-start md:items-end justify-between gap-4">
|
||||
<div className="w-full flex flex-col md:flex-row items-start md:items-start justify-between gap-4">
|
||||
{/* Participants */}
|
||||
{t.participants.length > 0 && (
|
||||
<>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex pl-2 items-center gap-2 flex-wrap text-sm text-muted-foreground">
|
||||
<div className="">
|
||||
<div className="flex flex-row pl-2 mb-2 items-center gap-2 text-sm text-muted-foreground">
|
||||
<Users className="size-3.5" />
|
||||
<span>Participants:</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 flex-wrap text-sm text-muted-foreground">
|
||||
<div className="max-w-full flex items-center gap-2 flex-wrap text-sm text-muted-foreground">
|
||||
{t.participants.map((p, i) => (
|
||||
<Badge
|
||||
key={i}
|
||||
@@ -143,7 +143,7 @@ export default async function TranscriptionPage({
|
||||
|
||||
{/* Play Audio button */}
|
||||
{t.audioUrl && (
|
||||
<div className="w-full md:w-auto flex justify-end">
|
||||
<div className="w-full md:w-[25%] flex justify-end">
|
||||
<PlayAudioButton
|
||||
audioUrl={t.audioUrl}
|
||||
title={t.name}
|
||||
|
||||
@@ -17,18 +17,88 @@ function useMounted() {
|
||||
);
|
||||
}
|
||||
|
||||
type LightPalette = "aged-parchment" | "tulasi-garden" | "yamuna-mist";
|
||||
type DarkPalette = "ancient-manuscript" | "nilamani" | "vraja-twilight";
|
||||
|
||||
type ReadingPrefs = {
|
||||
serifFont: boolean;
|
||||
fontSize: string;
|
||||
justified: boolean;
|
||||
lightPalette: LightPalette;
|
||||
darkPalette: DarkPalette;
|
||||
};
|
||||
|
||||
const DEFAULT_PREFS: ReadingPrefs = {
|
||||
serifFont: false,
|
||||
fontSize: "base",
|
||||
justified: false,
|
||||
lightPalette: "aged-parchment",
|
||||
darkPalette: "ancient-manuscript",
|
||||
};
|
||||
|
||||
// Preview swatches use approximations of each palette's background
|
||||
// and primary colours — the real colours come from the CSS variables.
|
||||
const LIGHT_PALETTES: {
|
||||
id: LightPalette;
|
||||
name: string;
|
||||
bg: string;
|
||||
primary: string;
|
||||
}[] = [
|
||||
// Warm cream + saffron — the original
|
||||
{
|
||||
id: "aged-parchment",
|
||||
name: "Aged Parchment",
|
||||
bg: "#f5ecd7",
|
||||
primary: "#b88940",
|
||||
},
|
||||
// Sage green background + deep emerald — distinctly GREEN
|
||||
{
|
||||
id: "tulasi-garden",
|
||||
name: "Tulasī Garden",
|
||||
bg: "#c4dcc1",
|
||||
primary: "#207537",
|
||||
},
|
||||
// Soft blue background + cerulean — distinctly BLUE
|
||||
{
|
||||
id: "yamuna-mist",
|
||||
name: "Yamunā Mist",
|
||||
bg: "#c2d7ec",
|
||||
primary: "#1f6fd6",
|
||||
},
|
||||
];
|
||||
|
||||
const DARK_PALETTES: {
|
||||
id: DarkPalette;
|
||||
name: string;
|
||||
bg: string;
|
||||
primary: string;
|
||||
}[] = [
|
||||
// Warm brown + saffron — the original
|
||||
{
|
||||
id: "ancient-manuscript",
|
||||
name: "Ancient Manuscript",
|
||||
bg: "#2a2519",
|
||||
primary: "#d0a04e",
|
||||
},
|
||||
// Deep navy + sky-blue — distinctly BLUE
|
||||
{
|
||||
id: "nilamani",
|
||||
name: "Nīlamaṇi",
|
||||
bg: "#101a36",
|
||||
primary: "#4a9fff",
|
||||
},
|
||||
// Deep wine + rose-red — distinctly RED/WARM
|
||||
{
|
||||
id: "vraja-twilight",
|
||||
name: "Vraja Twilight",
|
||||
bg: "#2b1217",
|
||||
primary: "#e07252",
|
||||
},
|
||||
];
|
||||
|
||||
const LIGHT_PALETTE_IDS = LIGHT_PALETTES.map((p) => p.id);
|
||||
const DARK_PALETTE_IDS = DARK_PALETTES.map((p) => p.id);
|
||||
|
||||
const FONT_SIZES = [
|
||||
{ value: "xs", label: "XS" },
|
||||
{ value: "sm", label: "S" },
|
||||
@@ -58,6 +128,12 @@ function loadPrefs(): ReadingPrefs {
|
||||
typeof parsed.justified === "boolean"
|
||||
? parsed.justified
|
||||
: DEFAULT_PREFS.justified,
|
||||
lightPalette: LIGHT_PALETTE_IDS.includes(parsed.lightPalette)
|
||||
? parsed.lightPalette
|
||||
: DEFAULT_PREFS.lightPalette,
|
||||
darkPalette: DARK_PALETTE_IDS.includes(parsed.darkPalette)
|
||||
? parsed.darkPalette
|
||||
: DEFAULT_PREFS.darkPalette,
|
||||
};
|
||||
} catch {
|
||||
return DEFAULT_PREFS;
|
||||
@@ -86,6 +162,11 @@ function applyClasses(prefs: ReadingPrefs) {
|
||||
|
||||
// Justified
|
||||
root.classList.toggle("reading-justified", prefs.justified);
|
||||
|
||||
// Palettes — always set both attributes so the appropriate one takes
|
||||
// effect when the user toggles light ↔ dark.
|
||||
root.setAttribute("data-light-palette", prefs.lightPalette);
|
||||
root.setAttribute("data-dark-palette", prefs.darkPalette);
|
||||
}
|
||||
|
||||
function Toggle({
|
||||
@@ -128,7 +209,7 @@ export function ReadingSettings() {
|
||||
const [prefs, setPrefs] = useState<ReadingPrefs>(() => loadPrefs());
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const { theme, setTheme } = useTheme();
|
||||
const { theme, setTheme, resolvedTheme } = useTheme();
|
||||
const mounted = useMounted();
|
||||
|
||||
// Apply classes on mount (prefs already loaded via lazy initializer)
|
||||
@@ -178,6 +259,14 @@ export function ReadingSettings() {
|
||||
const fontSizeIndex = FONT_SIZES.findIndex((f) => f.value === prefs.fontSize);
|
||||
const currentFontSizeLabel = FONT_SIZES[fontSizeIndex]?.label ?? "M";
|
||||
|
||||
// Show palettes matching the currently visible mode. Falls back to light if
|
||||
// resolvedTheme hasn't been determined yet (e.g. first render).
|
||||
const isDark = resolvedTheme === "dark";
|
||||
const visiblePalettes = isDark ? DARK_PALETTES : LIGHT_PALETTES;
|
||||
const activePaletteId = isDark ? prefs.darkPalette : prefs.lightPalette;
|
||||
const activePaletteName =
|
||||
visiblePalettes.find((p) => p.id === activePaletteId)?.name ?? "";
|
||||
|
||||
if (!mounted) {
|
||||
return (
|
||||
<Button variant="ghost" size="icon-sm" aria-label="Reading settings">
|
||||
@@ -239,6 +328,54 @@ export function ReadingSettings() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Palette selector — shows light or dark palettes depending on
|
||||
the currently resolved theme. */}
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Palette
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{activePaletteName}
|
||||
</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-2 mt-2">
|
||||
{visiblePalettes.map((p) => {
|
||||
const isActive = activePaletteId === p.id;
|
||||
return (
|
||||
<button
|
||||
key={p.id}
|
||||
type="button"
|
||||
onClick={() =>
|
||||
updatePrefs(
|
||||
isDark
|
||||
? { darkPalette: p.id as DarkPalette }
|
||||
: { lightPalette: p.id as LightPalette },
|
||||
)
|
||||
}
|
||||
aria-label={`Use ${p.name} palette`}
|
||||
aria-pressed={isActive}
|
||||
title={p.name}
|
||||
className={cn(
|
||||
"group relative aspect-square rounded-md transition-all cursor-pointer overflow-hidden",
|
||||
"border-2 outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
||||
isActive
|
||||
? "border-primary ring-2 ring-primary/30"
|
||||
: "border-border hover:border-foreground/40",
|
||||
)}
|
||||
style={{ backgroundColor: p.bg }}
|
||||
>
|
||||
{/* Primary-color dot in the corner */}
|
||||
<span
|
||||
className="absolute bottom-1.5 right-1.5 size-3 rounded-full ring-1 ring-black/10"
|
||||
style={{ backgroundColor: p.primary }}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-border pt-3 space-y-1">
|
||||
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Reading
|
||||
|
||||
Reference in New Issue
Block a user