Theme Guide

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.

TokenVariableLightDark
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.

src/app/layout.tsx (inline script)
<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:

Theme toggle button
"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.

src/app/globals.css (excerpt)
@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.

Rebranding to a green primary
: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.

Command Palette

Search for a command to run...