Zeno

Theming

Customizing your app's appearance with Tailwind CSS 4

Zeno uses Tailwind CSS 4 with CSS variables for theming, supporting light and dark modes.

Setup

Import the base styles in your app:

@import "@zeno-lib/tailwind/globals.css";
@import "@zeno-lib/ui/styles/globals.css";

CSS Variables

Theme colors are defined as CSS variables in theme.css:

:root {
  --background: var(--color-white);
  --foreground: var(--color-gray-950);
  --primary: var(--color-gray-900);
  --primary-foreground: var(--color-gray-50);
  --secondary: var(--color-gray-100);
  --muted: var(--color-gray-100);
  --accent: var(--color-gray-100);
  --destructive: var(--color-error-600);
  --border: var(--color-gray-200);
  --ring: var(--color-gray-400);
  --radius: 0.625rem;
}

.dark {
  --background: var(--color-gray-950);
  --foreground: var(--color-gray-50);
  /* ... dark mode overrides */
}

Using Theme Colors

Reference theme colors in Tailwind classes:

<div className="bg-background text-foreground">
  <button className="bg-primary text-primary-foreground">
    Primary Button
  </button>
  <span className="text-muted-foreground">Muted text</span>
</div>

Available Colors

VariableLightDark
backgroundWhiteGray 950
foregroundGray 950Gray 50
primaryGray 900Gray 200
secondaryGray 100Gray 800
mutedGray 100Gray 800
accentGray 100Gray 700
destructiveError 600Error 400
borderGray 20010% white
cardWhiteGray 900
popoverWhiteGray 800

Dark Mode

Dark mode uses the .dark class on the root element. Use next-themes for automatic switching:

import { ThemeProvider } from "next-themes"

function App({ children }) {
  return (
    <ThemeProvider attribute="class" defaultTheme="system">
      {children}
    </ThemeProvider>
  )
}

Customizing

Override variables in your app's CSS:

:root {
  --primary: var(--color-blue-600);
  --primary-foreground: var(--color-white);
  --radius: 0.5rem;
}

Utilities

The cn function merges Tailwind classes safely:

import { cn } from "@zeno-lib/ui/lib/utils"

// Merges and deduplicates classes
cn("px-4 py-2", "px-6") // → "py-2 px-6"
cn("text-red-500", isError && "text-red-700")