Overview
The portfolio implements a sophisticated dark mode system using Tailwind CSS’s class-based approach. It features:- Instant theme switching with no flash
- Persistent theme preference (localStorage)
- System preference detection
- Smooth transitions between themes
- Accessible theme toggle button
How Dark Mode Works
Theme Toggle Component
The theme toggle is implemented insrc/components/ThemeToggle.astro:
src/components/ThemeToggle.astro
- Toggle switches between
.darkclass on<html> - Icons automatically swap using
dark:hiddenanddark:block - Theme preference saved to localStorage
- Accessible with proper
aria-label
Theme Initialization
Theme initialization happens insrc/layouts/Layout.astro BEFORE the page renders:
src/layouts/Layout.astro
is:inline- Script runs immediately (not bundled)- Executes before first paint - No flash of wrong theme
- Checks localStorage first - Respects user preference
- Falls back to system preference - Good default behavior
The
is:inline directive is critical. Without it, Astro bundles the script and it runs too late, causing a flash of the wrong theme.Tailwind Configuration
Dark mode is configured intailwind.config.mjs:
tailwind.config.mjs
'class'- Dark mode toggled by.darkclass (recommended)'media'- Dark mode based on system preference onlyfalse- Dark mode disabled
Using Dark Mode in Components
Basic Usage
Tailwind’sdark: variant applies styles only in dark mode:
Common Patterns
Customizing Dark Mode Colors
Global Background and Text
The base colors are set in the Layout component:src/layouts/Layout.astro
Choose your color palette
Decide on light and dark background colors. Use tools like:
Test contrast
Verify text is readable on your backgrounds using WebAIM Contrast Checker.
Component-Specific Colors
Update individual components by modifying theirdark: classes:
src/components/Hero.astro
Custom Color Definitions
Define custom dark mode colors in Tailwind config:tailwind.config.mjs
Adding Dark Mode to New Components
When creating new components, follow this checklist:1. Backgrounds
1. Backgrounds
Every background should have a dark variant:
2. Text
2. Text
All text should be readable in both modes:
3. Borders
3. Borders
Borders need sufficient contrast:
4. Shadows
4. Shadows
Shadows should be visible or removed:
5. Icons and SVGs
5. Icons and SVGs
Icons need appropriate colors:
6. Interactive States
6. Interactive States
Hover, focus, and active states:
Advanced Dark Mode Patterns
Gradients
Create beautiful gradients that work in both themes:Images and Media
Handle images that may not look good in dark mode:- CSS Filter
- Separate Images
- Opacity Overlay
Transitions
Add smooth transitions when switching themes:Theme Persistence
How It Works
The theme is stored inlocalStorage:
Respecting System Preferences
The initialization script checks system preference:Enhanced Theme Toggle
Three-State Toggle
Implement Light / Auto / Dark toggle:ThemeToggleEnhanced.astro
Testing Dark Mode
Manual testing
Toggle between themes and check:
- All text is readable
- No white/dark boxes that shouldn’t be there
- Images look good (or have dark variants)
- Interactive elements are visible
Browser DevTools
Use Chrome DevTools:
- Open DevTools (F12)
- Cmd/Ctrl + Shift + P
- Type “Render”
- Select “Emulate CSS prefers-color-scheme: dark”
Accessibility check
Verify contrast ratios:
- WebAIM Contrast Checker
- Chrome DevTools Lighthouse audit
- WCAG AAA requires 7:1 ratio
Common Issues
Flash of wrong theme on page load
Flash of wrong theme on page load
Cause: Theme initialization script runs too late.Solution: Ensure script has
is:inline attribute and is in <head> before any content.Theme doesn't persist between pages
Theme doesn't persist between pages
Cause: localStorage not being saved.Solution: Check browser console for errors. Ensure localStorage is accessible (not disabled or in private mode).
Some elements don't change color
Some elements don't change color
Cause: Missing
dark: variants on those elements.Solution: Add appropriate dark: classes to all visible elements.Transitions look janky
Transitions look janky
Cause: Too many elements transitioning at once.Solution: Use
transition-colors instead of transition-all, or reduce transition duration.Best Practices
- Consistent color scale: Use the same color families (e.g., slate) across light and dark modes
- Sufficient contrast: Ensure WCAG AA minimum (4.5:1 for text)
- Test both modes: Design and develop with both themes visible
- Progressive enhancement: Site should work even if JavaScript fails
- Respect user choice: Don’t override system preferences without good reason
- Smooth transitions: Use
transition-colorsfor color changes - Accessible toggle: Include proper ARIA labels and keyboard support
Next Steps
Customization
Customize more aspects of your portfolio
Deployment
Deploy your themed portfolio to production