- Next.js 14 blog application with theme support - Docker containerization with volume bindings - Traefik integration with Let's Encrypt SSL - MDX support for blog posts - Theme toggle with localStorage persistence - Scheduled posts directory structure - Brand guidelines compliance with CHORUS colors 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
58 lines
2.2 KiB
TypeScript
58 lines
2.2 KiB
TypeScript
'use client'
|
|
|
|
import React, { useState, useEffect } from 'react'
|
|
|
|
export default function ThemeToggle() {
|
|
const [theme, setTheme] = useState<'light' | 'dark'>('dark')
|
|
|
|
useEffect(() => {
|
|
// Check for saved theme preference or default to dark
|
|
const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null
|
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
const initialTheme = savedTheme || (prefersDark ? 'dark' : 'light')
|
|
|
|
setTheme(initialTheme)
|
|
|
|
// Apply theme to document
|
|
if (initialTheme === 'dark') {
|
|
document.documentElement.classList.add('dark')
|
|
} else {
|
|
document.documentElement.classList.remove('dark')
|
|
}
|
|
}, [])
|
|
|
|
const toggleTheme = () => {
|
|
const newTheme = theme === 'dark' ? 'light' : 'dark'
|
|
setTheme(newTheme)
|
|
|
|
// Save to localStorage
|
|
localStorage.setItem('theme', newTheme)
|
|
|
|
// Apply to document
|
|
if (newTheme === 'dark') {
|
|
document.documentElement.classList.add('dark')
|
|
} else {
|
|
document.documentElement.classList.remove('dark')
|
|
}
|
|
}
|
|
|
|
return (
|
|
<button
|
|
onClick={toggleTheme}
|
|
className="p-chorus-sm hover:bg-black/10 dark:hover:bg-white/10 transition-colors rounded-lg backdrop-blur-sm bg-white/20 dark:bg-black/20 border border-white/30 dark:border-white/20"
|
|
aria-label="Toggle theme"
|
|
>
|
|
{theme === 'dark' ? (
|
|
// Sun icon for switching to light mode
|
|
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
|
|
</svg>
|
|
) : (
|
|
// Moon icon for switching to dark mode
|
|
<svg className="w-5 h-5 text-carbon-950" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
|
</svg>
|
|
)}
|
|
</button>
|
|
)
|
|
} |