Créa-blog

#100JoursPourCoder
Projet Créa-code

Ressources pour développeur web

Jour 21 : Mise en place Dark mode ou mode sombre du site web

Temps de lecture : 11 minutes
Accueil Projets Jour 21 : Mise en place Dark mode ou mode sombre du site web

Le mode sombre ou dark mode est devenu un standard d’ergonomie sur le web. Il améliore le confort visuel, surtout dans les environnements peu lumineux. Il réduit la fatigue oculaire, permet d’économiser de l’énergie sur les écrans OLED, et donne une image moderne à notre site.

Tous les systèmes récents proposent déjà cette option au niveau de l’OS ou du navigateur. Nous allons donc faire en sorte que notre site respecte la préférence de l’utilisateur, tout en lui laissant le choix manuel. Et cela, sans dépendance externe, avec uniquement du CSS, du JavaScript, et une intégration propre dans l’architecture MVC de notre projet de site web Créa-code.

Centraliser nos couleurs avec des variables CSS

Plutôt que d’écrire les couleurs « en dur » dans le CSS, nous utilisons des variables CSS. Cela permet de centraliser notre design, et de changer rapidement de thème.

Voici la version de base (thème clair) que nous utilisons déjà :

:root {
  --color-bg: #FFFFFF;                /* Fond principal : blanc, neutre et clair */
  --color-bg-alt: #F4F4F4;            /* Fond secondaire : gris clair pour les blocs alternés */
  --color-text: #1E1E1E;              /* Texte principal : anthracite, bonne lisibilité */
  --color-text-always: #1E1E1E;
  --color-text-secondary: #4A4A4A;    /* Texte secondaire : gris foncé */
  --color-accent-primary: #FFED13;    /* Couleur d’accent : jaune vif pour attirer l’attention */
  --color-accent-secondary: #0056B3;  /* Accent secondaire : bleu foncé pour les interactions */
  --color-success: #3CCF91;           /* Vert doux : messages de succès ou progression */
  --color-error: #FF4D4D;             /* Rouge doux : messages d’erreur ou alertes */  
  --border-color: #ccc;             
  
  --font-title: 'Inter', sans-serif;
  --font-text: 'Nunito Sans', sans-serif;
}

On utilise déjà une structure propre, claire et bien commentée. Cela va grandement nous aider à créer une version sombre simplement en redéfinissant ces variables.

Définir les variantes pour le thème sombre

On va maintenant créer un jeu de variables alternatives, qui s’activeront uniquement lorsque le body contient un attribut spécial : data-theme="dark".

Voici la version sombre correspondante :

body[data-theme="dark"] {
  --color-bg: #1A1A1A;                /* Fond principal : noir doux */
  --color-bg-alt: #2A2A2A;            /* Fond secondaire : gris foncé */
  --color-text: #F0F0F0;              /* Texte principal : très clair */
  --color-text-always: #1E1E1E;
  --color-text-secondary: #BBBBBB;    /* Texte secondaire : gris clair */
  --color-accent-primary: #FFD700;    /* Accent jaune légèrement atténué */
  --color-accent-secondary: #3399FF;  /* Accent secondaire : bleu lumineux */
  --color-success: #66E0A1;           /* Vert clair */
  --color-error: #FF7373;             /* Rouge clair légèrement rosé */
  --border-color: #333;

  --font-title: 'Inter', sans-serif;
  --font-text: 'Nunito Sans', sans-serif;
}

Ici, on garde la même structure de variables, ce qui permet au reste du CSS de ne pas changer : seules les valeurs changent selon le thème actif.

Utiliser les variables dans notre CSS global

Prenons un exemple simple, nous avons styliser précédemment le <body> et un bouton .btn-login :

body {
  font-family: var(--font-text);
  font-size: 16px;
  color: var(--color-text);
  background-color: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  padding-top: 120px;
}

.btn-login {
  background: transparent;
  color: var(--color-text-secondary);
  border: 1px solid var(--color-text-secondary);
}

Quand le data-theme="dark" est activé sur <body>, toutes les couleurs s’adaptent automatiquement sans écrire une seule ligne de CSS supplémentaire pour chaque composant.

Intégrer la feuille de style dans l’architecture MVC

Dans notre projet de site web  Créa-code, nous avons un fichier layout.php ou dans notre dossier app/Views/.

Dans la balise <head>, nous avons ajoutez cette ligne pour charger le CSS avec les variables :

<link rel="stylesheet" href="assets/css/styles.css">

Notre fichier styles.css contient à la fois :

  • les variables :root (thème clair)
  • les variables body[data-theme="dark"] (thème sombre)
  • l’usage des variables dans les composants

Préparer la suite

Pour que le mode sombre fonctionne vraiment, il ne suffit pas de définir des variables.
Il faudra ajouter du JavaScript pour pouvoir détecter le thème du navigateur (préférence utilisateur), appliquer dynamiquement data-theme="dark" ou "light" sur le <body> , mémoriser le choix de l’utilisateur dans localStorage, et permettre de changer de thème via un menu accessible.

Et surtout, intégrer tout cela dans notre architecture MVC, proprement, sans polluer les vues. Pour l’instant :

  • Les variables sont en place ✔️
  • Le CSS est prêt à gérer deux thèmes ✔️

Objectif : activer automatiquement le bon thème

Pour que notre site respecte les préférences système de l’utilisateur, nous allons utiliser JavaScript et l’API suivante :

window.matchMedia('(prefers-color-scheme: dark)')

Cette API renvoie un objet MediaQueryList qui nous permet de savoir si l’utilisateur préfère un thème sombre. En combinant cela avec le stockage local (localStorage), nous allons pouvoir :

  • détecter la préférence système à la première visite
  • enregistrer le choix de l’utilisateur si jamais il le change via un bouton
  • prioriser ce choix personnalisé sur celui du système

Code JavaScript pour appliquer le thème

Crée un fichier theme.js dans /public/assets/js/. On y ajoute le code commenté et facile à comprendre suivant :

// Fonction principale pour appliquer un thème
function applyTheme(theme) {
  document.body.setAttribute('data-theme', theme);
  localStorage.setItem('theme', theme);
}

// Fonction pour détecter le thème au chargement
function detectAndApplyTheme() {
  const storedTheme = localStorage.getItem('theme');

  if (storedTheme) {
    applyTheme(storedTheme);
  } else {
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    applyTheme(prefersDark ? 'dark' : 'light');
  }
}

// Appel initial
detectAndApplyTheme();

Ajoute ce script à la fin du fichier app/Views/partials/footer.php, juste avant la balise </body> :

<script src="<?= BASE_URL ?>assets/js/theme.js" defer></script>

Ce code vérifie s’il y a un thème enregistré en local (ex : par clic utilisateur) sinon, il utilise la préférence système et applique data-theme="dark" ou data-theme="light" sur le <body>.

Ajouter un menu pour basculer entre les thèmes

Nous allons maintenant créer une petite interface dans l’en-tête du site pour que l’utilisateur puisse choisir manuellement son thème.

Formation web et informatique - Alban Guillier - Formateur

Des formations informatique pour tous !

Débutant ou curieux ? Apprenez le développement web, le référencement, le webmarketing, la bureautique, à maîtriser vos appareils Apple et bien plus encore…

Formateur indépendant, professionnel du web depuis 2006, je vous accompagne pas à pas et en cours particulier, que vous soyez débutant ou que vous souhaitiez progresser. En visio, à votre rythme, et toujours avec pédagogie.

Découvrez mes formations Qui suis-je ?

Dans le fichier app/Views/partials/footer.php, on ajoute ce menu :

<div class="theme-switcher" aria-label="Choix du thème">
  <label for="theme-select" class="visually-hidden">Thème</label>
  <select id="theme-select" aria-label="Sélection du thème">
    <option value="light">☀️ Clair</option>
    <option value="dark">🌙 Sombre</option>
  </select>
</div>

On ajoute ensuite dans theme.js ce bloc pour synchroniser le menu (ou bouton) :

document.addEventListener('DOMContentLoaded', function () {
  const select = document.getElementById('theme-select');
  const currentTheme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');

  select.value = currentTheme;

  select.addEventListener('change', function () {
    applyTheme(this.value);
  });
});

Ce script détecte le thème en cours, sélectionne l’option correspondante dans le menu et applique le thème si l’utilisateur le change.

Rendre le menu accessible et stylisé

Pour l’accessibilité :

  • le menu utilise un <select> natif (donc compatible clavier, écran, mobile) ;
  • il est accompagné d’un aria-label.

Ajoute un peu de CSS pour qu’il s’intègre harmonieusement :

footer .footer-copy .theme-switcher {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.5rem;
  margin-top: 0.5em;
}

footer .footer-copy .theme-switcher select {
  padding: 0.5rem 1rem 0.5rem 1.5rem;
  border-radius: 8px;
  border: 1px solid var(--color-text-secondary);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-size: 1rem;
  appearance: none;
  cursor: pointer;
  transition: background-color 0.3s, color 0.3s, border-color 0.3s;
  background-image: url("data:image/svg+xml;utf8,<svg fill='currentColor' height='20' viewBox='0 0 20 20' width='20' xmlns='http://www.w3.org/2000/svg'><path d='M5.5 7l4.5 4 4.5-4z'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  background-size: 1rem;
  padding-right: 2rem;
}

footer .footer-copy .theme-switcher select:focus {
  outline: 2px solid var(--color-accent-secondary);
  outline-offset: 2px;
}

footer .footer-copy .theme-switcher select:hover {
  background-color: var(--color-bg-alt);
  color: var(--color-text);
  border-color: var(--color-text-secondary);
}

Adapter le logo et les images au mode dark ou mode sombre

Commençons par préparer deux versions du logo en les plaçant dans le dossier public/assets/images/. Une version light et une version dark.

Puis, dans le header (ex. views/partials/_header.php), on ajoute une classe img-theme-switch :

<img class="img-theme-switch" src="<?= BASE_URL ?>assets/img/logo-crea-code-horizontal500x100.webp" 
  data-src-light="<?= BASE_URL ?>assets/img/logo-crea-code-horizontal500x100.webp"
  data-src-dark="<?= BASE_URL ?>assets/img/logo-crea-code-horizontal-dark.webp" 
  alt="Logo Créa-code" 
  loading="lazy">

Et enfin, on adapte le code JavaScript pour changer le logo et les images selon le thème. Dans le fichier theme.js, on ajoute cette fonction :

function updateImagesForTheme(theme) {
  const images = document.querySelectorAll('img.img-theme-switch');
  if (!images.length) return;

  images.forEach((img) => {
    const newSrc = img.getAttribute(`data-src-${theme}`);
    if (newSrc) {
      img.setAttribute('src', newSrc);
    } else {
      console.warn(`data-src-${theme} manquant pour`, img);
    }
  });
}

Et on l’appelle ensuite dans applyTheme(), juste après avoir appliqué le thème :

function applyTheme(theme) {
  document.body.setAttribute('data-theme', theme);
  localStorage.setItem('theme', theme);
  updateImagesForTheme(theme);
}

Toutes les images qui auront une version light et une version sombre devront être sous la forme :

<img class="img-theme-switch" src="<?= BASE_URL ?>assets/img/nom_image_light.webp" 
  data-src-light="<?= BASE_URL ?>assets/img/nom_image_light.webp"
  data-src-dark="<?= BASE_URL ?>assets/img/nom_image_dark.webp" 
  alt="Description de l'image" 
  loading="lazy">

Accessibilité renforcée

Le dark mode ne doit pas juste être « joli ». Il doit rester lisible, contrasté, et accessible à tous.

Vérification des contrastes

Utilise des outils comme :

Assure-toi que le contraste entre --color-text et --color-bg soit au minimum 4.5:1 pour les textes standards.

b. Ajoute des attributs aria au menu

<div class="theme-switcher" aria-label="Choix du thème">
    <select id="theme-select" aria-label="Sélection du thème" aria-live="polite">
        <option value="light">☀️ Light mode</option>
        <option value="dark">🌙 Dark mode</option>
    </select>
</div>

L’attribut aria-live="polite" permet aux lecteurs d’écran de signaler le changement visuel de façon non intrusive.

Optimisation SEO

Le mode sombre n’affecte pas directement le SEO, car les bots n’interprètent pas les couleurs. Mais une bonne accessibilité, une navigation rapide, et une interface claire ont un impact indirect très positif.

En particulier :

  • Pas de FOUC (« Flash of Unstyled Content ») si le thème est appliqué dès le serveur
  • Chargement léger (aucune dépendance externe) ;
  • Personnalisation utilisateur = meilleure rétention.

Nous avons donc maintenant :

  • Thème respecté au premier affichage ✔️
  • Détection du système ✔️
  • Changement manuel par menu accessible ✔️
  • Sauvegarde persistante (localStorage) ✔️
  • Couleurs accessibles et contrastées ✔️
  • Intégration 100 % MVC ✔️

Mettre en place un dark mode ou mode sombre efficace ne se résume pas à inverser des couleurs : c’est un véritable travail de design, d’accessibilité et d’architecture. Grâce à une gestion fine des variables CSS, à une détection intelligente des préférences système, et à une intégration fluide dans ton architecture MVC en PHP, on offre à nos utilisateurs une expérience moderne, personnalisée et cohérente. Le système s’adapte automatiquement, respecte les préférences utilisateurs, mémorise leurs choix et ajuste même les images et icônes selon le thème.

Ce type de fonctionnalité n’est pas juste un détail visuel : c’est une amélioration concrète de l’ergonomie et de la qualité perçue de notre projet de site web. Que vous développiez un portfolio, un blog, une plateforme d’apprentissage comme Créa-code ou un outil professionnel, ce dark mode dynamique se place dans une logique de développement front-end durable, respectueuse de l’utilisateur et à la hauteur des standards du web moderne.

La suite demain …

Live on Twitch