Mode sombre

La propriété color-scheme indique les jeux de couleurs (par exemple, clair ou sombre) compatibles avec votre page. Cela indique au navigateur qu'il peut automatiquement appliquer un thème aux éléments d'interface utilisateur natifs (comme les barres de défilement, les commandes de formulaire et l'arrière-plan du canevas par défaut) pour qu'ils correspondent à la conception de votre site et contribuent à minimiser les flashs blancs lors du chargement initial.

Implémentation

1. Déclarer les schémas compatibles en HTML

OBLIGATOIRE : Pour éviter un "flash de contenu sans thème" (FOUC), placez une balise <meta> dans votre <head> afin que le navigateur sache quels thèmes vous acceptez avant même de commencer l'affichage. Bien que cette balise <meta> permette d'éviter le FOUC en définissant la couleur initiale du canevas de manière anticipée, elle n'élimine pas complètement les flashs dans tous les navigateurs ou toutes les conditions de chargement.

<!-- MANDATORY: Declare support for both light and dark themes -->
<meta name="color-scheme" content="light dark">

2. Appliquer un jeu de couleurs à l'échelle de la page à :root ou html CSS

OBLIGATOIRE : Appliquez la propriété color-scheme à l'élément html ou à la pseudo-classe :root. Les navigateurs examinent spécifiquement l'élément racine pour déterminer le thème de l'ensemble de la fenêtre d'affichage, y compris les barres de défilement racine et l'arrière-plan initial du "canevas". Si elles ne sont appliquées qu'à body, ces surfaces d'interface utilisateur globales peuvent rester en mode clair, car body ne contrôle pas le contexte d'affichage de la fenêtre.

/* MANDATORY: Apply color-scheme to :root or html for viewport-wide theming */
:root {
  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;
}

3. Définir des jetons de couleur clairs et sombres

Vous pouvez utiliser la fonction light-dark() pour définir des jetons de couleur qui s'adaptent automatiquement aux différentes valeurs color-scheme.

Nous vous recommandons également de conserver les valeurs de couleur brutes dans des propriétés personnalisées distinctes, ce qui facilite leur combinaison de différentes manières (et facilite le comportement de remplacement, si nécessaire).

Pour mieux contrôler les couleurs de l'UI intégrée, comme accent-color ou scrollbar-color, les auteurs peuvent éventuellement ajouter leurs propres couleurs dynamiques à l'aide de propriétés personnalisées et/ou de la fonction light-dark(). Cette fonction sélectionne automatiquement la bonne couleur en fonction de la valeur color-scheme calculée de l'élément et élimine le besoin de requêtes média redondantes, mais elle n'est pas requise pour une implémentation de base.

:root {
  --color-brand-light: oklch(45% 0.23 270);
  --color-brand-dark: oklch(85% 0.15 210);
  --color-brand-text-light: white;
  --color-brand-text-dark: oklch(40% 0.23 270);

  --color-brand: light-dark(var(--color-brand-light), var(--color-brand-dark));
  --color-brand-text: light-dark(var(--color-brand-text-light), var(--color-brand-text-dark));

  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;
}

button.primary {
  /* These automatically adapt to color scheme */
  background-color: var(--color-brand);
  color: var(--color-brand-text);
}

FACULTATIF : Un certain nombre de couleurs système sont disponibles. Elles s'adaptent également automatiquement au jeu de couleurs utilisé (et à d'autres modes de couleur, par exemple les couleurs forcées), comme canvas, canvastext, accentcolor (vérifiez la compatibilité), buttonborder, etc. Elles sont généralement trop limitées pour être utiles, sauf dans des cas très spécifiques où vous devez correspondre exactement à une certaine UI de navigateur par défaut ou en tant que valeurs de remplacement/par défaut.

FACULTATIF : Adaptez les paires de couleurs au contexte

Même lorsque vous remplacez le paramètre système par défaut, il peut être utile d'utiliser la requête média prefers-color-scheme pour définir différentes paires de couleurs qui tiennent compte des couleurs du navigateur et de l'OS autour de la page (ou de la page environnante, lorsque la page est utilisée comme un iframe).

Par exemple, utilisez un thème clair légèrement plus sombre lorsque le paramètre système est dark, ou un thème sombre plus contrasté lorsque le paramètre système est light, afin que la page ne soit pas visuellement dominée par l'UI environnante.

Personnalisation ultraprécise de l'UI du navigateur

La définition de color-scheme adapte déjà l'UI du navigateur au jeu de couleurs utilisé, mais cela utilisera les valeurs par défaut de l'OS et/ou les couleurs du système qui ne correspondent pas forcément parfaitement à la conception du site Web. Les navigateurs récents proposent plusieurs hooks de personnalisation précis pour ces éléments. Ne réimplémentez pas les commandes natives simplement pour personnaliser leur apparence sans épuiser les hooks de personnalisation fournis par les navigateurs récents.

Définir la couleur d'accentuation

Certains éléments de l'interface utilisateur du navigateur (par exemple, les cases à cocher cochées ou les curseurs) utilisent une couleur d'accentuation. Par défaut, il s'agit du paramètre de l'OS, mais vous pouvez utiliser la propriété accent-color pour définir une couleur qui correspond mieux à la page, comme la couleur de la marque de la page.

html {
  accent-color: light-dark(var(--color-accent-light), var(--color-accent-dark));
}

Problèmes à prendre en compte lors de l'utilisation d'accent-color

Le rendu de accent-color dans le navigateur varie. Pour les indicateurs généralement peints sur un arrière-plan de couleur d'accentuation, tels que les coches de case et les points de case d'option, les navigateurs peuvent appliquer différents traitements :

  • Chromium et Firefox sélectionnent généralement une couleur d'indicateur contrastée.
  • Safari peut conserver un indicateur blanc en mode clair ou un indicateur noir en mode sombre, tout en modifiant la couleur d'accentuation derrière lui. Cet ajustement ne fournit pas toujours un contraste suffisant pour chaque couleur fournie. Testez donc les combinaisons importantes dans Safari, y compris les jeux de couleurs clairs et sombres. Ne partez pas du principe que Safari choisira automatiquement la couleur d'indicateur offrant le meilleur contraste.

Couleurs de la barre de défilement

Vous pouvez utiliser scrollbar-color avec light-dark() pour définir des couleurs de barre de défilement personnalisées qui s'adaptent au jeu de couleurs utilisé.

:root {
  --color-scrollbar-track: light-dark(#eee, #222);
  --color-scrollbar-thumb: light-dark(#999, #666);
  scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
}

Problèmes à prendre en compte lors de l'utilisation de scrollbar-color

  • N'animez PAS scrollbar-color et n'utilisez PAS de transition. Un bug WebKit fait clignoter la barre de défilement chaque fois que scrollbar-color change.
  • Sur macOS, les propriétés scrollbar-color (standard) et ::-webkit-scrollbar (ancienne) sont ignorées par défaut, car macOS utilise des barres de défilement "superposées" natives. Vous DEVEZ associer les couleurs personnalisées à scrollbar-width (par exemple, thin ou auto) pour forcer macOS à les afficher.
  • Même lorsque scrollbar-width est appliqué, les barres de défilement en superposition de macOS affichent la piste (gouttière) comme transparente par défaut. Si la conception nécessite une couleur d'arrière-plan de piste visible sur macOS, vous DEVEZ appliquer scrollbar-gutter: stable; au conteneur à faire défiler, mais notez qu'elle n'apparaît que lorsque l'utilisateur pointe sur la barre de défilement.
  • Même avec scrollbar-gutter: stable, la piste peut être transparente sur macOS. Le curseur ne doit pas dépendre de la couleur de la piste pour être visible.

Personnalisation avancée

La plupart des interfaces utilisateur de navigateur exposent des pseudo-éléments pour personnaliser entièrement leur apparence, par exemple :

  • ::placeholder
  • ::spelling-error
  • ::grammar-error
  • ::selection
  • ::search-text
  • ::target-text
  • ::file-selector-button

Vous pouvez utiliser les couleurs light-dark() sur n'importe lequel de ces éléments pour appliquer des couleurs qui s'adaptent au jeu de couleurs utilisé.

4. Images différentes pour les modes clair et sombre

Pour les images CSS, light-dark() peut également être utilisé pour fournir différentes valeurs d'image.

IMPORTANT : Les deux valeurs de light-dark() doivent être du même type. Vous ne pouvez pas combiner une couleur avec une image. Si l'un des deux doit être une image, utilisez image(<color>) pour encapsuler la valeur de couleur.

Détection du thème basée sur JS

La plupart des embranchements de jeux de couleurs doivent être effectués en CSS, qui s'adapte automatiquement aux modifications.

Tout code JS lisant matchMedia("(prefers-color-scheme: dark)").matches DOIT également gérer son événement change, car la préférence système peut changer à tout moment.

FACULTATIF : Implémenter un bouton d'activation/de désactivation du jeu de couleurs

Si vous souhaitez permettre aux utilisateurs de remplacer manuellement leur paramètre système par défaut pour ce site, vous pouvez implémenter un bouton d'activation/de désactivation du mode sombre. La plupart des sites n'ont pas besoin d'un bouton d'activation/de désactivation persistant dans l'UI du site. Il suffit généralement d'utiliser par défaut le jeu de couleurs du système, éventuellement avec un paramètre dans une surface de paramètres distincte. Consultez Boutons d'activation/de désactivation du mode sombre pour connaître les bonnes pratiques concernant l'implémentation d'un bouton d'activation/de désactivation du mode sombre (persistant ou non).

FACULTATIF : Forçages spécifiques aux composants

Vous pouvez remplacer le thème global pour des éléments spécifiques en leur appliquant color-scheme.

Pour en savoir plus et connaître les pièges à éviter, consultez Thèmes clairs/sombres spécifiques aux composants.

Problèmes connus à prendre en compte

Problèmes à prendre en compte lors de l'utilisation de color-scheme

  • Chrome et Firefox respectent color-scheme pour les iFrames : ils affichent les pages intégrées dans le bon jeu de couleurs et ajustent la requête média prefers-color-scheme de la page intégrée pour refléter le color-scheme du contexte d'intégration. Safari ne le fait pas et résout prefers-color-scheme en fonction du paramètre système, même dans les iFrames.
    • Si vous contrôlez à la fois la page parent et l'iframe : transmettez explicitement le jeu de couleurs de la page parent à l'iframe, via un paramètre d'URL (?theme=dark) au moment de la construction de l'iframe ou via postMessage() (qui vous permet également de réagir aux modifications d'exécution). Dans l'iframe, définissez une classe sur <html> (et/ou color-scheme sur :root) à partir de ce signal au lieu de vous appuyer sur prefers-color-scheme.
    • Si vous ne contrôlez que la page intégrée : il n'existe aucun moyen fiable de détecter le color-scheme du contexte d'intégration depuis l'iFrame dans Safari. Exposez un paramètre de thème explicite sur votre API d'intégration (par exemple, une chaîne de requête ou un protocole postMessage) et documentez-le pour les intégrateurs.

Stratégies de remplacement

Solutions de remplacement et compatibilité des navigateurs avec color-scheme

La propriété color-scheme est une amélioration progressive. Les navigateurs qui ne sont pas compatibles ignoreront cette propriété et utiliseront leur UI par défaut en mode clair.

Pour s'adapter aux préférences de l'utilisateur dans les anciens navigateurs, utilisez les requêtes média prefers-color-scheme pour fournir différentes couleurs lorsque le mode sombre est préféré.

  • Utilisez la requête média pour activer les propriétés personnalisées sur :root ou html.
  • Évitez d'utiliser la requête média sur des composants individuels, sauf si le composant nécessite un type de personnalisation du mode sombre très spécifique au-delà des couleurs.
:root {
  /* Define brand colors for each mode */
  --color-brand-light: #0056b3;
  --color-brand-dark: #00e5ff;
  --color-brand: var(--color-brand-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --color-brand: var(--color-brand-dark);
  }

  /* Ignored in older browsers */
  color-scheme: light dark;
}

button.primary {
    background-color: var(--color-brand);
}

Solutions de remplacement et compatibilité des navigateurs pour light-dark()

Pour les navigateurs qui sont compatibles avec color-scheme, mais pas encore avec light-dark(), les versions claires et sombres des couleurs doivent d'abord être définies en tant que propriétés personnalisées. La requête média prefers-color-scheme doit être utilisée pour définir les couleurs du mode correspondant, comme dans l'exemple ci-dessous :

:root {
  /* Define browser UI accent color for each mode */
  --brand-accent-light: #0056b3;
  --brand-accent-dark: #00e5ff;
  --accent-color: var(--brand-accent-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --accent-color: var(--brand-accent-dark);
  }

  /* OPTIONAL: use light-dark() for more control of built-in UI colors */
  @supports (color: light-dark(white, black)) {
    --accent-color: light-dark(var(--brand-accent-light), var(--brand-accent-dark));
  }

  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;

  /* Example inherited color property */
  accent-color: var(--accent-color);
}

pre, code {
  color-scheme: dark;

  /* **Mandatory**: any inherited color properties must be set again, even if to the same design tokens */
  accent-color: var(--accent-color);
}

Solutions de secours et compatibilité des navigateurs avec les valeurs d'image light-dark()

Pour les navigateurs qui ne sont pas compatibles avec light-dark() pour les images, utilisez image-set() comme commutateur : attribuez à chaque option un type() à partir d'une propriété personnalisée, puis définissez l'option inactive sur un type MIME non compatible afin que le navigateur l'ignore. L'option active obtient une valeur vide. Il s'agit donc d'une option image-set() simple sans type().

Définissez le commutateur une fois sur :root. Chaque site d'utilisation n'a alors besoin que des deux images intégrées, comme light-dark().

:root {
  --light-type: ;
  --dark-type: type("image/do-not-use");

  /* MANDATORY: Fallback for browsers without light-dark() image support */
  @media (prefers-color-scheme: dark) {
    --light-type: type("image/do-not-use");
    --dark-type: ;
  }
}

.hero {
  background-image: image-set(url(hero-light.png) var(--light-type), url(hero-dark.png) var(--dark-type));

  @supports (background-image: light-dark(url("a"), url("b"))) {
    background-image: light-dark(url(hero-light.png), url(hero-dark.png));
  }
}
  • Seule l'image sélectionnée est téléchargée.
  • Résolutions toujours valides : image-set(url(a-1x.png) 1x var(--light-type), url(a-2x.png) 2x var(--light-type), url(b-1x.png) 1x var(--dark-type), url(b-2x.png) 2x var(--dark-type)).
  • NE PAS utiliser type("") pour l'option active. Les navigateurs traitent la chaîne vide comme non compatible et suppriment toutes les options.
  • Contrairement à light-dark(), le commutateur ne suit pas le color-scheme défini sur une sous-arborescence. Un bouton d'activation/de désactivation manuel, ou toute sous-arborescence qui définit color-scheme, DOIT également activer/désactiver --light-type et --dark-type.

Alternative : une propriété personnalisée par image

Cette méthode est plus simple lorsque seules quelques images varient, mais chaque image nécessite sa propre branche de requête média, et un bouton manuel doit activer chacune d'elles.

:root {
  --hero-image: url(hero-light.png);

  @media (prefers-color-scheme: dark) {
    --hero-image: url(hero-dark.png);
  }

  @supports (background-image: light-dark(url("a"), url("b"))) {
    --hero-image: light-dark(url(hero-light.png), url(hero-dark.png));
  }
}

.hero {
  background-image: var(--hero-image);
}

Créations de remplacement et compatibilité des navigateurs avec image()

Utilisez un dégradé avec un seul point de couleur et sans position, par exemple linear-gradient(blue).

Pour une compatibilité plus étendue avec les navigateurs, vous pouvez fournir explicitement des positions 0 100% pour l'arrêt de couleur unique, par exemple linear-gradient(blue 0 100%).

Solutions de remplacement et compatibilité des navigateurs pour scrollbar-color

Cette fonctionnalité est une amélioration progressive et ne nécessite pas toujours de créations de remplacement.

Si le style est important et que la cible Baseline de l'utilisateur est "Baseline Widely Available" ou une version antérieure, vous DEVEZ inclure les pseudo-éléments non standards ::-webkit-scrollbar en tant que solutions de secours.

Encapsulez les solutions de remplacement d'anciennes versions dans un bloc @supports not (scrollbar-color: auto) pour éviter les conflits entre les propriétés standards et les anciens sélecteurs WebKit dans les navigateurs qui sont compatibles avec les deux de manière native.

Si vous utilisez des propriétés personnalisées pour définir des couleurs, elles seront automatiquement appliquées aux sélecteurs WebKit hérités. Vous n'avez PAS besoin de les dupliquer.

/* Legacy fallback for WebKit/Blink browsers */
@supports not (scrollbar-color: auto) {
  .scroller::-webkit-scrollbar {
    /* Must define base size in WebKit for custom colors to be visual */
    width: 12px;
    height: 12px;
  }

  .scroller::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb);
  }

  .scroller::-webkit-scrollbar-track {
    background: var(--scrollbar-track);
  }
}

Solutions de remplacement et compatibilité des navigateurs pour accent-color

La propriété accent-color est une amélioration progressive. Les navigateurs qui ne sont pas compatibles avec cette propriété l'ignoreront et utiliseront les couleurs par défaut de leur UI.