Thème sombre

Avoir le choix d'un thème sombre sur son site (SCSS et JavaScript)

Étonnamment, je préfère les thèmes sombres, surement une dérive des journées passées sur une flotte de terminaux (et des soirées ?). Du coup, mon site web était aussi en sombre. Mais tout le monde n’est pas fan, et le noir peu être mal perçu. Alors, pourquoi ne pas donner le choix au visiteur ?

Le site avec un thème sombre ou clair

Sur cette piste, il existe plusieurs voies. J’ai choisi d’utiliser à la fois le SCSS qui était déjà inclus dans le thème Hugo. En le combinant aux variables CSS et aux attributs de page pour une mise à jour instantanée, à du JavaScript et à la requête de média CSS prefers-color-scheme, on obtiens une solution dont le thème :

  • est adapté aux préférences générales clair/sombre de l’utilisateur, si c’est supporté par le navigateur (ça marche bien avec Firefox, moins bien sur Android 9, probablement OK sur Mac),
  • est modifiable localement par l’utilisateur avec un bouton sympa,
  • dont le choix est stocké pour les visites suivantes.

Concrètement

En gardant la structure de mon site en hugo, cela donne :

  • dans /assets/scss/_predefined.scss, on définit les couleurs :
$text: #000000;
$background: #ffffff;
$darkmode-text: #ffffff;
$darkmode-background: #000000;
  • dans /assets/scss/style.scss on définit les variables CSS et on les applique la où il faut :
[data-theme="light"]{
  --background: #{$background};
  --text:#{$text};
}

[data-theme="dark"]{
  --background: #{$darkmode-background};
  --text:#{$darkmode-text};
}

body{
  color: var(--text);
  background: var(--background);
}
  • La checkbox à insérer où l’on veut :
<div class="theme-switch-wrapper">
  <label class="theme-switch" for="checkbox">
    <input type="checkbox" id="checkbox" />
    <div class="slider round"></div>
  </label>
  <em>{{ i18n "theme_switch" }}</em>
</div>
  • et on maquille la checkbox en curseur, toujours dans /assets/scss/style.scss :
/*Simple css to style it like a toggle switch*/
$height:20px;
$width:30px;
$rond:17px;

.theme-switch-wrapper {
  display: flex;
  align-items: center;
  em {
    margin-left: 10px;
    font-size: 1rem;
  }
  margin:0px 4px;
}
.theme-switch {
  display: inline-block;
  height: $height;
  position: relative;
  width: $width;
}

.theme-switch input {
  display:none;
}

.slider {
  background-color: #ccc;
  bottom: 0;
  cursor: pointer;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
  transition: .4s;
}

.slider:before {
  background-color: #fff;
  bottom: 1.7px;
  content: "";
  height: $rond;
  left: 2px;
  position: absolute;
  transition: .4s;
  width: $rond;
}

input:checked + .slider {
  background-color: #ffffff;
}

input:checked + .slider:before {
  transform: translateX($rond/2);
  background-color: #000;
}

.slider.round {
  border-radius: $height;
}

.slider.round:before {
  border-radius: 50%;
}
  • dans /assets/js/monjs.js on change le curseur et l’attribut de page data-theme :
(function() {
  'use strict';
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;

if (currentTheme) {
    document.documentElement.setAttribute('data-theme', currentTheme);
    if (currentTheme === 'dark') {
        toggleSwitch.checked = true;
    }
}
else {
  if (window.matchMedia("(prefers-color-scheme: dark)")){
    toggle(true)
  }
}

function toggle(bool) {
        toggleSwitch.checked = bool;
    if (bool) {
        document.documentElement.setAttribute('data-theme', 'dark');
        localStorage.setItem('theme', 'dark'); 
    }
    else {
        document.documentElement.setAttribute('data-theme', 'light');
        localStorage.setItem('theme', 'light');
    }    
}
function switchTheme(e) {
    if (e.target.checked) {
        document.documentElement.setAttribute('data-theme', 'dark');
        localStorage.setItem('theme', 'dark'); //add this
    }
    else {
        document.documentElement.setAttribute('data-theme', 'light');
        localStorage.setItem('theme', 'light'); //add this
    }    
}

window.matchMedia("(prefers-color-scheme: dark)").addListener(e => e.matches && toggle(true) )
window.matchMedia("(prefers-color-scheme: light)").addListener(e => e.matches && toggle(false) )

toggleSwitch.addEventListener('change', switchTheme, false);
})();
  • si on utilise hugo, le script est combiné pour être publié, dans mon cas c’est /layout/_default/baseof.html :
	{{ $tezoi := resources.Get "js/tezoi.js" -}}
	{{ $main := resources.Get "js/main.js" -}}
        {{ $script := slice $main $tezoi | resources.Concat "js/bundle.js" | minify | fingerprint -}}

Références


codehugo

525 mots

08/01/2020 11:36 +0100