Theming Overview
Understanding @duskmoon-dev/core's Material Design 3 theme system
Theming Overview
@duskmoon-dev/core implements Material Design 3’s dynamic color system, providing a comprehensive theming solution that’s both powerful and easy to customize.
What is Material Design 3?
Material Design 3 (MD3) is Google’s latest design system that emphasizes personalization, accessibility, and dynamic color. It introduces several key concepts:
- Dynamic Color: A color system that adapts to user preferences
- Surface Elevation: Five levels of surface containers for depth hierarchy
- Tertiary Colors: A third brand color for more design flexibility
- Semantic Colors: Dedicated colors for info, success, warning, and error states
Built-in Themes
@duskmoon-dev/core includes two carefully crafted themes. Full token values are listed in the Themes reference.
- Themes reference - Sunshine and moonlight tokens
Sunshine Theme (Light)
A warm, energetic light theme perfect for daytime use:
- Primary: Amber (warm orange-yellow)
- Secondary: Blue
- Tertiary: Teal
- Surface: Light backgrounds with subtle elevation
<html data-theme="sunshine">
<!-- Your app -->
</html>
Moonlight Theme (Dark)
A cool, sophisticated dark theme ideal for low-light environments:
- Primary: Blue
- Secondary: Purple
- Tertiary: Cyan
- Surface: Dark backgrounds with subtle elevation
<html data-theme="moonlight">
<!-- Your app -->
</html>
Color System
Color Roles
Material Design 3 defines specific roles for colors:
Primary Colors
The main brand color used for key components:
--color-primary /* Primary brand color */
--color-primary-focus /* Hover/focus state */
--color-primary-content /* Text on primary */
--color-primary-container /* Container background */
--color-on-primary-container /* Text on container */
Secondary Colors
Accent color that complements primary:
--color-secondary /* Secondary brand color */
--color-secondary-focus /* Hover/focus state */
--color-secondary-content /* Text on secondary */
--color-secondary-container /* Container background */
--color-on-secondary-container /* Text on container */
Tertiary Colors
Third brand color for additional variety (unique to @duskmoon-dev/core):
--color-tertiary /* Tertiary brand color */
--color-tertiary-focus /* Hover/focus state */
--color-tertiary-content /* Text on tertiary */
--color-tertiary-container /* Container background */
--color-on-tertiary-container /* Text on container */
Surface Colors
Background and surface colors with five elevation levels:
--color-surface /* Base surface */
--color-surface-dim /* Dimmed surface */
--color-surface-bright /* Bright surface */
--color-surface-container-lowest /* Level 0 */
--color-surface-container-low /* Level 1 */
--color-surface-container /* Level 2 */
--color-surface-container-high /* Level 3 */
--color-surface-container-highest/* Level 4 */
--color-on-surface /* Text on surface */
--color-on-surface-variant /* Secondary text */
--color-surface-variant /* Alternative surface */
Semantic Colors
Colors for specific states and feedback:
/* Info (Blue) */
--color-info
--color-on-info
--color-info-container
--color-on-info-container
/* Success (Green) */
--color-success
--color-on-success
--color-success-container
--color-on-success-container
/* Warning (Yellow) */
--color-warning
--color-on-warning
--color-warning-container
--color-on-warning-container
/* Error (Red) */
--color-error
--color-on-error
--color-error-container
--color-on-error-container
Using Theme Colors
In HTML Classes
Use Tailwind’s utility classes with theme colors:
<!-- Backgrounds -->
<div class="bg-primary">Primary background</div>
<div class="bg-surface-container">Surface container</div>
<!-- Text -->
<p class="text-primary">Primary text</p>
<p class="text-on-surface">Surface text</p>
<!-- Borders -->
<div class="border border-outline">Bordered element</div>
In Custom CSS
Reference theme CSS variables directly:
.custom-button {
background-color: hsl(var(--color-primary));
color: hsl(var(--color-primary-content));
border: 1px solid hsl(var(--color-outline));
}
/* With opacity */
.custom-overlay {
background-color: color-mix(in srgb, hsl(var(--color-surface)) 80%, transparent);
}
In Tailwind Arbitrary Values
Use theme colors with Tailwind’s arbitrary value syntax:
<div class="bg-[hsl(var(--color-primary))]">
Custom background
</div>
<div class="text-[hsl(var(--color-tertiary))]">
Custom text color
</div>
Theme Switching
Basic Theme Toggle
Implement theme switching with JavaScript:
<button id="theme-toggle">Toggle Theme</button>
<script>
const toggle = document.getElementById('theme-toggle');
toggle.addEventListener('click', () => {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'sunshine' ? 'moonlight' : 'sunshine';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
});
// Restore saved theme
const savedTheme = localStorage.getItem('theme') || 'sunshine';
document.documentElement.setAttribute('data-theme', savedTheme);
</script>
React Theme Toggle
import { useEffect, useState } from 'react';
export function ThemeToggle() {
const [theme, setTheme] = useState<'sunshine' | 'moonlight'>('sunshine');
useEffect(() => {
const saved = localStorage.getItem('theme') as 'sunshine' | 'moonlight';
if (saved) {
setTheme(saved);
document.documentElement.setAttribute('data-theme', saved);
}
}, []);
const toggleTheme = () => {
const newTheme = theme === 'sunshine' ? 'moonlight' : 'sunshine';
setTheme(newTheme);
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
};
return (
<button onClick={toggleTheme} className="btn btn-outlined">
{theme === 'sunshine' ? '🌙' : '☀️'}
{theme === 'sunshine' ? 'Dark Mode' : 'Light Mode'}
</button>
);
}
Vue Theme Toggle
<template>
<button @click="toggleTheme" class="btn btn-outlined">
{{ theme === 'sunshine' ? '🌙 Dark Mode' : '☀️ Light Mode' }}
</button>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const theme = ref('sunshine');
onMounted(() => {
const saved = localStorage.getItem('theme') || 'sunshine';
theme.value = saved;
document.documentElement.setAttribute('data-theme', saved);
});
const toggleTheme = () => {
theme.value = theme.value === 'sunshine' ? 'moonlight' : 'sunshine';
document.documentElement.setAttribute('data-theme', theme.value);
localStorage.setItem('theme', theme.value);
};
</script>
Surface Elevation
Material Design 3 uses five elevation levels for surface containers:
<!-- Level 0 - Lowest -->
<div class="card card-lowest">Lowest elevation</div>
<!-- Level 1 - Low -->
<div class="card card-low">Low elevation</div>
<!-- Level 2 - Default -->
<div class="card card-default">Default elevation</div>
<!-- Level 3 - High -->
<div class="card card-high">High elevation</div>
<!-- Level 4 - Highest -->
<div class="card card-highest">Highest elevation</div>
Use elevation to create visual hierarchy:
- Lowest/Low: Background elements, less important content
- Default: Standard UI elements, cards
- High/Highest: Important dialogs, popovers, tooltips
Color Contrast
@duskmoon-dev/core ensures proper color contrast automatically:
<!-- Light text on dark background -->
<div class="bg-primary text-primary-content">
Automatically readable
</div>
<!-- Dark text on light background -->
<div class="bg-surface text-on-surface">
Automatically readable
</div>
All color combinations meet WCAG AA standards for accessibility.
HSL Color Format
@duskmoon-dev/core uses HSL (Hue, Saturation, Lightness) color format:
/* Format: hue saturation% lightness% */
--color-primary: 38 92% 50%;
This format allows Tailwind to apply opacity utilities:
<div class="bg-primary bg-opacity-50"> <!-- 50% opacity -->
<div class="bg-primary bg-opacity-75"> <!-- 75% opacity -->
<div class="bg-primary bg-opacity-100"> <!-- 100% opacity (default) -->
Theme Scope
Themes can be scoped to specific sections:
<!-- Global theme -->
<html data-theme="sunshine">
<body>
<!-- Most of the app uses sunshine theme -->
<!-- Scoped dark theme section -->
<section data-theme="moonlight">
This section uses the moonlight theme
</section>
</body>
</html>
Best Practices
Consistent Color Usage
Use color roles consistently throughout your app:
- Primary: Main actions (submit buttons, primary CTAs)
- Secondary: Secondary actions, alternative paths
- Tertiary: Accent elements, decorative elements
- Semantic: Feedback and status indicators
Respect User Preferences
Detect and respect system theme preferences:
// Check system preference
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const defaultTheme = prefersDark ? 'moonlight' : 'sunshine';
// Also listen for changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
const newTheme = e.matches ? 'moonlight' : 'sunshine';
document.documentElement.setAttribute('data-theme', newTheme);
});
Test Both Themes
Always test your UI in both light and dark themes to ensure:
- Sufficient contrast
- Consistent visual hierarchy
- Proper color semantics
Avoid Hardcoded Colors
Use theme variables instead of hardcoded colors:
<!-- Good: Uses theme colors -->
<div class="bg-primary text-primary-content">
Theme-aware button
</div>
<!-- Avoid: Hardcoded colors -->
<div class="bg-blue-500 text-white">
Will not adapt to theme
</div>
Next Steps
- Theme Customization - Adjust sunshine and moonlight
- API Reference - Complete token reference
- Configuration - Plugin configuration options