feat: site-wide dark mode (Tailwind class strategy); ThemeToggle island in header; persist preference; adjust brand-surface for dark
This commit is contained in:
42
src/components/ThemeToggle.jsx
Normal file
42
src/components/ThemeToggle.jsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function ThemeToggle() {
|
||||||
|
const [isDark, setIsDark] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const stored = localStorage.getItem('omoluabi_theme');
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
const initial = stored ? stored === 'dark' : prefersDark;
|
||||||
|
setIsDark(initial);
|
||||||
|
document.documentElement.classList.toggle('dark', initial);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
const next = !isDark;
|
||||||
|
setIsDark(next);
|
||||||
|
document.documentElement.classList.toggle('dark', next);
|
||||||
|
localStorage.setItem('omoluabi_theme', next ? 'dark' : 'light');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={toggle}
|
||||||
|
className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||||||
|
aria-label="Toggle color theme"
|
||||||
|
>
|
||||||
|
{isDark ? (
|
||||||
|
<>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24"><path d="M21.64 13a1 1 0 0 0-1.05-.14 8 8 0 1 1-9.45-9.45 1 1 0 0 0-.14-2A10 10 0 1 0 23 14a1 1 0 0 0-1.36-1z"/></svg>
|
||||||
|
<span className="text-sm">Dark</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24"><path d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.8 1.42-1.42zm10.45 14.32l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM12 4a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm8-5h-2a1 1 0 1 1 0-2h2a1 1 0 1 1 0 2zM4 13H2a1 1 0 1 1 0-2h2a1 1 0 1 1 0 2zm2.76 6.36l-1.42 1.42-1.79-1.8 1.41-1.41 1.8 1.79zM19.78 4.46l-1.41-1.41-1.8 1.79 1.41 1.41 1.8-1.79z"/></svg>
|
||||||
|
<span className="text-sm">Light</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@@ -2,6 +2,7 @@
|
|||||||
// src/layouts/BaseLayout.astro
|
// src/layouts/BaseLayout.astro
|
||||||
import '../styles/global.css';
|
import '../styles/global.css';
|
||||||
import '../styles/main.css';
|
import '../styles/main.css';
|
||||||
|
import ThemeToggle from '../components/ThemeToggle.jsx';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -55,8 +56,11 @@ const { title = "Omoluabi Association Netherlands", description = "Preserving Ni
|
|||||||
<a href="/contact" class="text-gray-700 hover:text-nigerian-green-600 transition-colors">Contact</a>
|
<a href="/contact" class="text-gray-700 hover:text-nigerian-green-600 transition-colors">Contact</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CTA Buttons -->
|
<!-- Right side controls -->
|
||||||
<div class="hidden md:flex items-center space-x-4">
|
<div class="hidden md:flex items-center space-x-4">
|
||||||
|
<astro-island>
|
||||||
|
<ThemeToggle client:load />
|
||||||
|
</astro-island>
|
||||||
<a href="/donate" class="btn bg-gradient-to-r from-ankara-red-500 to-kente-gold-500 text-white hover:shadow-lg">
|
<a href="/donate" class="btn bg-gradient-to-r from-ankara-red-500 to-kente-gold-500 text-white hover:shadow-lg">
|
||||||
❤️ Donate
|
❤️ Donate
|
||||||
</a>
|
</a>
|
||||||
|
@@ -17,6 +17,11 @@
|
|||||||
--dutch-blue: #1e4785;
|
--dutch-blue: #1e4785;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dark theme tokens */
|
||||||
|
.dark {
|
||||||
|
--dutch-white: #0b0f14;
|
||||||
|
}
|
||||||
|
|
||||||
/* Custom scrollbar */
|
/* Custom scrollbar */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
|
@@ -373,6 +373,11 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dark .brand-surface {
|
||||||
|
background: radial-gradient(1200px 600px at 20% 20%, rgba(255,255,255,0.04), rgba(255,255,255,0) 60%),
|
||||||
|
linear-gradient(135deg, rgba(6, 59, 21, 0.95) 0%, rgba(23, 37, 84, 0.95) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Combined Flag Button */
|
/* Combined Flag Button */
|
||||||
.btn-combined-flag {
|
.btn-combined-flag {
|
||||||
background: linear-gradient(135deg, var(--nigerian-green) 0%, var(--dutch-red) 33%, var(--dutch-white) 66%, var(--dutch-blue) 100%);
|
background: linear-gradient(135deg, var(--nigerian-green) 0%, var(--dutch-red) 33%, var(--dutch-white) 66%, var(--dutch-blue) 100%);
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
// tailwind.config.mjs
|
// tailwind.config.mjs
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
|
darkMode: 'class',
|
||||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
|
Reference in New Issue
Block a user