Theming
Customize colors, dark mode, and design tokens in kuzan
Theme model
kuzan is theme-driven via CSS custom properties. The runtime color scheme is controlled by:
- A class on
<html>(class="dark"or omitted for light) - An optional
data-theme="system"attribute (follows the OS preference) - An optional
<ThemeInit />script that readslocalStorage['kui-theme']and applies the saved choice on first paint
There is no framework-level config — the choice lives in your src/routes/layout.koze.
Color schemes
Dark mode
<html lang="en" class="dark">
Dark background with light text. Optimized for low-light environments.
Light mode
<html lang="en">
White background with dark text. Default when no class is set.
System preference
Follows the user's OS color scheme via prefers-color-scheme:
<html lang="en" data-theme="system">
Runtime theme switching
Use the ThemeToggle component to let users switch themes:
<script>
import ThemeToggle from '@kuratchi/kuzan/theme-toggle.koze';
</script>
<ThemeToggle />
The toggle:
- Adds/removes the
.darkclass on<html> - Saves the preference to
localStorageunder"kui-theme" - Syncs all toggle instances on the page
Pair it with <ThemeInit /> in <head> so the saved theme applies before the page paints — see Preventing FOUC.
Corner radius variants
kuzan reads radius from a data-radius attribute on <html>:
<html lang="en" class="dark" data-radius="default">
Supported values:
data-radius="default"— standard rounded corners (0.375rem - 0.75rem)data-radius="none"— square corners (0px radius)data-radius="full"— pill-shaped / fully rounded corners (9999px)
Omit the attribute for the default behavior.
CSS variables
All theme values are exposed as CSS custom properties. Override them in your own stylesheet:
Color tokens
:root {
/* Backgrounds */
--kui-bg: #ffffff;
--kui-bg-muted: #f9fafb;
--kui-bg-elevated: #ffffff;
/* Foreground */
--kui-fg: #09090b;
--kui-fg-muted: #71717a;
/* Borders & focus */
--kui-border: #e4e4e7;
--kui-ring: #18181b;
/* Primary brand color */
--kui-primary: #584cd9;
--kui-primary-fg: #ffffff;
--kui-primary-weak: color-mix(in srgb, #584cd9 12%, transparent);
--kui-primary-strong: #4338ca;
/* Semantic colors */
--kui-destructive: #ef4444;
--kui-destructive-fg: #ffffff;
--kui-success: #22c55e;
--kui-success-fg: #ffffff;
--kui-warning: #f59e0b;
--kui-warning-fg: #ffffff;
}
.dark {
--kui-bg: #09090b;
--kui-bg-muted: #18181b;
--kui-fg: #fafafa;
--kui-fg-muted: #a1a1aa;
--kui-border: #27272a;
--kui-primary: #818cf8;
/* ... */
}
Spacing tokens
:root {
--kui-spacing-xs: 0.25rem;
--kui-spacing-sm: 0.5rem;
--kui-spacing-md: 1rem;
--kui-spacing-lg: 1.5rem;
--kui-spacing-xl: 2rem;
}
Radius tokens
:root {
--kui-radius-sm: 0.375rem;
--kui-radius-md: 0.5rem;
--kui-radius-lg: 0.75rem;
}
Shadow tokens
:root {
--kui-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
--kui-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
--kui-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
--kui-shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
}
Timing tokens
:root {
--kui-duration-fast: 100ms;
--kui-duration-base: 150ms;
}
Customizing colors
Override CSS variables in src/app.css after the theme import:
/* src/app.css */
@import '@kuratchi/kuzan/styles/theme.css';
:root {
/* Change primary brand color */
--kui-primary: #10b981;
--kui-primary-fg: #ffffff;
--kui-primary-strong: #059669;
/* Adjust background */
--kui-bg: #fafafa;
--kui-bg-muted: #f4f4f5;
}
.dark {
--kui-primary: #34d399;
--kui-primary-fg: #09090b;
}
The framework auto-injects src/app.css into the app shell — no <link> tag needed.
Using with Tailwind CSS
kuzan composes with Tailwind via the standard PostCSS / Tailwind v4 pipeline. Install:
npm install -D @tailwindcss/vite
Add to your vite.config.ts:
import tailwindcss from '@tailwindcss/vite';
import { koze } from '@kuratchi/koze/vite';
export default defineConfig({
plugins: [
koze(),
tailwindcss(),
cloudflare({ viteEnvironment: { name: 'ssr' } }),
],
});
Then import both from src/app.css:
@import 'tailwindcss';
@import '@kuratchi/kuzan/styles/theme.css';
@source './routes/**/*.koze';
You can use Tailwind utility classes alongside kuzan components:
<Card title="Dashboard" class="max-w-2xl mx-auto">
<p class="text-sm text-gray-600">Custom Tailwind styling</p>
</Card>
Font customization
The default font stack:
:root {
--kui-font-sans: 'Inter', 'Segoe UI', system-ui, -apple-system,
BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
}
Override it in src/app.css:
:root {
--kui-font-sans: 'Your Font', system-ui, sans-serif;
}
body {
font-family: var(--kui-font-sans);
}
Preventing FOUC
To prevent a flash of unstyled content when the page loads, use the <ThemeInit /> component in your layout's <head>:
<script>
import ThemeInit from '@kuratchi/kuzan/theme-init.koze';
</script>
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My App</title>
<ThemeInit />
</head>
<body>
<slot></slot>
</body>
</html>
<ThemeInit /> reads localStorage['kui-theme'] and applies the saved class to <html> before the page paints.
Next steps
- Components — Browse the full component reference
- Framework Styling — Learn about CSS processing in Koze