D
Docs

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

  1. Server-Side: Nuxt Color Mode detects stored preference or system preference
  2. Rendering: Page renders with correct theme class already applied to <html>
  3. Hydration: No flicker because theme is already applied
  4. 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 name
  • themes: Array of available theme names
  • typographies: Array of available typography names
  • isDarkTheme: Boolean indicating if current theme is dark
  • colorMode: 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:

  1. Default styles: Applied to base elements (h1, h2, p, etc.)
  2. Utility classes: Higher specificity for overrides
  3. 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:

  1. 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 */
}
  1. 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',
  }
}
  1. Add visual indicator in ThemeSwitcher.client.vue (optional):
theme === 'newtheme' ? 'bg-blue-800 border-blue-500' :

Adding New Typography

To add a new font:

  1. Load the font (Google Fonts, local files, etc.)
  2. 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