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 withdata-theme-switcher
attribute - Path Resolution: Uses Docusaurus
useBaseUrl
for consistent paths across all routes - Persistence: Selected theme is saved in
localStorage
asdocusaurus-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 withdata-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
nottheme1
- 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