
Posté le 11 juin 2024 - modifié le 14 juin 2024
Temps de lecture : environ 6 minutes
Javascript : Créer votre thème sombre facilement
Beaucoup de vos sites web ou applications préférées ont un thème sombre pour réduire l'impact des écrans et éviter une trop grande fatigue visuelle. C'est bien joli sur ces sites, mais vous cherchez à implémenter cette fonctionnalité sur votre propre site. Vous êtes au bon endroit ! Je vais vous montrer qu'avec quelques lignes de JavaScript et de CSS, on peut avoir un thème sombre de qualité, et cela de manière très simple.
La préférence de choix de thème utilisateur
Cette fonctionnalité repose sur une media query qui détecte le choix de thème de l'utilisateur, soit sombre, soit lumineux : prefers-color-scheme. Voyons comment utiliser cette préférence pour créer notre thème sombre.
Nous allons d'abord créer notre code HTML. Il faut créer un bouton switch à l'aide d'un input de type checkbox. Ensuite, le label doit être relié à l'identifiant défini sur l'input à l'aide de l'attribut for.
<div class="theme-switch">
<input type="checkbox" id="theme-switch__input">
<label for="theme-switch__input" id="theme-switch__label">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin=" round" class="lucide lucide-sun">
<circle cx="12" cy="12" r="4" /> <path d="M12 2v2" /> <path d="M12 20v2" /> <path d="m4.93 4.93 1.41 1.41" /> <path d="m17.66 17.66 1.41 1.41" /> <path d="M2 12h2" /> <path d="M20 12h2" /> <path d="m6.34 17.66-1.41 1.41" /> <path d="m19.07 4.93-1.41 1.41" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="lucide lucide-moon">
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
</svg>
</label>
</div>
Nous allons mettre un peu de CSS pour styliser tout cela. D'abord, il faut passer notre input en display: none;. Cet input fonctionnera toujours car lorsque nous appuierons sur le label, l'input se déclenchera. Ensuite, nous allons styliser notre bouton pour le rendre plus joli.
.theme-switch label {
cursor: pointer;
width: 3rem;
height: 3rem;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.theme-switch svg {
position: absolute;
}
#theme-switch__input {
display: none;
}
.light .lucide-sun,
.dark .lucide-moon {
opacity: 1;
}
.light .lucide-moon,
.dark .lucide-sun {
opacity: 0;
}
Parfait, maintenant que nous avons notre bouton, passons à la partie la plus intéressante : la gestion du thème. Il faut d'abord définir les couleurs pour le thème sombre et clair, mais aussi les jeux de couleurs permettant d'indiquer que l'on souhaite utiliser le thème préféré de l'utilisateur.
:root {
color-scheme: light dark;
--dark-color: #f2f2f2;
--dark-bg: #222222;
--light-bg: #f2f2f2;
--light-color: #222222;
}
Ensuite, à l'aide de la fonction CSS light-dark(light-color, dark-color), nous allons appliquer les couleurs définies dans nos variables. À l'heure actuelle, notre thème sombre/clair réagit seulement aux préférences des utilisateurs. Cependant, nous voulons pouvoir gérer cela directement dans notre page. Nous allons créer deux classes qui seront appliquées à l'élément le plus haut du DOM à l'aide de JavaScript. Cela changera entre les deux thèmes directement grâce à la propriété color-scheme qui sera définie soit sur light, soit sur dark.
html {
background-color: light-dark(var(--light-bg), var(--dark-bg));
color: light-dark(var(--light-color), var(--dark-color));
}
.light {
color-scheme: light;
}
.dark {
color-scheme: dark;
}
Pour le JavaScript, rien de plus simple. Nous allons stocker notre état dans le local storage pour avoir une persistance entre les pages. De même, il faut détecter la préférence de l'utilisateur et la stocker, puis la changer si l'utilisateur décide de le faire via notre bouton sur la page.
Nous allons d'abord récupérer les éléments du DOM dont nous avons besoin pour l'interaction avec l'utilisateur, notamment les icônes, le label et l'input.
const darkIcon = document.querySelector(".lucide-moon");
const lightIcon = document.querySelector(".lucide-sun");
const themeSwitchInput = document.querySelector("#theme-switch__input");
const themeSwitchLabel = document.querySelector("#theme-switch__label");
Ensuite, nous allons détecter si l'utilisateur a déjà défini son thème dans le local storage. Si ce n'est pas le cas, nous récupérons la préférence de l'utilisateur.
const getPreferredTheme = () => {
const storedTheme = localStorage.getItem("theme");
if (storedTheme) {
return storedTheme;
}
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
};
Ensuite, nous allons définir le choix de l'utilisateur, l'appliquer sur la page et le stocker dans le local storage pour la persistance. Cette fonction est vraiment l'élément central de notre code et sera utilisée très souvent.
const setTheme = (theme) => {
if (theme === "dark") {
document.documentElement.classList.add("dark");
document.documentElement.classList.remove("light");
themeSwitchInput.checked = true;
} else {
document.documentElement.classList.remove("dark");
document.documentElement.classList.add("light");
themeSwitchInput.checked = false;
}
localStorage.setItem("theme", theme);
};
Nous allons détecter le changement au niveau du bouton pour définir notre nouveau thème grâce à cette fonction qui réutilise celle que nous avons définie précédemment.
themeSwitchInput.addEventListener("change", (event) => {
const theme = event.target.checked ? "dark" : "light";
setTheme(theme);
});
Ajouter la détection dès que l'utilisateur change sa préférence sur son terminal peut être une bonne chose.
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
const newTheme = event.matches ? "dark" : "light";
setTheme(newTheme);
});
Avant de finir, il faut initialiser la préférence au chargement de la page avec la fonction que nous avons créée ci-dessus. Il suffit d'appeler la fonction directement pour que cela fonctionne.
const initTheme = () => {
const theme = getPreferredTheme();
setTheme(theme);
};
initTheme();
Enfin, essayons de rendre notre composant accessible. Pour cela, il faut que le bouton soit accessible au clavier. Nous allons donc appliquer un tabindex="0" et ajouter un peu de JavaScript pour compléter ceci.
themeSwitchLabel.addEventListener("keydown", (event) => {
let key = event.code;
if (key === "Space" || key === "Enter") {
themeSwitchInput.checked = !themeSwitchInput.checked;
const theme = themeSwitchInput.checked ? "dark" : "light";
setTheme(theme);
}
});
Nous pouvons aussi ajouter un span en sr-only pour indiquer à quoi correspond ce bouton, et cacher les SVGs grâce à aria-hidden="true" pour les lecteurs d'écran.
Comme vous pouvez le voir, rien de plus simple pour créer un thème sombre/clair. Si vous voulez accéder au code, il est disponible sur mon Codepen.
Si vous voulez approfondir ce sujet, voici quelques liens pour vous orienter :
Vous voulez en lire plus ?
Regardez ces autres articles :