
Je m'appelle Vladimir et je développe un front-end mobile pour Yandex Mail. Nos applications ont un thème sombre depuis un moment, mais il était incomplet: seule l'interface et les e-mails en clair étaient sombres. Les messages avec un formatage personnalisé sont restés légers et se sont détachés de l'interface sombre, blessant les yeux de nos utilisateurs la nuit.
Aujourd'hui, je vais vous expliquer comment nous avons résolu ce problème. Vous découvrirez deux techniques simples qui n'ont pas fonctionné pour nous et la méthode qui a finalement fait l'affaire - la recoloration adaptative des pages. Je partagerai également quelques idées sur l'adaptation d'images à un thème sombre. Pour être honnête, assombrir les pages avec du CSS personnalisé est une tâche assez particulière, mais je pense que certains d'entre vous peuvent trouver notre expérience utile.
Des méthodes simples
Avant de choisir notre technique magique de cintrage des couleurs, nous avons essayé deux options vraiment basiques: appliquer un style sombre supplémentaire ou un filtre CSS aux éléments. Aucune des deux options ne fonctionnait pour nous, mais elles pourraient être plus adaptées dans d'autres cas (simple, c'est cool, non?).
Remplacement des styles
Il s'agit d'une option simple, qui étend logiquement le propre thème CSS sombre de l'application. Vous venez de mettre des styles sombres supplémentaires sur un conteneur de courrier électronique (ou, en général, sur un conteneur pour que le contenu généré par l'utilisateur s'assombrisse):
.message--dark { background-color: black; color: white; }
Cependant, tous les styles sur les éléments à l'intérieur de l'e-mail remplaceront notre style racine. Et non !important
n'aide pas ici. L'idée peut être poussée plus loin en empêchant l'héritage:
.message--dark * { background-color: black !important; color: white !important; border-color: #333 !important; }
Dans ce cas, vous ne pouvez pas vous en passer !important
, car le sélecteur lui-même n'est pas très spécifique. Cela arrive également à remplacer la plupart des styles en ligne (ceux avec en ligne !important
sera toujours trouvé, et vous ne pouvez pas faire grand-chose à ce sujet).
Notre style peint avec passion tout de la même couleur, introduisant ainsi un autre problème. Il y a une chance que le designer veuille dire quelque chose avec la façon dont il a arrangé les couleurs (vous savez, les priorités, les appariements, tous ces trucs de designer), et nous avons juste pris leur idée et l'avons jetée par la fenêtre. Ce n'est pas une bonne chose à faire.

Si vous ne respectez pas les concepteurs autant que moi et décidez d'utiliser cette méthode, n'oubliez pas de gérer les choses pas si évidentes:
box-shadow
. Cependant, vous ne pourrez pas remplacer uniquement la couleur. Soit vous débarrasser complètement des ombres ou faire la paix avec les plus légères.- Les couleurs des éléments sémantiques, tels que les liens ou les entrées.
- SVG en ligne. Utilisez le
fill
au lieu de l' background
- background
et le stroke
au lieu de la color
, mais vous ne pouvez jamais en être sûr, ce pourrait être l'inverse.
D'un point de vue technique, c'est une méthode solide: il faut trois lignes de code (OK, trente pour une version prête à la production avec tous les cas de bord pris en charge), il est compatible avec tous les navigateurs du monde, il fonctionne sur des pages dynamiques prêtes à l'emploi, et la façon dont CSS est attaché au document est complètement hors de propos. Il y a aussi un joli bonus: vous pouvez facilement modifier les couleurs dans le style pour correspondre aux couleurs principales de l'application (par exemple, faire l'arrière-plan #bbbbb8
au lieu du noir).
Au fait, c'est ainsi que nous avions l'habitude d'assombrir les e-mails auparavant. Si un e-mail avait ses propres styles, nous abandonnerions et laisserions la lumière au cas où.
Utiliser un filtre CSS
Il s'agit d'une méthode intelligente et élégante. Vous pouvez assombrir une page à l'aide d'un filtre CSS:
.message--dark { filter: invert(100) hue-rotate(180deg); }
Les images deviennent effrayantes, mais nous pouvons facilement résoudre ce problème:
.message-dark img { filter: invert(100) hue-rotate(180deg); }

Cependant, cela ne résout pas le problème des images de contenu attachées via la propriété background (bien sûr, c'est pratique pour ajuster le rapport d'aspect, mais qu'en est-il de la sémantique?). OK, supposons que nous puissions trouver tous ces éléments, les marquer explicitement et changer leurs couleurs également.
La bonne chose à propos de cette méthode est qu'elle préserve la luminosité et le contraste d'origine. D'un autre côté, c'est plein de problèmes qui l'emportent sur les avantages:

- Les pages sombres deviennent claires.
- Vous ne pouvez pas contrôler les couleurs finales. Quel filtre appliquez-vous à l'arrière-plan pour qu'il corresponde à votre marque
#bbbbb8
? C'est un vrai gratte-tête. - La double inversion rend les images fanées.
- Les performances sont lentes, en particulier sur les appareils mobiles - il est logique, au lieu de simplement rendre la page, le navigateur exécute des transformations de Fourier à chaque tirage.
Cette méthode fonctionnerait pour les e-mails avec du texte de couleur neutre, mais je n'ai jamais rencontré personne avec une boîte de réception remplie d'un tel contenu. D'un autre côté, les filtres sont le seul moyen d'assombrir les éléments sans accéder à leur contenu - cadres, composants Web ou images.
Thème sombre adaptatif
Et maintenant, il est temps pour la magie! Sur la base des inconvénients des deux premières approches, nous avons fait une liste de contrôle de ce que nous devons prendre en charge:
- Rendre l'arrière-plan sombre, le texte clair et les bordures quelque part entre les deux.
- Détectez les pages sombres et conservez leurs couleurs.
- Conservez l'équilibre original de luminosité et de contraste.
- Contrôlez les couleurs résultantes.
- Laissez les teintes inchangées.
Vous devez donc modifier les couleurs des styles pour assombrir l'arrière-plan. Pourquoi ne pas faire ça littéralement? Prenez tous les styles, extrayez les règles liées aux color
( color
, background
- background
, border
, box-shadow
et leurs sous-propriétés) et remplacez-les par leurs versions "assombries": assombrissez l'arrière-plan et les bordures (mais pas autant que le arrière-plan), éclaircir le texte, etc.
Cette méthode a un avantage incroyable qui réchauffera le cœur de tout développeur. Vous pouvez configurer des règles de conversion des couleurs pour chaque propriété - oui, en utilisant du code! Avec un peu d'imagination, vous pouvez adapter les couleurs à n'importe quel thème externe, effectuer la correction des couleurs de votre choix (par exemple, faire un thème clair au lieu d'un sombre, ou un thème de n'importe quelle couleur que vous aimez), et même ajouter un peu de contextualité - par exemple, traiter différemment les frontières larges et étroites.
Les inconvénients sont ce que vous attendez d'une approche «tout en un». Nous exécutons des scripts, encapsulons les styles et gâchons CSS avec regex. Ce dernier, cependant, n'est pas aussi humiliant qu'avec HTML, car la grammaire CSS est régulière (au moins au niveau avec lequel nous travaillons).
Voici notre plan sombre:
- Normalisez les propriétés de style héritées (
bgcolor
et autres) et déplacez-les dans style="..."
. - Trouvez tous les styles en ligne.
- Trouvez toutes les règles liées aux couleurs dans chaque style (
background-color
, color
, box-shadow
et autres). - Prenez les couleurs des propriétés liées aux couleurs et faites-les correspondre avec le bon convertisseur (assombrissez l'arrière-plan, éclaircissez le texte).
- Appelez le convertisseur.
- Remettez les propriétés converties dans le CSS.
Le code wrapper - normalisation, localisation des styles, analyse - est assez trivial. Voyons comment fonctionne exactement notre convertisseur magique.
Conversions HSL
Assombrir une couleur n'est pas aussi simple qu'il y paraît, surtout si vous souhaitez conserver la teinte (par exemple, transformer le bleu en bleu foncé, pas orange). Vous pouvez le faire en RVB ordinaire, mais c'est problématique. Si vous êtes dans la conception algorithmique, vous savez qu'en RVB, même les dégradés sont cassés. Pendant ce temps, travailler avec des couleurs en HSL est un vrai régal: au lieu du rouge, du vert et du bleu, dont vous ne savez pas quoi faire, vous avez les trois canaux suivants:
- Hue - la chose que nous cherchons à préserver.
- La saturation, ce qui n'est pas très important pour nous en ce moment.
- Légèreté, que nous allons changer.
Vous pouvez le voir comme un cylindre. Nous devons renverser ce cylindre. La correction des couleurs fait quelque chose comme ceci: (h, s, l) => [h, s, 1-l]
.
Couleurs déjà OK
Parfois, nous avons de la chance et la conception personnalisée de l'e-mail (ou d'une partie de celui-ci) est déjà sombre. Cela signifie que nous n'avons rien à changer - le concepteur a probablement fait un meilleur travail de sélection des couleurs que notre algorithme n'aurait jamais pu rêver. Dans HSL, il vous suffit de considérer la L - Légèreté. Si sa valeur est supérieure ou inférieure (pour le texte et l'arrière-plan, respectivement) au seuil (que vous pouvez définir), laissez-le tranquille.
Cirque dynamique
Même si nous n'avions pas besoin de tout cela (merci beaucoup, désinfectant, vous avez sauvé ma journée ici), je vais énumérer les fonctionnalités supplémentaires dont un thème adaptatif a besoin pour assombrir des pages entières et pas seulement des e-mails statiques maladroits des années 90. Juste avertissement: cette tâche n'est pas pour tout le monde.
Styles en ligne dynamiques
Changer les styles en ligne est le cas le plus simple qui gâche nos pages sombres. C'est courant, mais il existe une solution simple: ajoutez MutationObserver
et corrigez les styles en ligne dès que les modifications se produisent.
Styles externes
Travailler avec des styles référencés par un élément <link>
à l'intérieur de la page peut être angoissant en raison des instructions asynchronicity et @import
. Et CORS n'améliore pas les choses. Il semble que ce problème puisse être résolu assez élégamment avec un travailleur Web (en procurant tous les *.css
).
Styles dynamiques
Pour aggraver les choses, les scripts peuvent ajouter, supprimer ou réorganiser les éléments <style>
et <link>
comme ils le souhaitent, et même changer les règles dans <style>
. Ici, vous devrez également utiliser MutationObserver
, mais chaque modification entraînera davantage de traitement.
Variables CSS
Les choses deviennent vraiment folles lorsque des variables CSS entrent en jeu. Vous ne pouvez pas assombrir les variables: même si vous supposez qu'une variable contient une couleur basée sur son format (bien que je déconseille cela), vous ne savez toujours pas quel est son rôle - arrière-plan, texte, bordure ou tout à la fois? De plus, les valeurs des variables CSS sont héritées, vous devez donc suivre non seulement les styles, mais les éléments auxquels ils sont appliqués, et cela dégénère rapidement hors de contrôle.
Une fois que les variables CSS arrivent dans le courant dominant, nous avons des problèmes. En revanche, color()
sera supporté d'ici là, nous permettant de remplacer toutes nos conversions JS par une color(var(--bg) lightness(-50%))
.
Résumé

Dans notre cas, lorsque le désinfectant ne laisse que des styles en ligne, l'assombrissement adaptatif au niveau CSS fonctionne comme un charme: il donne un assombrissement de meilleure qualité, ne perturbe pas la structure d'origine et est relativement simple et rapide. L'extension pour s'attaquer aux pages dynamiques ne vaut probablement pas la peine, cependant. Heureusement, si vous travaillez avec du contenu généré par les utilisateurs et que vous ne développez pas de navigateur, votre désinfectant fonctionne probablement de la même manière.
En pratique, le mode adaptatif doit être associé à des remplacements de style, car les éléments standard comme <input>
ou <a>
utilisent souvent les styles d'éclairage par défaut.
Comment assombrir les images
L'assombrissement de l'image est un problème distinct qui me dérange personnellement. C'est difficile, et cela me donne une excuse pour enfin utiliser l'expression "analyse spectrale". Il y a plusieurs problèmes typiques avec des images dans un thème sombre.
Certaines images sont tout simplement trop lumineuses. Cela nous donne le même problème que les courriels brillants avec lesquels nous avons commencé notre quête. Ce problème se pose souvent sur les photos ordinaires, mais ce n'est pas exclusif à celles-ci. Étant donné que l'écriture de CSS de newsletter n'est pas très amusante, certains gars aiment sauter les coins en insérant une mise en page complexe en tant qu'image, ce qui empêche l'assombrissement. Quand je vois cela, mon perfectionniste intérieur gémit de frustration. Ces images doivent être grisées, mais pas inversées, sauf si vous voulez effrayer vos utilisateurs.

Ensuite, il y a des images sombres avec une réelle transparence. C'est un problème typique avec les logos - ils sont conçus avec un fond clair à l'esprit, et lorsque nous le remplaçons par un fond sombre, le logo disparaît. Ces images doivent être inversées.

Quelque part entre les deux se trouvent des images qui utilisent un fond blanc censé représenter la transparence. Dans le thème sombre, ils se retrouvent simplement dans un triangle blanc étrange. Dans un monde parfait, nous pourrions changer le fond blanc en fond transparent, mais si vous avez déjà utilisé un outil de baguette magique, vous savez combien il est difficile de le faire automatiquement.

Il est intéressant de noter que certaines images n'ont aucune signification. Ce sont généralement des pixels de suivi ou des «supports de format» dans des mises en page particulièrement bizzare. Vous pouvez les rendre invisibles en toute sécurité (disons, avec l' opacity: 0
).

Analyser ce qu'il y a à l'intérieur
Pour comprendre comment ajuster une image pour un thème sombre, nous devons regarder à l'intérieur et analyser son contenu, de préférence avec une méthode simple et rapide. Voici la première version d'un algorithme qui résout ce problème.
Comptez d'abord les pixels sombres, clairs et transparents de l'image. Comme optimisation évidente, seul un petit sous-ensemble de pixels peut être considéré. Ensuite, déterminez la luminosité globale de l'image (claire, sombre ou moyenne) et s'il y a de la transparence. Inversez les images sombres avec transparence, les images lumineuses opaques sombres et laissez le reste inchangé.
J'étais vraiment contente de cette découverte jusqu'à ce que je tombe sur un bulletin de charité avec une photo d'une leçon dans une école africaine. Le designer l'a centrée en ajoutant des pixels transparents sur les côtés. Nous ne voulions pas nous impliquer dans une histoire sur la reconnaissance d'images offensive, nous avons donc décidé de laisser la manipulation d'images hors de notre première itération.
À l'avenir, nous pouvons éviter de tels problèmes en employant cette heuristique que j'appelle "l'analyse spectrale". C'est-à-dire, compter le nombre de teintes différentes dans l'image et inverser uniquement s'il n'y en a pas trop. Vous pouvez utiliser la même méthode pour reconnaître les images fragmentaires lumineuses et les inverser également.

Conclusion
Pour concevoir un thème sombre à part entière, nous avons dû trouver un moyen d'assombrir les e-mails contenant un formatage personnalisé - et nous l'avons fait. Tout d'abord, nous avons essayé deux solutions CSS simples et pures - remplacer les styles et utiliser un filtre CSS. Aucun d'eux n'a atteint le but: le premier était trop dur avec le design original, et l'autre ne fonctionnait tout simplement pas bien. Finalement, nous avons décidé d'utiliser l'assombrissement adaptatif. Nous démontons les styles, remplaçons les couleurs et les assemblons. Nous travaillons actuellement sur l'application du thème sombre aux images. Pour ce faire, nous devons analyser leur contenu et ensuite ajuster seulement certains d'entre eux.
Si vous devez changer la couleur des extraits HTML personnalisés pour l'adapter à un thème sombre, gardez à l'esprit trois méthodes:
- Remplacement des styles. C'est une méthode simple et sans problème, et vous en aurez de toute façon besoin pour votre application. La mauvaise nouvelle est qu'elle détruit toutes les couleurs d'origine.
- Filtre CSS. C'est amusant mais laisse beaucoup à désirer. Réservez-le aux éléments qui ne sont pas faciles d'accès, comme les cadres ou les composants Web.
- Réécriture de style. Cette méthode fait un excellent travail pour assombrir, mais elle est plus complexe.
Même si vous n'essayez jamais de tout cela, j'espère que vous avez passé un bon moment à lire cet article!