Theming System
The application features a comprehensive theming system built on Nuxt Color Mode that provides SSR-safe theme switching without flicker. The system supports multiple color schemes, typography options, and automatic system preference detection.
Overview
The theming system consists of four main components:
- Color Themes: Six distinct color schemes with different aesthetics
- Typography: Four font families with detailed styling configurations
- SSR-Safe Switching: No flicker on page load using Nuxt Color Mode
- Theme Switcher: A comprehensive UI component for theme and font selection
Color Themes
Available Themes
Terminal Theme (Dark) - Default
A dark, brutalist aesthetic inspired by terminal interfaces:
- Background: Pure black (
oklch(0 0 0)
) - Primary Color: Bright yellow (
oklch(0.8 0.18 85)
) - Border Radius: 0 (sharp, brutalist corners)
- Design Philosophy: Minimal, functional, developer-focused
- CSS Class:
.terminal-mode
Forest Theme (Dark)
A nature-inspired dark theme with green accents:
- Background: Dark forest green (
oklch(0.15 0.02 120)
) - Primary Color: Vibrant green (
oklch(0.65 0.18 160)
) - Border Radius: 0.625rem (medium rounded)
- Design Philosophy: Natural, calming, organic
- CSS Class:
.forest-mode
Ocean Theme (Dark)
A deep blue theme reminiscent of ocean depths:
- Background: Deep ocean blue (
oklch(0.12 0.03 240)
) - Primary Color: Bright cyan (
oklch(0.7 0.15 184.704)
) - Border Radius: 0.5rem (moderate rounded)
- Design Philosophy: Fluid, professional, tech-focused
- CSS Class:
.ocean-mode
Purple Theme (Dark)
An elegant purple theme with sophisticated styling:
- Background: Rich purple (
oklch(0.13 0.04 280)
) - Primary Color: Bright purple (
oklch(0.65 0.15 285)
) - Border Radius: 0.75rem (large rounded)
- Design Philosophy: Creative, luxurious, artistic
- CSS Class:
.purple-mode
Light Theme
A clean, bright theme for daytime use:
- Background: Pure white (
oklch(1 0 0)
) - Primary Color: Bright yellow (
oklch(0.8 0.18 85)
) - Border Radius: 0.5rem (moderate rounded)
- Design Philosophy: Clean, accessible, professional
- CSS Class:
.light-mode
Cream Theme
A warm, comfortable theme with brown accents:
- Background: Warm cream (
oklch(0.98 0.01 85)
) - Primary Color: Rich brown (
oklch(0.5 0.12 35)
) - Border Radius: 0.375rem (small rounded)
- Design Philosophy: Warm, readable, comfortable
- CSS Class:
.cream-mode
Theme Configuration
Themes are configured in app.config.ts
with metadata for the theming system:
export default defineAppConfig({
theme: {
default: 'terminal',
available: ['terminal', 'forest', 'ocean', 'purple', 'light', 'cream'],
config: {
terminal: {
name: 'Terminal',
description: 'Dark terminal theme with yellow accents',
isDark: true,
className: 'terminal-mode',
primary: 'yellow',
radius: '0',
},
forest: {
name: 'Forest',
description: 'Dark forest theme with green accents',
isDark: true,
className: 'forest-mode',
primary: 'green',
radius: '0.625rem',
},
// ... other themes
}
}
})
CSS-Based Theme System
All theme colors are defined in CSS using the -mode
suffix convention:
/* Terminal Theme (Dark) */
.terminal-mode,
.dark.terminal-mode,
.dark {
--radius: 0;
--background: oklch(0 0 0);
--foreground: oklch(0.85 0 0);
--primary: oklch(0.8 0.18 85);
/* ... all shadcn required variables */
}
/* Forest Theme (Dark) */
.forest-mode,
.dark.forest-mode {
--radius: 0.625rem;
--background: oklch(0.15 0.02 120);
--primary: oklch(0.65 0.18 160);
/* ... complete color scheme */
}
Color System
Each theme includes all required shadcn/ui variables:
- Base Colors:
--background
,--foreground
,--card
,--popover
- Interactive Colors:
--primary
,--secondary
,--muted
,--accent
- Semantic Colors:
--destructive
,--success
,--warning
,--info
- Form Colors:
--border
,--input
,--ring
- Chart Colors:
--chart-1
through--chart-5
- Sidebar Colors: Complete sidebar color scheme
- Border Radius:
--radius
(varies per theme)
Typography System
Available Fonts
JetBrains Mono (Default)
- Type: Monospace
- Use Case: Developer-focused, terminal aesthetic
- Characteristics: Fixed-width, excellent code readability
- CSS Class:
.font-jetbrains
Inter
- Type: Sans-serif
- Use Case: Modern, clean interfaces
- Characteristics: Optimized for screen reading, versatile
- CSS Class:
.font-inter
Roboto
- Type: Sans-serif
- Use Case: Google Material Design aesthetic
- Characteristics: Friendly, readable, widely recognized
- CSS Class:
.font-roboto
Montserrat
- Type: Sans-serif
- Use Case: Elegant, geometric design
- Characteristics: Strong headers, modern appearance
- CSS Class:
.font-montserrat
Typography Configuration
Each font includes comprehensive styling with overridable defaults:
jetbrains: {
name: 'JetBrains Mono',
fontFamily: '\'JetBrains Mono\', monospace',
className: 'font-jetbrains',
headings: {
fontFamily: '\'JetBrains Mono\', monospace',
fontWeight: {
h1: '700', // bold
h2: '700', // bold
h3: '700', // bold
h4: '500', // medium
},
lineHeight: {
h1: 'none',
h2: 'tight',
h3: 'tight',
h4: 'normal',
},
letterSpacing: {
h1: 'tight',
h2: 'tight',
h3: 'normal',
h4: 'normal',
},
},
body: {
fontFamily: '\'JetBrains Mono\', monospace',
fontWeight: '400',
lineHeight: 'relaxed',
},
code: {
fontFamily: '\'JetBrains Mono\', monospace',
},
}
Typography Utility Classes
The system provides utility classes for overriding default typography:
/* Font Weights */
.font-light { font-weight: 300; }
.font-normal { font-weight: 400; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.font-extrabold { font-weight: 800; }
/* Line Heights */
.leading-none { line-height: 1; }
.leading-tight { line-height: 1.25; }
.leading-snug { line-height: 1.375; }
.leading-normal { line-height: 1.5; }
.leading-relaxed { line-height: 1.625; }
.leading-loose { line-height: 2; }
/* Letter Spacing */
.tracking-tighter { letter-spacing: -0.05em; }
.tracking-tight { letter-spacing: -0.025em; }
.tracking-normal { letter-spacing: 0em; }
.tracking-wide { letter-spacing: 0.025em; }
.tracking-wider { letter-spacing: 0.05em; }
SSR-Safe Theme System
Nuxt Color Mode Integration
The theming system is built on Nuxt Color Mode which provides:
- No Flicker: Themes are applied before page render
- SSR Compatible: Works with server-side rendering
- System Detection: Automatically detects user's system preference
- Persistent Storage: Saves preferences to localStorage
- Browser Support: Works with IE9+ and all modern browsers
Configuration
Nuxt Color Mode is configured in nuxt.config.ts
:
export default defineNuxtConfig({
modules: ['@nuxtjs/color-mode'],
colorMode: {
preference: 'system', // default value
fallback: 'terminal', // fallback if no system preference
classSuffix: '-mode', // adds -mode suffix to theme names
storage: 'localStorage',
storageKey: 'nuxt-color-mode'
}
})
How It Works
- Server-Side: Nuxt Color Mode detects stored preference or system preference
- Rendering: Page renders with correct theme class already applied to
<html>
- Hydration: No flicker because theme is already applied
- Switching: Theme changes update the HTML class and save to storage
Using the Theme System
Programmatic Usage
Use the useTheme
composable in your components:
<script setup>
const {
currentTheme,
currentTypography,
themes,
typographies,
isDarkTheme,
colorMode,
applyTheme,
applyTypography
} = useTheme()
// Apply a theme (SSR-safe, no flicker)
applyTheme('forest')
// Apply typography
applyTypography('inter')
// Check if current theme is dark
console.log(isDarkTheme.value) // true/false
// Access Nuxt Color Mode directly
console.log(colorMode.preference) // 'forest'
console.log(colorMode.value) // actual detected value
</script>
Available Properties
currentTheme
: Currently active theme name (computed from colorMode.preference)currentTypography
: Currently active typography namethemes
: Array of available theme namestypographies
: Array of available typography namesisDarkTheme
: Boolean indicating if current theme is darkcolorMode
: Direct access to Nuxt Color Mode object
Available Methods
applyTheme(themeName: string)
: Switch to a specific theme (SSR-safe)applyTypography(typographyName: string)
: Switch to a specific font
SSR-Safe Components
Use the ColorScheme
component for SSR-safe theme-dependent content:
<template>
<ColorScheme placeholder="Loading theme...">
<div v-if="isDarkTheme">Dark theme content</div>
<div v-else>Light theme content</div>
</ColorScheme>
</template>
Theme Switcher Component
Usage
Include the theme switcher in your layout:
<template>
<ThemeSwitcherClient />
</template>
Features
The theme switcher provides:
- Tabbed Interface: Separate tabs for themes and typography
- Visual Previews:
- Color indicators for each theme with appropriate colors
- Font previews showing "Aa" in the actual font
- Theme Names: Displays friendly names from configuration
- Dark/Light Icons: Automatically switches between sun/moon icons
- Persistent Storage: Automatically saves all preferences
- Accessibility: Proper ARIA labels and keyboard navigation
Theme Indicators
Each theme has a visual color indicator:
- Terminal: Black with yellow border
- Forest: Dark green with green border
- Ocean: Dark blue with cyan border
- Purple: Dark purple with purple border
- Light: White with gray border
- Cream: Light amber with brown border
Customizing Typography
Overriding Default Styles
Typography styles can be overridden at any level:
<template>
<!-- Uses default typography from theme -->
<h1>Default heading style</h1>
<!-- Override with utility classes -->
<h1 class="font-light tracking-wide">Custom heading style</h1>
<!-- Override for a whole section -->
<div class="font-semibold leading-relaxed">
<h2>This heading inherits container styles</h2>
<p>This paragraph also inherits container styles</p>
</div>
</template>
CSS Specificity
Typography styles are applied with appropriate specificity:
- Default styles: Applied to base elements (h1, h2, p, etc.)
- Utility classes: Higher specificity for overrides
- Inline styles: Highest specificity for specific cases
Implementation Details
CSS Class Management
Nuxt Color Mode automatically manages CSS classes:
<!-- Nuxt Color Mode automatically applies -->
<html class="forest-mode dark">
<!-- Your content with forest theme applied -->
</html>
Theme Detection
The system supports multiple detection methods:
- System Preference: Respects
prefers-color-scheme
- Stored Preference: Uses localStorage value
- Fallback: Uses configured default theme
Performance
- Zero JavaScript: Theme switching uses pure CSS
- Instant Application: No DOM manipulation delays
- Minimal Bundle: Only metadata in JavaScript
- Efficient Storage: Single localStorage key per preference type
Adding New Themes
To add a new theme:
- Add CSS classes in
assets/css/tailwind.css
:
/* New Theme */
.newtheme-mode,
.dark.newtheme-mode {
--radius: 0.5rem;
--background: oklch(0.2 0.05 200);
--foreground: oklch(0.9 0 0);
--primary: oklch(0.7 0.2 200);
/* ... all required shadcn variables */
}
- Update configuration in
app.config.ts
:
available: ['terminal', 'forest', 'ocean', 'purple', 'light', 'cream', 'newtheme'],
config: {
newtheme: {
name: 'New Theme',
description: 'A custom theme with blue accents',
isDark: true,
className: 'newtheme-mode',
primary: 'blue',
radius: '0.5rem',
}
}
- Add visual indicator in
ThemeSwitcher.client.vue
(optional):
theme === 'newtheme' ? 'bg-blue-800 border-blue-500' :
Adding New Typography
To add a new font:
- Load the font (Google Fonts, local files, etc.)
- Update configuration in
app.config.ts
:
available: ['jetbrains', 'inter', 'roboto', 'montserrat', 'newfont'],
config: {
newfont: {
name: 'New Font',
fontFamily: '\'New Font\', sans-serif',
className: 'font-newfont',
headings: {
fontFamily: '\'New Font\', sans-serif',
fontWeight: { h1: '800', h2: '700', h3: '600', h4: '500' },
lineHeight: { h1: 'tight', h2: 'tight', h3: 'snug', h4: 'normal' },
letterSpacing: { h1: 'tight', h2: 'normal', h3: 'normal', h4: 'normal' },
},
body: {
fontFamily: '\'New Font\', sans-serif',
fontWeight: '400',
lineHeight: 'relaxed',
},
code: {
fontFamily: '\'JetBrains Mono\', monospace', // Keep monospace for code
},
}
}
Best Practices
Design Consistency
- Maintain consistent component behavior across all themes
- Use semantic color names in components, not specific values
- Test all themes with your complete UI component library
- Ensure proper contrast ratios for accessibility
Performance
- Themes apply instantly without JavaScript overhead
- CSS-only switching provides optimal performance
- Minimal configuration data in JavaScript bundle
- Efficient localStorage usage
Accessibility
- All themes meet WCAG contrast requirements
- Respect user's system preferences when possible
- Provide clear visual feedback during theme switching
- Test with screen readers and keyboard navigation
- Support high contrast mode and reduced motion preferences
User Experience
- No flicker on page load or refresh
- Instant theme switching with visual feedback
- Persistent preferences across sessions
- Intuitive theme switcher with visual previews
- Graceful fallbacks for unsupported browsers
Development
- Use the
ColorScheme
component for SSR-safe theme-dependent content - Test theme switching in both development and production builds
- Verify SSR compatibility with your deployment setup
- Monitor bundle size when adding new themes or fonts