Published on

Theming System: From Matrix Green to Sunset Orange

Authors

Creating a theming system for my Git Command Terminal was more complex than changing a few colors. I wanted users to switch between radically different aesthetics - from cyberpunk Matrix green to warm Sunset orange.

Here's how I built a robust theming system that goes beyond basic color swapping.

More Than Just Colors

A terminal theme needs to convey personality and mood:

  • Matrix: Hacker aesthetic with bright green on black
  • Oceanic: Calm blues suggesting deep waters
  • Sunset: Warm oranges and yellows for a cozy feel
  • Midnight: Rich purples for late-night coding
  • Neon: Cyberpunk pink and cyan combinations
  • Light: Traditional light theme for accessibility

Each theme needed to feel cohesive across the entire application, not just the terminal area.

Design System Requirements

The theming system needed to support:

  • Runtime theme switching without page reload
  • Consistent color relationships across themes
  • Accessibility considerations (contrast ratios)
  • Easy addition of new themes
  • Type safety for theme values

Why OKLCH Over HSL/RGB

Traditional color spaces have limitations:

/* HSL - perceptually uneven */
hsl(240, 50%, 50%) /* Not the same perceived lightness as */
hsl(60, 50%, 50%)  /* this yellow */

/* RGB - no intuitive relationship */
rgb(128, 128, 255) /* Hard to create harmonious variations */

OKLCH (Oklch Lightness Chroma Hue) provides perceptual uniformity:

/* OKLCH - perceptually even lightness */
oklch(0.7 0.15 240) /* Same perceived lightness as */
oklch(0.7 0.15 60)  /* this, just different hue */

Benefits for Theme Creation

Predictable Lightness: 0.7 lightness looks the same across all hues Intuitive Adjustments: Change just the hue to create color variations Better Gradients: Smoother transitions between colors Accessibility: Easier to maintain contrast ratios

Comprehensive Color Palette

Each theme defines all semantic colors:

export interface Theme {
  id: string
  name: string
  colors: {
    background: string
    foreground: string
    card: string
    cardForeground: string
    popover: string
    popoverForeground: string
    primary: string
    primaryForeground: string
    secondary: string
    secondaryForeground: string
    accent: string
    accentForeground: string
    destructive: string
    destructiveForeground: string
    muted: string
    mutedForeground: string
    border: string
    input: string
    ring: string
  }
}

Matrix Theme Example

The iconic Matrix theme using OKLCH:

{
  id: 'matrix',
  name: 'Matrix Terminal',
  colors: {
    background: 'oklch(0.15 0.02 240)',      // Very dark blue-black
    foreground: 'oklch(0.7 0.15 145)',       // Bright matrix green
    primary: 'oklch(0.7 0.15 145)',          // Same matrix green
    primaryForeground: 'oklch(0.15 0.02 240)', // Dark background
    accent: 'oklch(0.7 0.15 45)',            // Warning yellow-green
    destructive: 'oklch(0.65 0.2 25)',       // Error red-orange
    // ... other colors
  }
}

Dynamic Theme Application

Runtime theme switching updates CSS variables:

export const applyTheme = (theme: Theme) => {
  const root = document.documentElement

  Object.entries(theme.colors).forEach(([key, value]) => {
    // Convert camelCase to kebab-case
    const cssVariable = key.replace(/([A-Z])/g, '-$1').toLowerCase()
    root.style.setProperty(`--${cssVariable}`, value)
  })
}

Theme Persistence

User theme preferences persist across sessions:

export const getCurrentTheme = (): string => {
  if (typeof window === 'undefined') return 'matrix'
  return localStorage.getItem('git-terminal-theme') || 'matrix'
}

export const setCurrentTheme = (themeId: string) => {
  if (typeof window === 'undefined') return
  localStorage.setItem('git-terminal-theme', themeId)
}

Theme Handler Hook

A custom hook manages theme state and persistence:

export function useThemeHandlers({ currentThemeId, setCurrentThemeId }: UseThemeHandlersProps) {
  useEffect(() => {
    const savedTheme = getCurrentTheme()
    setCurrentThemeId(savedTheme)
    const theme = themes.find((t) => t.id === savedTheme) || themes[0]
    applyTheme(theme)
  })

  useEffect(() => {
    const theme = themes.find((t) => t.id === currentThemeId) || themes[0]
    applyTheme(theme)
  }, [currentThemeId])

  const handleThemeChange = (themeId: string) => {
    setCurrentThemeId(themeId)
    setCurrentTheme(themeId)
    const theme = themes.find((t) => t.id === themeId)
    if (theme) {
      toast.success(strings.buildSuccessMessage('themeChanged', { themeName: theme.name }))
    }
  }

  return {
    handleThemeChange,
  }
}

Color Theory in Practice

Each theme uses color theory principles:

  • Matrix Theme: Monochromatic green with high contrast
  • Oceanic Theme: Analogous blues and teals
  • Sunset Theme: Warm oranges and yellows
  • Midnight Theme: Cool purples and magentas
  • Neon Theme: Complementary pink and cyan

Looking Forward

Building this theming system taught me that good themes are more than collections of colors - they're cohesive design languages that affect the entire user experience. The investment in OKLCH color spaces and semantic color roles paid off with a system that's both powerful for developers and delightful for users.

The complete theming system, with all six themes and the infrastructure to support them, is available in the Git Command Terminal repository. It demonstrates how thoughtful color design can transform a simple terminal into an immersive, personalized experience.