Ajout du mode automatique (préférence système) au toggle dark mode
Le toggle cycle maintenant entre auto → light → dark. En mode auto, le thème suit prefers-color-scheme et réagit en temps réel aux changements système. L'icône et l'aria-label reflètent l'état courant.
This commit is contained in:
parent
24b63d4e9b
commit
8c550a947f
2 changed files with 76 additions and 15 deletions
|
|
@ -1,16 +1,30 @@
|
||||||
---
|
---
|
||||||
// DarkModeToggle component - toggles between light and dark mode
|
// DarkModeToggle component - cycles between auto / light / dark
|
||||||
---
|
---
|
||||||
|
|
||||||
<button
|
<button
|
||||||
id="darkModeToggle"
|
id="darkModeToggle"
|
||||||
type="button"
|
type="button"
|
||||||
class="flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer text-neutral-600 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
|
class="flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer text-neutral-600 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
|
||||||
aria-label="Toggle dark mode"
|
aria-label="Changer le thème"
|
||||||
>
|
>
|
||||||
|
<!-- Auto icon (system preference) -->
|
||||||
<svg
|
<svg
|
||||||
class="w-5 h-5 dark:hidden"
|
class="w-5 h-5 hidden"
|
||||||
id="sunIcon"
|
data-theme-icon="auto"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
||||||
|
</svg>
|
||||||
|
<!-- Light icon (sun) -->
|
||||||
|
<svg
|
||||||
|
class="w-5 h-5 hidden"
|
||||||
|
data-theme-icon="light"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
|
|
@ -22,9 +36,10 @@
|
||||||
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"
|
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"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
<!-- Dark icon (moon) -->
|
||||||
<svg
|
<svg
|
||||||
class="hidden w-5 h-5 dark:block"
|
class="w-5 h-5 hidden"
|
||||||
id="moonIcon"
|
data-theme-icon="dark"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
|
|
@ -39,17 +54,62 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function setupDarkModeToggle() {
|
function getThemePreference(): 'auto' | 'light' | 'dark' {
|
||||||
const toggle = document.getElementById('darkModeToggle');
|
const stored = localStorage.getItem('theme');
|
||||||
|
if (stored === 'light' || stored === 'dark') return stored;
|
||||||
|
return 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
toggle?.addEventListener('click', () => {
|
function getEffectiveDark(preference: 'auto' | 'light' | 'dark'): boolean {
|
||||||
document.documentElement.classList.toggle('dark');
|
if (preference === 'dark') return true;
|
||||||
const isDark = document.documentElement.classList.contains('dark');
|
if (preference === 'light') return false;
|
||||||
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTheme(preference: 'auto' | 'light' | 'dark') {
|
||||||
|
const isDark = getEffectiveDark(preference);
|
||||||
|
document.documentElement.classList.toggle('dark', isDark);
|
||||||
|
updateIcon(preference);
|
||||||
|
updateAriaLabel(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateIcon(preference: 'auto' | 'light' | 'dark') {
|
||||||
|
document.querySelectorAll<HTMLElement>('[data-theme-icon]').forEach(icon => {
|
||||||
|
icon.classList.toggle('hidden', icon.dataset.themeIcon !== preference);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAriaLabel(preference: 'auto' | 'light' | 'dark') {
|
||||||
|
const toggle = document.getElementById('darkModeToggle');
|
||||||
|
const labels = { auto: 'Thème : automatique', light: 'Thème : clair', dark: 'Thème : sombre' };
|
||||||
|
toggle?.setAttribute('aria-label', labels[preference]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cycle: Record<string, 'light' | 'dark' | 'auto'> = { auto: 'light', light: 'dark', dark: 'auto' };
|
||||||
|
|
||||||
|
function setupDarkModeToggle() {
|
||||||
|
const toggle = document.getElementById('darkModeToggle');
|
||||||
|
const preference = getThemePreference();
|
||||||
|
applyTheme(preference);
|
||||||
|
|
||||||
|
toggle?.addEventListener('click', () => {
|
||||||
|
const current = getThemePreference();
|
||||||
|
const next = cycle[current];
|
||||||
|
if (next === 'auto') {
|
||||||
|
localStorage.removeItem('theme');
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('theme', next);
|
||||||
|
}
|
||||||
|
applyTheme(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||||
|
if (getThemePreference() === 'auto') {
|
||||||
|
applyTheme('auto');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run on page load and on Astro navigation
|
|
||||||
setupDarkModeToggle();
|
setupDarkModeToggle();
|
||||||
document.addEventListener('astro:after-swap', setupDarkModeToggle);
|
document.addEventListener('astro:after-swap', setupDarkModeToggle);
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -15,7 +15,8 @@ const { title } = Astro.props;
|
||||||
<!-- Used to add dark mode right away, adding here prevents any flicker -->
|
<!-- Used to add dark mode right away, adding here prevents any flicker -->
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
if (typeof Storage !== 'undefined') {
|
if (typeof Storage !== 'undefined') {
|
||||||
if (localStorage.getItem('theme') === 'dark') {
|
var theme = localStorage.getItem('theme');
|
||||||
|
if (theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||||
document.documentElement.classList.add('dark')
|
document.documentElement.classList.add('dark')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue