Theme Customization
Customize colors, dark mode tokens, and build your brand palette.
Theme Customization
SentinelGrid uses CSS variables defined with oklch() color space for perceptually uniform lightness. Dark mode is the default and is toggled by adding the .dark class to <html>.
Dark mode active
Toggle to see tokens swap in real time.
Color Tokens
SentinelGrid defines semantic color tokens (background, card, primary, etc.) plus status tokens (success, warning, destructive, info) and an eight-color chart palette. Each token has a light and a dark variant.
| Token | Variable | Light | Dark |
|---|---|---|---|
| background | --background | oklch(0.985 0.002 240) | oklch(0.14 0.005 240) |
| card | --card | oklch(1 0 0) | oklch(0.18 0.006 240) |
| primary | --primary | oklch(0.62 0.13 200) | oklch(0.78 0.13 195) |
| accent | --accent | oklch(0.96 0.015 280) | oklch(0.28 0.04 290) |
| success | --success | oklch(0.6 0.13 150) | oklch(0.72 0.15 155) |
| warning | --warning | oklch(0.7 0.16 75) | oklch(0.78 0.16 75) |
| destructive | --destructive | oklch(0.58 0.22 25) | oklch(0.68 0.21 22) |
| info | --info | oklch(0.6 0.12 230) | oklch(0.72 0.13 230) |
| border | --border | oklch(0.9 0.005 240) | oklch(1 0 0 / 8%) |
| muted-foreground | --muted-foreground | oklch(0.5 0.01 240) | oklch(0.66 0.008 240) |
Chart palette
The chart palette is indexed 1–8 and used by every chart wrapper. Colors are tuned for accessibility in both themes.
chart-1
--chart-1
#22D3EE
chart-2
--chart-2
#A78BFA
chart-3
--chart-3
#F59E0B
chart-4
--chart-4
#34D399
chart-5
--chart-5
#F87171
chart-6
--chart-6
#FB923C
chart-7
--chart-7
#60A5FA
chart-8
--chart-8
#71717A
Dark & Light Mode
Theme state is stored in localStorage under sentinelgrid-theme. A blocking inline script in the root layout applies the class before hydration to prevent a flash of incorrect theme.
<script
dangerouslySetInnerHTML={{
__html: `
try {
const stored = localStorage.getItem('sentinelgrid-theme');
const theme = stored || 'dark';
if (theme === 'dark') document.documentElement.classList.add('dark');
else document.documentElement.classList.remove('dark');
} catch (_) {
document.documentElement.classList.add('dark');
}
`,
}}
/>To toggle themes, use the next-themes package (already wired in the root layout) or manipulate the class directly:
"use client";
import { Moon, Sun } from "lucide-react";
import { Button } from "@/components/ui/button";
export function ThemeToggle() {
return (
<Button
variant="ghost"
size="icon"
onClick={() => document.documentElement.classList.toggle("dark")}
>
<Sun className="w-4 h-4 dark:hidden" />
<Moon className="w-4 h-4 hidden dark:block" />
</Button>
);
}CSS Variables
Variables live in globals.css under the :root (light) and .dark selectors. Tailwind v4 maps them through the @theme inline block so utilities like bg-primary resolve correctly.
@theme inline {
--color-primary: var(--primary);
--color-background: var(--background);
--color-card: var(--card);
/* ... */
}
:root {
--primary: oklch(0.62 0.13 200);
}
.dark {
--primary: oklch(0.78 0.13 195);
}Adding a Custom Palette
To rebrand SentinelGrid, replace the primary and accent OKLCH values in both :root and .dark. Keep the lightness similar so contrast ratios are preserved.
:root {
--primary: oklch(0.6 0.15 150); /* was cyan */
}
.dark {
--primary: oklch(0.72 0.16 155); /* was cyan */
--sidebar-primary: oklch(0.72 0.16 155);
}OKLCH primer
oklch(L C H) stands for lightness, chroma, hue. Hue is in degrees (0–360), so swapping from cyan (200) to violet (290) is a single number change. Lightness stays at 0.62 in light mode and 0.78 in dark mode for consistency.