feat: add theme switching

This commit is contained in:
2025-11-12 23:13:02 +04:00
parent d4624b1729
commit 36e07ade98
5 changed files with 318 additions and 5 deletions
+15
View File
@@ -2,6 +2,21 @@
## Version 2.1.0 - Design Afrofuturisme Caribéen (2025-11-05)
### 🌓 Système de Thème Clair/Sombre
-**Switch thème** : Bouton dans la navigation pour basculer entre thème clair et sombre
-**Détection automatique** : Utilise le thème système par défaut (prefers-color-scheme)
-**Persistance** : Sauvegarde de la préférence utilisateur dans localStorage
-**Sans flash** : Script inline dans le head pour éviter le FOUC (Flash Of Unstyled Content)
-**Palette claire adaptée** : Couleurs ajustées pour une excellente lisibilité en mode clair
-**Écoute système** : Changement automatique si le thème système change (si pas de préférence utilisateur)
-**Responsive** : Bouton de thème visible et accessible sur mobile
-**Accessibilité WCAG 2 AA** : Toutes les couleurs en thème clair respectent le ratio de contraste 4.5:1
- Variables de couleurs assombries (#B87A00, #006B3D, #A01030)
- Ajustements spécifiques pour backgrounds, bordures et box-shadows
- Navigation adaptée avec fond clair (#0D0D0D sur fond clair)
- Tous les éléments décoratifs (tags, cards, boutons) optimisés
- Section contact avec liens visibles en mode clair
### 🎨 Refonte Visuelle Complète - Style Afrofuturisme Caribéen
-**Palette de couleurs enrichie** : Couleurs plus vibrantes et chaudes
- Or plus éclatant (#FDB813), vert émeraude saturé (#00D66C)
+20
View File
@@ -75,6 +75,26 @@
"nonprofitStatus": "NonprofitANBI"
}
</script>
<script>
(function() {
// Récupérer le thème stocké ou détecter le thème système
const storedTheme = localStorage.getItem('oki-theme');
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
let theme = storedTheme;
// Si pas de préférence stockée, utiliser le thème système
if (!storedTheme) {
theme = systemPrefersDark ? 'dark' : 'light';
}
// Appliquer le thème immédiatement
if (theme === 'light') {
document.documentElement.classList.add('light-theme');
}
})();
</script>
</head>
<body>
{% include "partials/nav.njk" %}
+4
View File
@@ -13,6 +13,10 @@
</li>
{% endfor %}
</ul>
<button id="theme-toggle" class="theme-toggle" aria-label="Changer de thème" title="Changer de thème">
<span class="theme-icon theme-icon-dark">🌙</span>
<span class="theme-icon theme-icon-light">☀️</span>
</button>
<div class="menu-toggle">
<span></span>
<span></span>
+188
View File
@@ -27,6 +27,35 @@
--rouge-clair: #FF6B9D; /* Rose corail */
}
/* Thème Clair */
html.light-theme,
html.light-theme body {
/* Couleurs inversées pour le mode clair */
--noir-oki: #FFF8E7; /* Fond clair */
--blanc-creme: #0D0D0D; /* Texte sombre */
/* Ajustements pour la lisibilité */
--noir-profond: #F5F0E8; /* Fond de section clair */
--gris-sombre: #E8DDD0; /* Gris clair */
/* Couleurs vives assombries pour WCAG AA (contraste 4.5:1 minimum) */
--or-oki: #B87A00; /* Or assombri pour contraste suffisant */
--vert-oki: #006B3D; /* Vert assombri */
--rouge-oki: #A01030; /* Rouge assombri */
--turquoise-caraibes: #006B75; /* Turquoise assombri */
--violet-nuit: #6B2D5C; /* Violet ok */
--bleu-ocean: #004B7F; /* Bleu assombri */
/* Couleurs dérivées plus claires pour bordures/décorations */
--or-clair: #D99000; /* Or clair pour éléments secondaires */
--vert-clair: #008C4F; /* Vert clair */
--rouge-clair: #C01545; /* Rouge clair */
/* Couleurs d'accentuation */
--jaune-soleil: #B87A00; /* Aligné avec l'or */
--orange-flamme: #C85000; /* Orange assombri */
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background:
@@ -51,6 +80,108 @@ nav {
border-bottom: 2px solid var(--or-oki);
}
html.light-theme nav {
background: rgba(255, 248, 231, 0.95);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
html.light-theme .nav-links a {
color: #0D0D0D;
}
html.light-theme .nav-links a:hover {
color: var(--or-oki);
}
/* Ajustements de contraste pour le thème clair (WCAG 2 AA) */
html.light-theme .tag {
background: rgba(184, 122, 0, 0.15);
box-shadow: 0 4px 15px rgba(184, 122, 0, 0.2);
}
html.light-theme .value-card {
background: rgba(184, 122, 0, 0.08);
border: 3px solid rgba(184, 122, 0, 0.3);
box-shadow: 0 6px 20px rgba(184, 122, 0, 0.12);
}
html.light-theme .value-card:hover {
background: rgba(184, 122, 0, 0.15);
box-shadow: 0 12px 35px rgba(184, 122, 0, 0.25);
}
html.light-theme .hosting-banner {
background: rgba(184, 122, 0, 0.12);
box-shadow: 0 10px 30px rgba(184, 122, 0, 0.2);
}
html.light-theme .donation-amount {
background: rgba(184, 122, 0, 0.15);
box-shadow: 0 6px 20px rgba(184, 122, 0, 0.2);
}
html.light-theme .donation-amount:hover {
box-shadow: 0 10px 30px rgba(184, 122, 0, 0.4);
}
html.light-theme .donation-amount.monthly {
background: rgba(0, 107, 61, 0.15);
}
html.light-theme .service-card {
background: rgba(0, 107, 61, 0.1);
box-shadow: 0 10px 30px rgba(0, 107, 61, 0.15);
}
html.light-theme .btn-primary:hover {
box-shadow: 0 15px 40px rgba(184, 122, 0, 0.4);
}
html.light-theme .public-benefits {
background: rgba(184, 122, 0, 0.12);
border: 2px solid rgba(184, 122, 0, 0.3);
}
html.light-theme .publics-footer {
background: rgba(184, 122, 0, 0.12);
border: 2px solid rgba(184, 122, 0, 0.3);
}
html.light-theme .faq-item {
background: rgba(255, 255, 255, 0.5);
border: 3px solid rgba(184, 122, 0, 0.3);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
}
html.light-theme .faq-item:hover {
box-shadow: 0 8px 25px rgba(184, 122, 0, 0.25);
}
html.light-theme .faq-item.active {
box-shadow: 0 10px 35px rgba(184, 122, 0, 0.35);
}
html.light-theme .engagement-card {
background: rgba(184, 122, 0, 0.12);
box-shadow: 0 10px 30px rgba(184, 122, 0, 0.2);
}
html.light-theme .engagement-card:hover {
box-shadow: 0 25px 50px rgba(184, 122, 0, 0.35);
}
html.light-theme .contact {
background: rgba(0, 0, 0, 0.05);
}
html.light-theme .contact-links a {
color: #0D0D0D;
}
html.light-theme .contact-links a:hover {
color: var(--or-oki);
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
@@ -101,6 +232,54 @@ nav {
color: var(--or-oki);
}
/* Theme Toggle Button */
.theme-toggle {
background: rgba(255, 255, 255, 0.1);
border: 2px solid var(--or-oki);
border-radius: 20px 10px 20px 10px;
padding: 0.5rem 0.8rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
transition: all 0.3s ease;
font-size: 1.2rem;
position: relative;
overflow: hidden;
}
.theme-toggle:hover {
background: rgba(253, 184, 19, 0.2);
border-color: var(--jaune-soleil);
transform: scale(1.05);
}
.theme-icon {
transition: all 0.4s ease;
display: inline-block;
}
.theme-icon-light {
display: none;
}
html.light-theme .theme-icon-dark {
display: none;
}
html.light-theme .theme-icon-light {
display: inline-block;
}
html.light-theme .theme-toggle {
background: rgba(0, 0, 0, 0.1);
border-color: var(--or-oki);
}
html.light-theme .theme-toggle:hover {
background: rgba(232, 166, 37, 0.2);
}
.menu-toggle {
display: none;
flex-direction: column;
@@ -869,6 +1048,15 @@ input[type="number"]:focus {
display: none;
}
.nav-container {
gap: 1rem;
}
.theme-toggle {
padding: 0.4rem 0.6rem;
font-size: 1rem;
}
.menu-toggle {
display: flex;
}
+87 -1
View File
@@ -1,3 +1,74 @@
// Theme Management
(function() {
const themeToggle = document.getElementById('theme-toggle');
// Fonction pour obtenir le thème actuel
function getCurrentTheme() {
return document.documentElement.classList.contains('light-theme') ? 'light' : 'dark';
}
// Fonction pour changer de thème
function toggleTheme() {
const currentTheme = getCurrentTheme();
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
if (newTheme === 'light') {
document.documentElement.classList.add('light-theme');
} else {
document.documentElement.classList.remove('light-theme');
}
// Sauvegarder la préférence
localStorage.setItem('oki-theme', newTheme);
// Mettre à jour le menu mobile si ouvert
const navLinks = document.querySelector('.nav-links');
if (navLinks && navLinks.style.display === 'flex') {
const background = newTheme === 'light' ? 'rgba(255, 248, 231, 0.98)' : 'rgba(0, 0, 0, 0.98)';
navLinks.style.background = background;
}
// Mettre à jour les styles de la nav en fonction du scroll actuel
const nav = document.querySelector('nav');
const currentScroll = window.pageYOffset;
const isLightTheme = newTheme === 'light';
if (currentScroll > 100) {
if (isLightTheme) {
nav.style.background = 'rgba(255, 248, 231, 0.98)';
nav.style.boxShadow = '0 2px 20px rgba(184, 122, 0, 0.3)';
} else {
nav.style.background = 'rgba(0, 0, 0, 0.98)';
nav.style.boxShadow = '0 2px 20px rgba(232, 166, 37, 0.3)';
}
} else {
if (isLightTheme) {
nav.style.background = 'rgba(255, 248, 231, 0.95)';
nav.style.boxShadow = '';
} else {
nav.style.background = 'rgba(0, 0, 0, 0.95)';
nav.style.boxShadow = '';
}
}
}
// Écouter le changement de thème système
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkModeMediaQuery.addEventListener('change', (e) => {
// Ne changer automatiquement que si l'utilisateur n'a pas de préférence stockée
if (!localStorage.getItem('oki-theme')) {
if (e.matches) {
document.documentElement.classList.remove('light-theme');
} else {
document.documentElement.classList.add('light-theme');
}
}
});
// Ajouter l'événement au bouton
themeToggle?.addEventListener('click', toggleTheme);
})();
// Smooth scrolling
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
@@ -20,12 +91,16 @@ let menuOpen = false;
menuToggle?.addEventListener('click', () => {
menuOpen = !menuOpen;
if (menuOpen) {
// Déterminer le fond selon le thème actuel
const isLightTheme = document.documentElement.classList.contains('light-theme');
const background = isLightTheme ? 'rgba(255, 248, 231, 0.98)' : 'rgba(0, 0, 0, 0.98)';
navLinks.style.display = 'flex';
navLinks.style.position = 'absolute';
navLinks.style.top = '100%';
navLinks.style.left = '0';
navLinks.style.right = '0';
navLinks.style.background = 'rgba(0, 0, 0, 0.98)';
navLinks.style.background = background;
navLinks.style.flexDirection = 'column';
navLinks.style.padding = '1rem';
navLinks.style.borderTop = '2px solid #E8A625';
@@ -82,14 +157,25 @@ const nav = document.querySelector('nav');
window.addEventListener('scroll', () => {
const currentScroll = window.pageYOffset;
const isLightTheme = document.documentElement.classList.contains('light-theme');
if (currentScroll > 100) {
if (isLightTheme) {
nav.style.background = 'rgba(255, 248, 231, 0.98)';
nav.style.boxShadow = '0 2px 20px rgba(184, 122, 0, 0.3)';
} else {
nav.style.background = 'rgba(0, 0, 0, 0.98)';
nav.style.boxShadow = '0 2px 20px rgba(232, 166, 37, 0.3)';
}
} else {
if (isLightTheme) {
nav.style.background = 'rgba(255, 248, 231, 0.95)';
nav.style.boxShadow = '';
} else {
nav.style.background = 'rgba(0, 0, 0, 0.95)';
nav.style.boxShadow = '';
}
}
// Hide/show navbar on scroll
if (currentScroll > lastScroll && currentScroll > 500) {