Skip to main content
SubZeroDev

Professional uncertainty since 2026.

Wellโ€ฆ Why not?


Theme System

This template includes a sophisticated theme switching system with 10+ professionally designed color themes that are dynamically loaded and persisted in localStorage. Version 1.0 includes significant improvements for path resolution and cross-route compatibility.

๐Ÿšจ Breaking Changes in v1.0โ€‹

Path Resolution Fixedโ€‹

  • Fixed: Theme switcher now works correctly on all routes (/, /docs/template, etc.)
  • Change: Uses Docusaurus useBaseUrl hook for proper path resolution
  • Migration: No changes needed for existing themes - paths are automatically resolved

Configuration Architectureโ€‹

  • New: Centralized configuration in config/globalConfig.yml
  • Change: Theme data now stored in src/themes.json (auto-generated)
  • Change: TypeScript entities moved to src/entities/

Available Themesโ€‹

  • Default/Elegant - Refined green with glassmorphism effects
  • Ocean Blue - Professional blue tones with modern contrast
  • Sunset - Warm orange and red gradients (default theme)
  • Purple Night - Deep purple for dark environments
  • Forest - Natural green shades with earthy tones
  • Agent - Dark theme optimized for development
  • Material Design Themes - Five variants following Material Design:
    • Material Red - Bold red with proper contrast ratios
    • Material Indigo - Deep indigo for professional applications
    • Material Teal - Calming teal for content-focused sites
    • Material Amber - Warm amber for creative projects
    • Material Pink - Subtle pink for modern aesthetics
  • Nuke - Minimal dark theme for distraction-free reading

Implementation Architectureโ€‹

Component Architecture (v1.0)โ€‹

// ThemeSwitcher.tsx - New implementation
import useBaseUrl from '@docusaurus/useBaseUrl';

const ThemeSwitcher: React.FC = () => {
// Pre-compute resolved URLs for all themes
const themesWithResolvedUrls = themes.map((theme) => ({
...theme,
resolvedCssUrl: useBaseUrl(theme.cssFile)
}));

// Theme application with resolved URLs
const applyTheme = (themeName: string) => {
const theme = themesWithResolvedUrls.find((t) => t.name === themeName);
if (!theme) return;

// Create link with resolved URL
link.href = theme.resolvedCssUrl;
};
};

Path Resolution Systemโ€‹

Before v1.0 (Problematic):

// โŒ Relative paths failed on sub-routes
cssFile: 'themes/blue.css'; // Failed on /docs/template

v1.0 (Fixed):

// โœ… useBaseUrl resolves paths correctly on all routes
resolvedCssUrl: useBaseUrl(theme.cssFile); // Works everywhere

Data Structureโ€‹

Theme configuration is now stored in src/themes.json (auto-generated):

{
"themes": [
{
"name": "sunset",
"displayName": "Sunset",
"cssFile": "themes/sunset.css"
}
],
"defaultTheme": "sunset"
}

Configurationโ€‹

Default Theme Settingโ€‹

Configure the default theme in config/globalConfig.yml:

# config/globalConfig.yml
preBuild:
defaultTheme: blue
# Theme configuration is auto-generated in data/themes.json

Theme Files Locationโ€‹

Theme files remain in static/themes/ for direct access:

static/themes/
โ”œโ”€โ”€ sunset.css # Default theme
โ”œโ”€โ”€ blue.css # Ocean Blue theme
โ”œโ”€โ”€ purple.css # Purple night theme
โ”œโ”€โ”€ forest.css # Forest green theme
โ”œโ”€โ”€ material-*.css # Material Design variants
โ”œโ”€โ”€ nuke.css # Minimal dark theme
โ””โ”€โ”€ default.css # Fallback theme

How Theme Switching Worksโ€‹

The ThemeSwitcher component (src/components/ThemeSwitcher/) provides:

  • Dynamic Loading: CSS files are injected via <link> tags with data-theme-switcher attribute
  • Path Resolution: Uses Docusaurus useBaseUrl for consistent paths across all routes
  • Persistence: Selected theme is saved in localStorage as docusaurus-theme-color
  • Default Fallback: Uses configured default theme from globalConfig.yml
  • URL Access: Theme files are accessible via resolved URLs on all routes
  • No Build Process: Themes load instantly without requiring rebuilds

Adding a New Theme (v1.0)โ€‹

To create a custom theme, follow these updated steps:

1. Create Theme CSS Fileโ€‹

Create a new {name}.css file in static/themes/ directory with theme metadata:

/* static/themes/mytheme.css */
/* @theme-id: mytheme */
/* @theme-name: My Custom Theme */

:root {
--ifm-color-primary: #your-primary-color;
--ifm-color-primary-dark: #your-dark-variant;
--ifm-color-primary-light: #your-light-variant;
--ifm-color-primary-lightest: #your-lightest-variant;
--ifm-color-primary-darker: #your-darker-variant;
--ifm-color-primary-darkest: #your-darkest-variant;

/* Add more custom properties as needed */
--ifm-background-surface-color: #your-background;
--ifm-color-emphasis-100: #your-subtle-color;
}

2. Run Pre-Build Scriptโ€‹

The theme will be automatically detected when you run the pre-build process:

# Run pre-build to regenerate theme configuration
npm run prestart
# or
tsx ./scripts/pre-build.ts

3. Add Color Preview (Optional)โ€‹

Add corresponding color preview styles in src/components/ThemeSwitcher/ThemeSwitcher.css:

.theme-switcher__color-preview--mytheme {
background: linear-gradient(
135deg,
#your-primary-color,
#your-secondary-color
);
}

That's it! Your theme will automatically appear in the theme switcher dropdown after the pre-build process detects it.

v1.0 Architecture Overviewโ€‹

Component Structureโ€‹

src/components/ThemeSwitcher/
โ”œโ”€โ”€ ThemeSwitcher.tsx # Main component with useBaseUrl integration
โ”œโ”€โ”€ ThemeSwitcher.css # Styling and color previews
โ””โ”€โ”€ index.ts # Export barrel

src/config/
โ””โ”€โ”€ globalConfig.yml # Theme configuration

src/entities/
โ”œโ”€โ”€ theme.ts # Theme TypeScript interface
โ””โ”€โ”€ navbarLink.ts # Navbar link interface

Auto-generated:
โ”œโ”€โ”€ src/themes.json # Theme data (auto-generated)
โ””โ”€โ”€ src/navbarLinks.json # Navbar data (auto-generated)

v1.0 Technical Implementationโ€‹

  • Path Resolution: Uses Docusaurus useBaseUrl hook for consistent URL resolution across all routes
  • Pre-computed URLs: Theme URLs are resolved once at component initialization for better performance
  • Dynamic CSS Injection: Themes are loaded by creating <link> elements with data-theme-switcher attribute
  • Persistence: Uses localStorage.setItem('docusaurus-theme-color', theme)
  • Cleanup: Previous theme links are removed before injecting new ones
  • Auto-detection: New themes are automatically detected by the pre-build script
  • Type Safety: Full TypeScript support with proper interfaces

Data Flow (v1.0)โ€‹

// 1. Theme data loaded from auto-generated JSON
import themeData from './themes.json';

// 2. URLs resolved using Docusaurus hook
const themesWithResolvedUrls = themes.map((theme) => ({
...theme,
resolvedCssUrl: useBaseUrl(theme.cssFile) // โ† Key improvement
}));

// 3. Theme applied with resolved URL
link.href = theme.resolvedCssUrl; // โ† Works on all routes

Integration with Docusaurusโ€‹

The theme system integrates seamlessly with Docusaurus v3.8.1:

  • Uses standard CSS custom properties (--ifm-* variables)
  • Compatible with light/dark mode switching
  • Respects Docusaurus color mode preferences
  • Works with all Docusaurus UI components
  • New: Proper baseURL handling for deployments

Theme Development Best Practicesโ€‹

v1.0 Theme Metadataโ€‹

Include metadata comments in your CSS files for auto-detection:

/* @theme-id: mytheme */
/* @theme-name: My Beautiful Theme */

:root {
/* Your theme styles here */
}

Color Systemโ€‹

Follow Docusaurus color conventions:

/* Primary colors (required) */
--ifm-color-primary: #main-brand-color;
--ifm-color-primary-dark: #darker-variant;
--ifm-color-primary-light: #lighter-variant;

/* Extended palette (recommended) */
--ifm-color-primary-lightest: #lightest-variant;
--ifm-color-primary-darker: #darker-variant;
--ifm-color-primary-darkest: #darkest-variant;

/* Semantic colors (optional) */
--ifm-color-success: #success-color;
--ifm-color-warning: #warning-color;
--ifm-color-danger: #danger-color;
--ifm-color-info: #info-color;

Accessibility Guidelinesโ€‹

  • Maintain WCAG AA contrast ratios (4.5:1 for normal text)
  • Test themes in both light and dark modes
  • Ensure sufficient contrast for links and interactive elements
  • Validate color combinations with accessibility tools

Naming Conventionโ€‹

  • Use descriptive theme names: ocean-blue not theme1
  • Follow kebab-case for file names: material-indigo.css
  • Use clear display names: "Material Design Indigo" not "Mat Ind"

Default Theme Customizationโ€‹

Edit src/css/custom.css to customize the default theme (used when no theme is selected):

:root {
--ifm-color-primary: #2d7d54; /* Primary brand color */
--ifm-color-primary-dark: #256749; /* Darker variant */
--ifm-color-primary-light: #359962; /* Lighter variant */

/* Custom additions */
--ifm-font-family-base: 'Inter', system-ui, -apple-system, sans-serif;
--ifm-heading-font-weight: 600;
--ifm-line-height-base: 1.6;
}

CSS Theme Variantsโ€‹

The template provides two main CSS approaches:

Standard Docusaurus Stylingโ€‹

  • File: src/css/custom.css
  • Approach: Builds on Docusaurus default styles
  • Best for: Most documentation sites and standard use cases
  • Features: Maintains Docusaurus UI consistency

Minimal/Clean Stylingโ€‹

  • File: src/css/custom.nuke.css
  • Approach: Comprehensive CSS reset with minimal styling
  • Best for: Content-focused sites requiring maximum customization
  • Features: Clean slate for custom designs

To switch between approaches, update your docusaurus.config.ts:

// Standard approach
theme: {
customCss: './src/css/custom.css',
}

// Minimal approach
theme: {
customCss: './src/css/custom.nuke.css',
}

Theme Debuggingโ€‹

Inspecting Current Themeโ€‹

// In browser console
localStorage.getItem('docusaurus-theme-color'); // Current theme
document.querySelector('[data-theme-switcher]')?.href; // Current CSS file

Theme Development Toolsโ€‹

  • Use browser DevTools to test color combinations
  • Validate accessibility with tools like axe-DevTools
  • Test responsive behavior across device sizes
  • Check theme persistence across page reloads

Browser Supportโ€‹

  • โœ… All modern browsers supporting CSS custom properties
  • โœ… localStorage for theme persistence
  • โœ… Dynamic <link> tag injection
  • โœ… Graceful fallback to default theme