thc-dynamic-theme-demo Svelte Themes

Thc Dynamic Theme Demo

Production-ready SvelteKit dynamic theme customization demo with WCAG accessibility and security best practices

Dynamic Theme Customization at Runtime

This repository is the companion demo for the article "Dynamic Theme Customization at Runtime" published on The Hackpile Chronicle.

Important Note: This codebase includes production-ready enhancements beyond what's covered in the article. The article focuses on core concepts and patterns, while this repository adds security, accessibility, and performance features needed for real-world applications.

  • Following the article? The core patterns work exactly as described—extra features are opt-in
  • Production deployment? See PRODUCTION.md for security, accessibility & deployment guide
  • What's been added? See IMPROVEMENTS.md for complete documentation
  • Contrast & accessibility? See CONTRAST-USAGE.md for real-world examples

Core Concept

A practical Svelte 5 + SvelteKit approach to theming with a clear rule:

  • Use CSS-first theming when colors are known at build time
  • Use runtime JavaScript + CSS custom properties when colors are only known at runtime

Production Enhancements

Beyond the article's core patterns, this repository includes enterprise-grade features:

Security

  • Input validation & sanitization for all color values
  • CSS injection prevention
  • Type-safe error boundaries with custom error classes
  • Validation before DOM manipulation

Accessibility

  • WCAG 2.1 AA/AAA contrast validation
  • Screen reader announcements for theme changes
  • prefers-reduced-motion and prefers-contrast support
  • Automatic color adjustments for accessibility compliance

Performance

  • Memoized color calculations for hot paths
  • Debounced/throttled updates
  • RequestAnimationFrame-based visual updates
  • Cache management to prevent memory leaks

Configuration

  • Environment-based configuration (.env support)
  • Debug mode for development
  • Production API integration helpers
  • Configurable WCAG levels and transition speeds

Why add these? The article demonstrates how to implement dynamic theming; this codebase shows how to do it safely and accessibly in production. Features like WCAG validation, input sanitization, and error handling are critical when user-provided colors or API data drive your UI.

See PRODUCTION.md for deployment checklist, security guidelines, and API integration
See IMPROVEMENTS.md for detailed API documentation of all enhancements
See CONTRAST-USAGE.md for contrast checking and accessible text color examples

What this demo includes

1) Context-based theme system (light / dark / system)

  • Theme context with preference persistence
  • System theme detection
  • Nested theme overrides with ThemeProvider
  • SSR-safe setup to avoid flash of incorrect theme

Main entry points:

  • src/lib/theme/theme-context.svelte.ts – theme state management
  • src/lib/theme/ThemeProvider.svelte – context provider component
  • src/lib/theme/ThemeToggle.svelte – UI toggle component
  • src/lib/theme/accessibility.svelte.ts(NEW) a11y utilities & announcements

2) Runtime dynamic theme patterns

Five focused patterns demonstrating when runtime theming is needed:

  1. User-customizable accent color
  2. API-driven brand themes
  3. Runtime palette generation
  4. Third-party library integration (chart colors)
  5. White-label multi-tenant branding

Main entry point: src/routes/+page.svelte

3) Production utilities (NEW)

Additional modules for production-ready applications:

  • color-utils.ts – Color math with validation, WCAG calculations, memoized functions
  • accessibility.svelte.ts – Motion/contrast preferences, screen reader announcements
  • performance.ts – Memoization, debounce, throttle, RAF utilities
  • config.ts – Environment-based configuration, API integration helpers
  • dynamic-colors.svelte.ts – Enhanced with input validation & error handling
  • tenant-theme.svelte.ts – Enhanced with data validation & rollback logic

Following the Article vs Using Production Features

If you're following the article...

Core APIs work exactly as described – no breaking changes!

The article teaches the fundamental patterns. You can use the code as-is:

// Article pattern - still works perfectly
import { getThemeContext } from '$lib/theme/theme-context.svelte';
const theme = getThemeContext();
theme.toggle();

All production features are opt-in additions that don't affect the core patterns.

If you're building for production...

Add validation, accessibility, and performance

Enhance with production features when needed:

// Add color validation (prevents CSS injection)
import { isValidHexColor } from '$lib/theme/color-utils';
if (isValidHexColor(userColor)) {
    applyDynamicColors({ accent: userColor });
}

// Check WCAG contrast (accessibility compliance)
import { meetsWCAGContrast } from '$lib/theme/color-utils';
const isAccessible = meetsWCAGContrast(foreground, background, { level: 'AA' });

// Use memoized functions (performance optimization)
import { memoized } from '$lib/theme/color-utils';
const hsl = memoized.hexToHsl(color); // Cached result

Full production guide: PRODUCTION.md Complete API reference: IMPROVEMENTS.md

Why this project exists

Most theming guides stop at dark mode toggles. This demo covers the real-world 5% where CSS-only breaks down:

  • Colors chosen by users
  • Colors fetched from APIs/CMS
  • Colors computed at runtime
  • Libraries that require resolved JS color strings
  • Tenant-specific branding in multi-tenant SaaS

Tech stack

  • Svelte 5 (runes: $state, $derived, $effect)
  • SvelteKit 2 (SSR, hooks, routing)
  • TypeScript 5 (strict mode enabled)
  • Vite 7
  • lucide-svelte (icons)

Getting started

Prerequisites

  • Node.js 20+
  • pnpm 9+

Install dependencies

pnpm install

Start development server

pnpm dev

Then open the local URL shown in the terminal.

Build for production

pnpm build
pnpm preview

Useful scripts

  • pnpm dev – run local dev server
  • pnpm build – create production build
  • pnpm preview – preview production build
  • pnpm check – type + Svelte checks
  • pnpm lint – ESLint + Prettier checks
  • pnpm format – format code

Project structure

src/
├── routes/
│   ├── +page.svelte                      # Main demo page (all 5 patterns)
│   └── +layout.svelte                    # Root layout with ThemeProvider
├── lib/
│   ├── theme/
│   │   ├── theme-context.svelte.ts       # Theme state management
│   │   ├── ThemeProvider.svelte          # Context provider component
│   │   ├── ThemeToggle.svelte            # Theme switcher UI
│   │   ├── types.ts                      # TypeScript type definitions
│   │   ├── color-utils.ts                # Color math, validation, WCAG (ENHANCED)
│   │   ├── dynamic-colors.svelte.ts      # Dynamic color API (ENHANCED)
│   │   ├── brand-theme.svelte.ts         # Pattern 2: API-driven brands
│   │   ├── color-palette.svelte.ts       # Pattern 3: Palette generation
│   │   ├── chart-theme.svelte.ts         # Pattern 4: Chart library integration
│   │   ├── tenant-theme.svelte.ts        # Pattern 5: Multi-tenant (ENHANCED)
│   │   ├── accessibility.svelte.ts       # NEW: A11y utilities
│   │   ├── performance.ts                # NEW: Perf optimizations
│   │   └── config.ts                     # NEW: Environment config
│   └── components/
│       ├── BrandThemeDemo.svelte         # Pattern 2 demo component
│       ├── ColorCustomizer.svelte        # Pattern 1 demo component
│       ├── PaletteGenerator.svelte       # Pattern 3 demo component
│       ├── ThemeAwareChart.svelte        # Pattern 4 demo component
│       └── TenantThemeDemo.svelte        # Pattern 5 demo component
├── app.css                               # Global styles + CSS variables
└── hooks.server.ts                       # SSR theme handling

.env.example                              # NEW: Environment variable template
PRODUCTION.md                             # NEW: Deployment guide
IMPROVEMENTS.md                           # NEW: Enhancement documentation

Key changes from article:

  • (ENHANCED) marks files with production improvements
  • (NEW) marks files added beyond the article
  • Core patterns remain unchanged in demo components

Documentation

Read the companion article "Dynamic Theme Customization at Runtime" on The Hackpile Chronicle for in-depth explanation of the core patterns and concepts.

Additional Guides

  • PRODUCTION.md – Complete production deployment guide

    • Security checklist & hardening
    • Accessibility implementation (WCAG 2.1)
    • Performance optimization strategies
    • API integration patterns
    • Environment configuration
    • Testing & monitoring
    • Troubleshooting guide
  • IMPROVEMENTS.md – Enhancement documentation

    • Complete API reference for new features
    • Code examples for validation, WCAG, performance
    • Migration guide from article version
    • Backward compatibility notes
  • .env.example – Environment configuration template

    • All available environment variables
    • Development vs production examples

Quick Start Examples

Basic theme switching (from article)

import { getThemeContext } from '$lib/theme/theme-context.svelte';

const theme = getThemeContext();
theme.toggle(); // Switch between light/dark
theme.setPreference('dark'); // Explicit theme

With validation (production)

import { applyDynamicColors } from '$lib/theme/dynamic-colors.svelte';
import { isValidHexColor } from '$lib/theme/color-utils';

function applyUserColor(color: string) {
    if (!isValidHexColor(color)) {
        console.error('Invalid color format');
        return;
    }
    applyDynamicColors({ accent: color });
}

With WCAG validation (production)

import { meetsWCAGContrast, findAccessibleColor } from '$lib/theme/color-utils';

function ensureAccessibleColors(fg: string, bg: string) {
    if (!meetsWCAGContrast(fg, bg, { level: 'AA', size: 'normal' })) {
        // Auto-fix to meet WCAG AA
        fg = findAccessibleColor(fg, bg, { level: 'AA' });
    }
    return { foreground: fg, background: bg };
}

With performance (production)

import { memoized } from '$lib/theme/color-utils';
import { debounce } from '$lib/theme/performance';

// Memoized color calculations
const colors = palette.map((c) => memoized.hexToHsl(c));

// Debounced user input
const debouncedApply = debounce(applyColors, 300);

Contributing

This is a demo project accompanying an article. Feel free to fork and adapt for your needs!

If you find issues with the production enhancements or have suggestions, please open an issue.

License

MIT

Top categories

Loading Svelte Themes