Theming System
Matrix UI provides a powerful theming system based on CSS custom properties, enabling complete visual customization while maintaining consistency.
Built-in Themes
Matrix UI comes with 5 professionally designed themes:
Theme Structure
Each theme defines a set of design tokens using CSS custom properties:
// Theme structure with light and dark mode support
interface Theme {
name: string
colors: ThemeColors // Light mode colors
darkColors: ThemeColors // Dark mode colors
gradients: {
primary: string
secondary: string
accent: string
}
}
// Color tokens (HSL format: "hue saturation% lightness%")
interface ThemeColors {
background: string
foreground: string
card: string
'card-foreground': string
popover: string
'popover-foreground': string
primary: string
'primary-foreground': string
secondary: string
'secondary-foreground': string
muted: string
'muted-foreground': string
accent: string
'accent-foreground': string
destructive: string
'destructive-foreground': string
success: string
'success-foreground': string
warning: string
'warning-foreground': string
border: string
input: string
ring: string
radius: string
// Chart colors: 'chart-1' through 'chart-8'
}Design Tokens
Matrix UI uses a three-tier token system:
Raw Tokens
Base color values
--matrix-blue-500: #3b82f6
--matrix-gray-900: #111827
--matrix-red-600: #dc2626Semantic Tokens
Contextual meanings
--primary: var(--matrix-blue-500)
--background: var(--matrix-white)
--destructive: var(--matrix-red-600)Component Tokens
Component-specific values
--button-bg: var(--primary)
--button-hover: var(--primary-hover)
--card-border: var(--border)Applying Themes
Using the Theme Utils
import { applyTheme, getStoredTheme, getStoredMode, type ColorMode } from '@matrix-ui/themes'
// Apply a theme with light mode (default)
applyTheme('carbon')
// Apply a theme with dark mode
applyTheme('obsidian', 'dark')
// Restore user's saved preferences
const savedTheme = getStoredTheme() || 'carbon'
const savedMode = getStoredMode() // Returns 'light' if not set
applyTheme(savedTheme, savedMode)
// Toggle between light and dark mode
function toggleDarkMode() {
const theme = getStoredTheme() || 'carbon'
const currentMode = getStoredMode()
const newMode: ColorMode = currentMode === 'light' ? 'dark' : 'light'
applyTheme(theme, newMode)
}In a Next.js App
// app/layout.tsx
import type { Metadata } from 'next'
// Flash prevention script - runs before React hydration
const themeScript = `
(function() {
try {
var mode = localStorage.getItem('matrix-mode');
if (mode === 'dark') {
document.documentElement.classList.add('dark');
}
} catch (e) {}
})();
`
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html: themeScript }} />
</head>
<body>{children}</body>
</html>
)
}
// In a client component:
'use client'
import { useEffect } from 'react'
import { applyTheme, getStoredTheme, getStoredMode } from '@matrix-ui/themes'
export function ThemeProvider({ children }) {
useEffect(() => {
const theme = getStoredTheme() || 'carbon'
const mode = getStoredMode()
applyTheme(theme, mode)
}, [])
return children
}Creating Custom Themes
You can create your own themes by defining both light and dark color sets:
// Define a custom theme with light and dark modes
const myCustomTheme = {
name: 'my-theme',
colors: {
// Light mode colors (HSL format)
background: '0 0% 100%',
foreground: '240 10% 3.9%',
card: '0 0% 100%',
'card-foreground': '240 10% 3.9%',
primary: '346.8 77.2% 49.8%',
'primary-foreground': '355.7 100% 97.3%',
// ... all 32 color tokens
},
darkColors: {
// Dark mode colors
background: '20 14.3% 4.1%',
foreground: '0 0% 95%',
card: '24 9.8% 8%',
'card-foreground': '0 0% 95%',
primary: '346.8 77.2% 49.8%',
'primary-foreground': '355.7 100% 97.3%',
// ... all 32 color tokens
},
gradients: {
primary: 'from-rose-500 to-rose-600',
secondary: 'from-gray-100 to-gray-200',
accent: 'from-rose-50 to-rose-100',
}
}
// Register and use custom theme (coming soon)
// For now, override CSS variables directly in your globals.cssTheme Generator
Use the CLI to generate a theme CSS file:
# Generate theme CSS with both light and dark modes
npx matrix-ui theme generate --name carbon --mode both --out ./matrix-theme.css
# Generate only dark mode
npx matrix-ui theme generate --name obsidian --mode darkDark Mode
Matrix UI has built-in dark mode support for all themes:
Using Built-in Dark Mode
import { applyTheme, getStoredTheme, getStoredMode, type ColorMode } from '@matrix-ui/themes'
// Apply dark mode
applyTheme('carbon', 'dark')
// Toggle between modes
function toggleDarkMode() {
const theme = getStoredTheme() || 'carbon'
const currentMode = getStoredMode()
const newMode: ColorMode = currentMode === 'light' ? 'dark' : 'light'
applyTheme(theme, newMode)
}
// The applyTheme function automatically:
// 1. Sets CSS variables for the selected mode
// 2. Toggles the 'dark' class on <html>
// 3. Saves preference to localStorageListening to Theme Changes
// Listen for theme and mode changes
window.addEventListener('theme-change', (event) => {
const { theme, mode } = event.detail
console.log(`Theme: ${theme}, Mode: ${mode}`)
})System Preference Detection
import { applyTheme, getStoredTheme, getStoredMode, type ColorMode } from '@matrix-ui/themes'
// Detect and apply system preference on first visit
function initializeTheme() {
const storedMode = localStorage.getItem('matrix-mode')
// If user hasn't set a preference, use system preference
if (!storedMode) {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
const mode: ColorMode = prefersDark ? 'dark' : 'light'
applyTheme('carbon', mode)
} else {
const theme = getStoredTheme() || 'carbon'
const mode = getStoredMode()
applyTheme(theme, mode)
}
}
// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
const theme = getStoredTheme() || 'carbon'
const mode: ColorMode = e.matches ? 'dark' : 'light'
applyTheme(theme, mode)
})Theme Customization
Overriding Specific Tokens
/* In your global CSS */
:root {
/* Override primary color */
--primary: 262.1 83.3% 57.8%;
/* Override radius */
--radius: 0.75rem;
/* Override specific component */
--button-height: 2.75rem;
}Component-Level Theming
// Using className for one-off customization
<Button
className="bg-purple-600 hover:bg-purple-700 text-white"
>
Custom Styled Button
</Button>
// Creating variant with CVA
const customButtonVariants = cva(
"inline-flex items-center justify-center",
{
variants: {
variant: {
brand: "bg-brand text-brand-foreground hover:bg-brand/90",
}
}
}
)Best Practices
Use Semantic Tokens
Always use semantic tokens (e.g., --primary) instead of raw colors to ensure your UI adapts to theme changes.
Maintain Contrast Ratios
Ensure your custom themes meet WCAG accessibility standards with proper contrast ratios between foreground and background colors.
Test Across Themes
Always test your components with multiple themes to ensure they look good in both light and dark modes.
Color Format
Matrix UI uses HSL color format for better manipulation and consistency: