Comment j'ai ajouté facilement un dark mode à mon site
Comme à chaque fois que je passe plus de 2h sur un problème technique, je vais documenter ma solution, au cas où elle puisse être utile à quelqu'un·e (ou à la moi du futur !)
Dans cet épisode, voici la méthode que j'ai utilisée pour ajouter un dark mode à mon site. Mon code n'est probablement pas le plus propre ou le plus optimisé, mais il fonctionne et n'est pas dépendant d'une stack technique particulière (à part jQuery, mais il pourrait facilement être adapté pour faire sans, avec quelques getElementByID()
). Il prend en compte les préférences du navigateur / de l'OS, et le choix est gardé d'une visite à l'autre.
J'ai commencé par ajouter un bouton dans mon header :
<button onclick="toggleDarkmode();" class="btn-dm" aria-pressed="false">
<span class="emote">⏾</span>
<span class="sr-only">mode sombre</span>
</button>
Un rappel de comment utiliser l'attribut aria-pressed.
Le deuxième span avec le texte "mode sombre" est masqué, sauf pour les lecteurs d'écran, grâce à ce CSS, fourni par Gaël Poupard.
/* Only display content to screen readers */
/* See: http://a11yproject.com/posts/how-to-hide-content/ */
/* See: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/ */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
clip-path: inset(50%);
border: 0;
}
Pour passer du mode classique au mode sombre et vice-versa, j'ai décidé d'ajouter ou retirer une classe dark-mode
sur le <body>
, et d'utiliser les variables CSS. Ça a été l'occasion de me rendre compte que j'avais oublié plein de valeurs en dur dans des coins reculés de mes feuilles de styles 🫣
/* Les valeurs en mode clair */
:root {
--first:#01847c;
--second:#00AAA0;
--third:#8ED2C9;
--fourth:#84eadd;
--fifth:#FFB85F;
--bg:#FDF9EA;
--bg-revert:#3f3f3f;
--txt:#464646;
--txt-revert: #FFFFFF;
--white:#FFFFFF;
--second-50: rgba(0, 170, 160, 0.50);
--second-15: rgba(0, 170, 160, 0.15);
--second-10: rgba(0, 170, 160, 0.10);
--third-50: rgba(142, 210, 201, 0.50);
--fifth-80: rgba(255,184,95, 0.80);
--fifth-50: rgba(255,184,95, 0.50);
--fifth-30: rgba(255,184,95, 0.30);
--fifth-15: rgba(255,184,95, 0.15);
}
/* Les valeurs en mode sombre */
.dark-mode {
--first:#79c7be;
--second:#5fbeb4;
--third:#40b5a9;
--fourth:#00ab9f;
--fifth:#FFB85F;
--bg:#3f3f3f; /*282828*/
--bg-revert:#FDF9EA;
--txt:#e3e3e3;
--txt-revert: #464646;
--white:#FFFFFF;
--second-50: rgba(95, 190, 180, 0.50);
--second-15: rgba(95, 190, 180, 0.15);
--second-10: rgba(95, 190, 180, 0.10);
--third-50: rgba(64, 181, 169, 0.50);
--fifth-80: rgba(255,184,95, 0.80);
--fifth-50: rgba(255,184,95, 0.50);
--fifth-30: rgba(255,184,95, 0.30);
--fifth-15: rgba(255,184,95, 0.15);
}
/* exemple d'utilisation d'une valeur */
.test {
color: var(--second);
}
Bon, et maintenant, le plus dur, le 😱 JavaScript 😱
$lightOrDark = "";
//activation et désactivation du dark mode
function toggleDarkmode() {
$lightOrDark = ($lightOrDark == "dark") ? "light" : "dark";
applyDarkmode();
}
//appliquer le dark mode en fonction de l'état choisi
function applyDarkmode() {
if ($lightOrDark == "dark") {
localStorage.setItem('darkmode', 'dark');
$('body').addClass('dark-mode');
$('.btn-dm').attr('aria-pressed', 'true');
$('.emote').text("\ud81a\udd13");
} else {
localStorage.setItem('darkmode', 'light');
$('body').removeClass('dark-mode');
$('.btn-dm').attr('aria-pressed', 'false');
$('.emote').text("\u23fe");
}
}
$( document ).ready(function() {
//detection du color scheme préféré + gestion du localStorage
if(localStorage.getItem('darkmode') != null ) {
$lightOrDark = localStorage.getItem('darkmode');
} else {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
$lightOrDark = "dark";
localStorage.setItem('darkmode', 'dark');
}
}
applyDarkmode();
});
L'utilisation du localStorage (localStorage.setItem('darkmode')
et localStorage.getItem('darkmode')
) permet de rester dans le même mode en changeant de page ou en revenant sur le site plus tard.
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
permet de prendre en compte les préférences navigateur / OS de l'utilisateurice.
Vous noterez également que les emotes ne sont pas encodées de la même manière dans le HTML et le JS, j'ai utilisé ce site pour récupérer les bonnes valeurs.
Merci à Gaëtan et Enwin, respectivement pour la motivation et l'aide à mettre ça en place :)