Astuces CSS pour faire un thème sombre

Le plus souvent quand on fait un thème sombre et un thème clair pour son site, on a déjà l’un des deux et on fait en sorte d’ajouter un « patch » qui va ajouter le support de l’autre.

Dans ce qui suit, je ferais comme si on avait un thème clair, et qu’on veut l’adapter (le patcher) pour avoir un thème sombre.

Introduction et méthode

Idéalement, le on ne touchera pas du tout au thème de base. C’est le patch qui va alors écraser certains styles du thème de base.

En pratique, le CSS qui concerne l’agencement des éléments n’est pas modifié : position, margin, padding, display, etc. ne changeront pas.
Seules quelques déclarations esthétiques vont être réécrites : color, background, *-shadow, border

Ceci rend extrêmement simple la création d’un thème sombre à partir d’un thème clair.

Typiquement, pour un site initialement en noir sur blanc, on va faire en sorte qu’il soit en blanc sur noir avec le thème sombre.

Par exemple, si le thème initial est ça :

body {
    color: black;
    background-color: white;
    margin: 20px;
    font-size: 3em;
}

p {
   text-align: center;
}

Le patch pourrait être tout simplement :

body {
    color: white;
    background-color: black;
}

On mettra alors ceci comme patch dans le CSS destiné uniquement au thème sombre :

@media (prefers-color-scheme: dark) {
    body {
        color: white;
        background-color: black;
    }
}

Seul le CSS qui change est redéfini. Inutile de faire une duplication complète du thème.

Bien-sûr, ça fait un peu plus de CSS, mais l’avantage est qu’il contiendra les deux variantes en même temps. Ceci est pratique si c’est le navigateur qui choisi lequel appliquer, selon la politique thème clair/sombre du système et donc de l’utilisateur (politique que je vous conseille d’appliquer). Rappelons que le thème sombre/clair peut changer au cours de la journée sur certains systèmes (iOS le fait très bien par exemple).

On peut mettre le thème sombre (ou son patch) dans un fichier à part, et le navigateur ne le chargera qu’à sa discrétion, avec l’utilisation d’un élément LINK dédiée :

<link type="text/css" rel="stylesheet" media="(prefers-color-scheme: dark)" href="styles/style-dark.css" />

Plus votre thème est bien pensé, plus il sera rapide de l’adapter. Si vous aviez mis des color à chaque élément, il faudra tous les réécrire. Mais si vous n’avez mis qu’une couleur initiale, qui est héritée sur tous les éléments de la page, changer le thème sera rapide.

Approche avec les variables

Une autre approche qui existe, et que je conseille pour les thèmes nouveaux que vous créez est de faire un « template de thème » avec des variables, qui va convenir au clair, comme au sombre, et dont les couleurs sont contenues dans des variables qui eux dépendront du thème choisi.

C’est une méthode moderne, mais qui ne marchera pas sur les vieux navigateurs (sans support du prefered-color-scheme, ou des variables).

L’idée est de faire ça :

/* Définition des variables couleurs thème clair */
@media (prefers-color-scheme: light) {
body {
    --couleur-text: black;
    --couleur-fond: white;
}

/* Définition des variables couleurs thème sombre */
@media (prefers-color-scheme: dark) {
body {
    --couleur-text: white;
    --couleur-fond: black;
}

/* CSS normal */
body {
    color: var(--couleur-text);
    background-color: var(--couleur-fond);
}

Bien-sûr, ces variables sont globales et peuvent être utilisées partout où l’on souhaite mettre cette couleur. Donc pour le texte, mais aussi des bordures ou des couleurs d’ombres par exemple :

table, td {
    border: 1px solid var(--couleur-text);
}

(Même si pour cet exemple spécifique, il serait bien plus judicieux de mettre border: 1px solid currentColor; qui est une valeur CSS existante depuis très longtemps).

Le thème clair/sombre contiendra alors seulement une série de variables. On pourra, de même, ajouter des thèmes rouges, bleu, inversé… bref c’est plus libre.

Dans ce qui suit, quelques astuces en vrac, car les différences entre un thème sombre et un thème clair ne se limitent pas qu’à changer du noir-sur-blanc en blanc-sur-noir.

Astuces diverses

Certains éléments peuvent changer de couleur (au hover, au focus…) même dans le seul thème clair. D’autres éléments ont des couleurs par défaut du système, qu’il faudra réécrire si l’utilisateur choisit un thème web sombre sans un thème système sombre.
Il faudra tester intensivement son CSS pour vérifier que les couleurs soient assez lisibles.

Il faut aussi s’assurer que toutes les couleurs soient cohérentes (sinon ça sera moche) : donc le texte et les fonds, mais aussi la couleur :

  • des bordures ;
  • des ombres (boîtes, texte) ;
  • des images de fond (préférez ceux avec un canal alpha) ;
  • des images de contenu (rien de pire qu’un schéma tout blanc sur un écran noir) ;
  • des couleurs des liens : visités ou non-visités ;
  • des éléments de l’interface (contrôles, icônes, menus, bulles de listes…) ;
  • d’éventuellement du pointeur de souris, si vous le modifiez ;
  • du ::placeholder d’un champ texte (s’il est gris il devra être lisible à la fois sur fond clair et sombre).

Pour les images, il faut faire attention :

  • une image de fond claire avec du texte noir positionné par dessus, oui ! Mais garder le même fond en ayant changé le texte en blanc, non !
  • inversement : si vous avez une image transparente avec du texte noir posé sur une page blanche : bien ! Mais si le fond de page change et que l’image reste identique, donc en texte noir, on aura du texte noir (sur l’image) sur un fond noir (la page) : pas bien !

Assombrir les images en CSS sans changer la couleur

L’idée ici est de prendre une PNG transparente avec, par exemple, du texte écrit en noir. Si le fond de la page est clair, pas de soucis. Si le fond de la page est noir, l’image sera illisible.

On peut utiliser ce CSS :

@media (prefers-color-scheme: dark) {
	img[src$=".png"] {
		filter: invert(1);
	}
}

Cela inversera la luminosité des couleurs (le blanc devient noir, le noir devient blanc, et le gris 50 % reste tel qu’il est.
Problème : cela inverse également le sens du cercle chromatique : le rouge deviendra vert, le bleu deviendra jaune, etc. Ce n’est pas idéal.

Pour conserver les teintes des couleurs, il faut retourner le cercler de 180° : hue-rotate(180deg). Ainsi, le rouge redevient rouge, le vert redevient vert. C’est juste que le rouge foncé deviendra rouge clair, etc.

Personnellement, j’ajoute également un saturate(3) afin d’ajuster l’ensemble. Le CSS final :

@media (prefers-color-scheme: dark) {
	img[src$=".png"] {
		filter: invert(1) hue-rotate(180deg) saturate(3);
	}
}

Le résultat :

Exemple d’application des filtres CSS.
Avec le filtre, les couleurs sombres dans l’image transparente restent lisibles.

Cette méthode n’est pas parfaite (le bleu semble un peu terne par exemple), mais il a l’avantage d’inverser la luminosité pour que ça reste lisible quel que soit le fond, et de conserver les teintes (le rouge reste rouge, le vert reste vert).

Évitez les ombres

La seconde astuce dans la confection d’un thème sombre concerne les ombres (text-shadow, box-shadow).

Les ombres, c’est joli sur un thème clair car ça ajoute de la profondeur et permet de définir la hauteur d’un élément sur l’axe Z. Ça permet de voir quel élément est littéralement à l’avant-plan. L’ombre fonctionne donc exactement comme dans la vie réelle : si elle est large est diffuse, l’objet est plus haut que si l’ombre est petite et sombre.

En thème sombre, si vous mettez une ombre claire, on perd la relation avec la vie réelle, car les ombres claires ça n’existe pas ! Conserver la relation avec la vie réelle est un des fondements du design en Material Design, par exemple.

Il faut donc éviter les ombres dans un thème sombre : ça n’a pas de sens. Préférez jouer sur la couleur de fond. En particulier pour ce qui est du :focus ou du :hover. Il faut toujours que l’interaction soit visible par l’utilisateur. En faisant ça on retrouve un parallèle avec la vie réelle : si on éclaire un élément sous une faible luminosité, les éléments en avant seront plus clairs que ceux en arrière plans.

Voici un exemple, avec un champ texte qui est ombré dans le thème clair et coloré dans le thème sombre :

Champ texte en clair VS sombre
Ici, lors du focus, le thème sombre joue sur la couleur de fond, alors que le thème clair joue sur l’ombre.

En material design, c’est ce qu’il faut faire. D’autres design utilisent autre méthodes.

Évitez les bordures

De même que les ombres, en thème sombre, les bordures n’ont pas la même signification.

Sur un thème clair, les bordures délimitent les éléments un peu comme dans la réalité, car la lumière se diffuse à cet endroit. En thème sombre, si on veut que ça se voit, on met un bord clair, mais on perd là aussi le rapprochement avec la réalité. Préférez donc là aussi de jouer plutôt sur les couleurs de fond.

Si vous voulez tout de même une bordure, il y a une petite astuce qui consiste à utiliser des bordures grises transparentes (avec RGBA). Ainsi, la bordure sera toujours visible quel que soit le thème de fond (même gris) car sa couleur sera sa propre couleur transparente additionnée à la couleur de fond.

Essayez par exemple :

border: 1px solid rgba(0, 0, 0, .15)

Du gris plutôt que du blanc

Quand je parle plus haut d’utiliser du blanc sur du noir, c’est une caricature. Le blanc sur du noir, principalement sur des écrans à fort contraste, comme les écrans OLED, cela pique les yeux : le contraste est trop fort.

Préférez donc du gris clair sur du gris foncé. Au besoin, en plus d’un thème sombre, vous pouvez proposer un thème « noir ». Ici, un fond noir et du texte gris clair donnera un rendu particulièrement appréciable sur des écrans OLED, où les noirs sont absolus.

Faites attention à tout

Je le répète, mais testez bien votre code. Certains éléments de page tirent toujours leurs couleurs des styles système (les menus ou les éléments de formulaire notamment, telles que les optgroup ou select).
Si le thème système est clair, mais que vous voulez forcer un thème sombre, alors le système mettra un texte sombre et vous un fond sombre : ça sera illisible. Le mieux, je le recommande, c’est de se conformer au thème du système de vos visiteurs.

Au pire, mettez un bouton pour permettre à l’utilisateur de choisir.

Concernant l’accessibilité

Faites attention à tout… y compris à l’accessibilité ! Généralement les contrastes sont moins bons sur les thèmes sombres. À titre d’exemple, on peut facilement constater que les textes en gras sont plus difficiles à repérer sur un thème sombre. Une astuce couramment utilisée pour ça, si votre texte est en gris clair sur noir, c’est de mettre le gras en blanc (en plus de le mettre en gras) pour le faire ressortir d’autant plus.

Notez également que même si le thème sombre est là pour reposer vos yeux dans un environnement faiblement éclairé, il semblerait qu’il fatigue beaucoup plus au contraire (source et source) !