added dark/light mode and control
This commit is contained in:
@@ -24,7 +24,7 @@ export default function Footer() {
|
||||
>
|
||||
<div style={{ display: "flex", gap: 16 }}>
|
||||
<span>
|
||||
(2026) - Hunter W.
|
||||
(2026) - Hunter W. - Powered by Next.JS
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 16 }}>
|
||||
|
||||
95
components/ThemeSwitch.tsx
Normal file
95
components/ThemeSwitch.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
|
||||
const themes = ['system', 'light', 'dark'] as const
|
||||
|
||||
const ThemeSwitch = () => {
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
if (!mounted) return null
|
||||
|
||||
const next = () => {
|
||||
const i = themes.indexOf(theme as (typeof themes)[number])
|
||||
setTheme(themes[(i + 1) % themes.length])
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={next}
|
||||
aria-label={`Theme: ${theme}`}
|
||||
title={`Theme: ${theme}`}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 'var(--space-4)',
|
||||
right: 'var(--space-4)',
|
||||
zIndex: 9999,
|
||||
background: 'var(--surface)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--radius-md)',
|
||||
color: 'var(--fg-secondary)',
|
||||
cursor: 'pointer',
|
||||
padding: '8px',
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
transition: `background var(--duration-fast) var(--ease-out),
|
||||
border-color var(--duration-fast) var(--ease-out),
|
||||
color var(--duration-fast) var(--ease-out)`,
|
||||
}}
|
||||
onMouseEnter={e => {
|
||||
e.currentTarget.style.background = 'var(--surface-hover)'
|
||||
e.currentTarget.style.borderColor = 'var(--border-strong)'
|
||||
e.currentTarget.style.color = 'var(--fg)'
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
e.currentTarget.style.background = 'var(--surface)'
|
||||
e.currentTarget.style.borderColor = 'var(--border)'
|
||||
e.currentTarget.style.color = 'var(--fg-secondary)'
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.75"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
{theme === 'dark' ? (
|
||||
// Moon
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3a7 7 0 0 0 9.79 9.79z" />
|
||||
) : theme === 'light' ? (
|
||||
// Sun
|
||||
<>
|
||||
<circle cx="12" cy="12" r="5" />
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</>
|
||||
) : (
|
||||
// Monitor (system)
|
||||
<>
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
|
||||
<line x1="8" y1="21" x2="16" y2="21" />
|
||||
<line x1="12" y1="17" x2="12" y2="21" />
|
||||
</>
|
||||
)}
|
||||
</svg>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default ThemeSwitch
|
||||
Reference in New Issue
Block a user