Skip to content

Theming

Drizzle Cube provides a comprehensive theming system that allows you to customize the appearance of all client components. The theming system supports light mode, dark mode, and even custom themes like the built-in neon theme.

Drizzle Cube uses CSS custom properties (variables) prefixed with --dc- for all theming. This approach allows you to:

  • Switch between built-in themes (light, dark, neon)
  • Create custom themes by overriding CSS variables
  • Integrate with parent application themes (including DaisyUI)
  • Support system preference detection
  • Persist user theme preferences

The simplest way to implement theming is to use the built-in theme functions:

import { getTheme, setTheme, watchThemeChanges } from '@drizzle-cube/client'
// Get the current theme
const currentTheme = getTheme() // Returns: 'light' | 'dark' | 'neon'
// Set a theme
setTheme('dark') // Switches to dark theme
// Watch for theme changes
const unwatch = watchThemeChanges((theme) => {
console.log('Theme changed to:', theme)
})
// Clean up the watcher when done
unwatch()

Here’s a complete example of a theme toggle component:

import { useEffect, useState } from 'react'
import { getTheme, setTheme, watchThemeChanges, type Theme } from '@drizzle-cube/client'
export default function ThemeToggle() {
const [currentTheme, setCurrentTheme] = useState<Theme>('light')
useEffect(() => {
// Initialize theme state
setCurrentTheme(getTheme())
// Watch for theme changes from other sources
const unwatch = watchThemeChanges((theme) => {
setCurrentTheme(theme)
})
return unwatch
}, [])
const toggleTheme = () => {
const nextTheme = currentTheme === 'light' ? 'dark' : 'light'
setTheme(nextTheme)
}
return (
<button onClick={toggleTheme}>
{currentTheme === 'light' ? '🌙 Dark' : '☀️ Light'}
</button>
)
}

The default theme with clean, professional colors suitable for most applications.

setTheme('light')

Key characteristics:

  • White surfaces with subtle gray backgrounds
  • Dark text on light backgrounds
  • Blue primary colors
  • Excellent readability

A modern dark theme with reduced eye strain for low-light environments.

setTheme('dark')

Key characteristics:

  • Slate dark backgrounds
  • Light text on dark surfaces
  • Lighter blue primary colors
  • Reduced contrast for comfort

A bold, vibrant theme with fluorescent colors for a modern, eye-catching look.

setTheme('neon')

Key characteristics:

  • Deep purple-black backgrounds
  • Electric cyan and magenta accents
  • Bright neon borders and highlights
  • Glow effects on shadows

You can customize any theme by overriding the CSS custom properties in your application’s CSS:

/* Custom theme in your app's CSS */
:root {
--dc-primary: #ff6b6b;
--dc-primary-hover: #ee5a52;
--dc-surface: #ffffff;
--dc-text: #2d3748;
}
/* Custom dark theme variant */
[data-theme="dark"] {
--dc-primary: #ffa5a5;
--dc-primary-hover: #ff8787;
--dc-surface: #1a202c;
--dc-text: #f7fafc;
}

Use the applyTheme function to apply a custom theme configuration:

import { applyTheme, type ThemeConfig } from '@drizzle-cube/client'
const customTheme: ThemeConfig = {
name: 'corporate',
colors: {
primary: '#0066cc',
primaryHover: '#0052a3',
surface: '#ffffff',
surfaceSecondary: '#f5f7fa',
text: '#1a1a1a',
textSecondary: '#4a4a4a',
border: '#e0e0e0',
}
}
applyTheme(customTheme)

Drizzle Cube exports preset configurations you can use as a starting point:

import { THEME_PRESETS, applyTheme } from '@drizzle-cube/client'
// Apply a preset
applyTheme(THEME_PRESETS.dark)
// Extend a preset
const myTheme = {
...THEME_PRESETS.dark,
colors: {
...THEME_PRESETS.dark.colors,
primary: '#ff00ff', // Override specific colors
}
}
applyTheme(myTheme)

Background colors for cards, modals, and panels:

VariableDescription
--dc-surfacePrimary surface background
--dc-surface-secondarySecondary surface background
--dc-surface-tertiaryTertiary surface background
--dc-surface-hoverHover state for surfaces

Text colors at various emphasis levels:

VariableDescription
--dc-textPrimary text color
--dc-text-secondarySecondary text color
--dc-text-mutedMuted text color
--dc-text-disabledDisabled text color

Colors for dividers, outlines, and borders:

VariableDescription
--dc-borderPrimary border color
--dc-border-secondarySecondary border color
--dc-border-hoverHover state for borders

Main interactive element colors:

VariableDescription
--dc-primaryPrimary action color
--dc-primary-hoverHover state for primary
--dc-primary-contentText color on primary background

Colors for success, warning, error, and info states:

VariableDescription
--dc-successSuccess state color
--dc-success-bgSuccess background color
--dc-success-borderSuccess border color
--dc-warningWarning state color
--dc-warning-bgWarning background color
--dc-warning-borderWarning border color
--dc-errorError state color
--dc-error-bgError background color
--dc-error-borderError border color
--dc-infoInfo state color
--dc-info-bgInfo background color
--dc-info-borderInfo border color

Colors for destructive actions:

VariableDescription
--dc-dangerDanger action color
--dc-danger-hoverHover state for danger
--dc-danger-bgDanger background color

Colors for modal backdrops and overlays:

VariableDescription
--dc-overlayPrimary overlay color
--dc-overlay-lightLight overlay color

Colors for semantic field type badges in the Query Builder:

VariableDescription
--dc-dimension-bgDimension badge background
--dc-dimension-textDimension badge text
--dc-dimension-borderDimension badge border
--dc-time-dimension-bgTime dimension badge background
--dc-time-dimension-textTime dimension badge text
--dc-time-dimension-borderTime dimension badge border
--dc-measure-bgMeasure badge background
--dc-measure-textMeasure badge text
--dc-measure-borderMeasure badge border

Pre-configured shadow styles for elevation:

VariableDescription
--dc-shadow-smSmall shadow
--dc-shadowDefault shadow
--dc-shadow-mdMedium shadow
--dc-shadow-lgLarge shadow
--dc-shadow-xlExtra large shadow
--dc-shadow-2xl2X large shadow

Drizzle Cube automatically detects the user’s system color scheme preference:

// The getTheme() function checks in this order:
// 1. localStorage saved preference
// 2. data-theme attribute
// 3. .dark or .neon class on html/body
// 4. System preference (prefers-color-scheme)
// 5. Defaults to 'light'
const theme = getTheme()

Monitor theme changes from any source (user action, system preference change, etc.):

import { watchThemeChanges } from '@drizzle-cube/client'
const unwatch = watchThemeChanges((newTheme) => {
console.log('Theme changed to:', newTheme)
// Update your app state, analytics, etc.
})
// Remember to clean up
unwatch()

Theme preferences are automatically saved to localStorage:

import { setTheme } from '@drizzle-cube/client'
// This will save 'dark' to localStorage
setTheme('dark')
// On next page load, getTheme() will return 'dark'

Remove all custom theme properties and reset to the built-in theme:

import { resetTheme } from '@drizzle-cube/client'
// Removes all --dc-* custom properties from inline styles
resetTheme()

Get the current value of any theme variable:

import { getThemeVariable } from '@drizzle-cube/client'
const primaryColor = getThemeVariable('primary')
// Returns the computed value, e.g., '#3b82f6'

Set specific theme variables programmatically:

import { setThemeVariable } from '@drizzle-cube/client'
setThemeVariable('primary', '#ff00ff')
setThemeVariable('surface', '#ffffff')

If your application uses DaisyUI, you can map Drizzle Cube theme variables to DaisyUI’s theme system:

/* In your app's CSS */
:root {
--dc-surface: var(--color-base-100);
--dc-surface-secondary: var(--color-base-200);
--dc-surface-tertiary: var(--color-base-300);
--dc-text: var(--color-base-content);
--dc-primary: var(--color-primary);
--dc-primary-hover: var(--color-primary-focus);
--dc-success: var(--color-success);
--dc-warning: var(--color-warning);
--dc-error: var(--color-error);
--dc-info: var(--color-info);
}

Drizzle Cube exports Tailwind utility classes for theme variables:

<div className="bg-dc-surface border-dc-border text-dc-text">
<h1 className="text-dc-text-secondary">Title</h1>
<button className="bg-dc-primary hover:bg-dc-primary-hover">
Click me
</button>
</div>

Available utility classes:

  • Background: bg-dc-surface, bg-dc-surface-secondary, bg-dc-surface-tertiary
  • Text: text-dc-text, text-dc-text-secondary, text-dc-text-muted
  • Border: border-dc-border, border-dc-border-secondary
  • Hover: hover:bg-dc-surface-hover, hover:border-dc-border-hover
  • Field types: bg-dc-dimension, text-dc-dimension, border-dc-dimension, etc.

Sync Drizzle Cube themes with your application’s theme system:

import { watchThemeChanges, setTheme } from '@drizzle-cube/client'
// Watch your app's theme changes
appTheme.onChange((theme) => {
// Map your theme to Drizzle Cube theme
if (theme === 'dark') {
setTheme('dark')
} else {
setTheme('light')
}
})
// Watch Drizzle Cube theme changes
watchThemeChanges((dcTheme) => {
// Update your app's theme system
appTheme.set(dcTheme)
})

All theme functions and types are fully typed:

import type { Theme, ThemeConfig, ThemeColorTokens } from '@drizzle-cube/client'
// Theme type is a union of valid theme names
const myTheme: Theme = 'dark' // ✓ Valid
const invalidTheme: Theme = 'purple' // ✗ Type error
// ThemeConfig for custom themes
const config: ThemeConfig = {
name: 'custom',
colors: {
primary: '#ff0000',
surface: '#ffffff',
// All colors are optional and typed
}
}
// ThemeColorTokens for complete color definitions
const colors: ThemeColorTokens = {
surface: '#ffffff',
surfaceSecondary: '#f9fafb',
surfaceTertiary: '#f3f4f6',
surfaceHover: '#f3f4f6',
text: '#111827',
textSecondary: '#374151',
textMuted: '#6b7280',
textDisabled: '#9ca3af',
// ... all other color tokens
}

Set the theme as early as possible to avoid flash of unstyled content:

// In your app's entry point or root component
import { useEffect } from 'react'
import { getTheme, setTheme } from '@drizzle-cube/client'
export function App() {
useEffect(() => {
// This will read from localStorage or system preference
const savedTheme = getTheme()
setTheme(savedTheme)
}, [])
return <YourApp />
}

Always give users control over the theme:

<ThemeToggle />
{/* or */}
<select onChange={(e) => setTheme(e.target.value as Theme)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="neon">Neon</option>
</select>

Consider respecting the user’s system preferences by default:

import { getTheme, setTheme } from '@drizzle-cube/client'
// On app load
const theme = getTheme() // Automatically checks system preference
if (!localStorage.getItem('theme')) {
// User hasn't explicitly chosen, respect system
setTheme(theme)
}

When customizing, use the semantic variable names rather than hardcoding colors in components:

/* Good - uses theme variables */
.my-component {
background-color: var(--dc-surface);
color: var(--dc-text);
border: 1px solid var(--dc-border);
}
/* Avoid - hardcoded colors won't respond to theme changes */
.my-component {
background-color: #ffffff;
color: #111827;
border: 1px solid #e5e7eb;
}

When creating custom themes, test all components in each theme mode to ensure good contrast and readability.

If you’re using the deprecated isDarkMode() function:

// Old way (deprecated)
import { isDarkMode } from '@drizzle-cube/client'
const dark = isDarkMode()
// New way
import { getTheme } from '@drizzle-cube/client'
const theme = getTheme()
const dark = theme === 'dark' || theme === 'neon'

If you’re adding theming to an existing Drizzle Cube integration:

  1. Import the theme utilities:

    import { getTheme, setTheme, watchThemeChanges } from '@drizzle-cube/client'
  2. Create a theme toggle component (see example above)

  3. Initialize the theme on app load:

    useEffect(() => {
    setTheme(getTheme())
    }, [])
  4. Replace any hardcoded colors with theme variables

import { useState, useEffect } from 'react'
import {
getTheme,
setTheme,
watchThemeChanges,
type Theme,
CubeProvider,
AnalyticsDashboard
} from '@drizzle-cube/client'
export function ThemedDashboardApp() {
const [theme, setThemeState] = useState<Theme>('light')
useEffect(() => {
// Initialize theme
const currentTheme = getTheme()
setTheme(currentTheme)
setThemeState(currentTheme)
// Watch for changes
const unwatch = watchThemeChanges((newTheme) => {
setThemeState(newTheme)
})
return unwatch
}, [])
const handleThemeChange = (newTheme: Theme) => {
setTheme(newTheme)
}
return (
<div className="app">
<header>
<h1>My Analytics Dashboard</h1>
<select
value={theme}
onChange={(e) => handleThemeChange(e.target.value as Theme)}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="neon">Neon</option>
</select>
</header>
<CubeProvider apiUrl="/api/cube">
<AnalyticsDashboard
dashboardId="sales"
layout={{ lg: [...], md: [...], sm: [...], xs: [...] }}
/>
</CubeProvider>
</div>
)
}
import { applyTheme, type ThemeConfig } from '@drizzle-cube/client'
// Define your brand colors
const brandTheme: ThemeConfig = {
name: 'brand',
colors: {
// Brand colors
primary: '#6366f1', // Indigo
primaryHover: '#4f46e5',
primaryContent: '#ffffff',
// Light mode surfaces
surface: '#ffffff',
surfaceSecondary: '#f8fafc',
surfaceTertiary: '#f1f5f9',
// Text colors
text: '#0f172a',
textSecondary: '#475569',
textMuted: '#64748b',
// Border colors
border: '#e2e8f0',
borderSecondary: '#cbd5e1',
// Semantic colors
success: '#10b981',
warning: '#f59e0b',
error: '#ef4444',
info: '#3b82f6',
}
}
// Apply on app load
applyTheme(brandTheme)

This comprehensive theming system gives you complete control over the appearance of Drizzle Cube components while maintaining consistency and accessibility across all themes.