Comment créer un thème sombre et ne pas nuire. Expérience de l'équipe Yandex.Mail


Mon nom est Vladimir, je suis engagé dans un frontend mobile dans Yandex.Mail. Dans notre application, il y avait déjà un sujet sombre, mais pas assez: nous avons pu repeindre l'interface et des lettres simples. Mais les lettres formatées restaient claires et contrastaient avec une interface sombre, ce qui pouvait me fatiguer les yeux la nuit.


Aujourd'hui, je dirai aux lecteurs de Habr comment nous avons résolu ce problème. Vous découvrirez deux méthodes simples qui ne nous convenaient pas, puis - notre principale méthode de repeinture adaptative de pages et, enfin, la direction de la prochaine itération: repeindre des images. Bien que la tâche elle-même - repeindre des pages avec un formatage arbitraire - soit spécifique, je pense que notre expérience vous sera également utile.


Des moyens simples


Avant d'arriver à notre «repainter» magique, nous avons essayé deux options simples comme un bouchon: ajouter un style sombre supplémentaire ou un filtre CSS à l'élément. Ils ne nous convenaient pas, mais peut-être que dans certains cas, ils seront encore meilleurs (parce que c'est juste = cool).


Remplacer les styles


La manière la plus simple, en développant logiquement le thème sombre de l'application elle-même en CSS: nous accrocherons les styles sombres sur un conteneur de lettres (dans le cas général, pour le contenu de quelqu'un d'autre qui doit être repeint):


.message--dark { background-color: black; color: white; } 

Mais si les éléments à l'intérieur de la lettre ont leurs propres styles, ils redéfiniront notre style racine. Non !important n'aidera pas. Vous pouvez presser une idée en coupant 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. De plus, il sera nécessaire de redéfinir les styles en ligne (et les styles en ligne avec !important vont quand même ramper, il n'y a rien à faire).


Notre style est assez maladroit et colore tout de la même manière, donc un autre problème se pose: probablement le designer voulait dire quelque chose en arrangeant les couleurs (priorités des éléments et autres choses de designer), mais nous avons pris et jeté cette idée.



Si vous respectez moins les designers que moi, et décidez toujours d'utiliser cette méthode, n'oubliez pas de terminer les bagatelles non évidentes:


  • box-shadow - seule la couleur ne peut pas être redéfinie; vous devez supprimer toutes les ombres ou vivre avec des ombres claires.
  • Couleurs des éléments sémantiques - liens, éléments d'entrée.
  • SVG en ligne - au lieu de l' background - background ils doivent définir le fill et au lieu de la color - le stroke , mais ce n'est pas précis, selon le SVG - cela peut être et vice versa.

Techniquement, la méthode n'est pas mauvaise: ce sont trois lignes de code (d'accord, trente pour la version redi de production avec des cas d'angle), la compatibilité avec tous les navigateurs du monde, le traitement des pages dynamiques hors de la boîte et aucune liaison à la façon de connecter les styles dans le document original. Un bonus spécial est que vous pouvez facilement modifier les couleurs dans le style afin qu'elles correspondent à l'application principale (par exemple, créez l'arrière-plan #bbbbb8 au lieu du noir).


Au fait, nous avions l'habitude de repeindre les lettres de cette façon, mais si nous trouvions des styles à l'intérieur de la lettre, nous avions peur et laissions la lettre claire.


Filtre CSS


Option très spirituelle et élégante. Vous pouvez repeindre la page avec un filtre CSS:


 .message--dark { filter: invert(100) hue-rotate(180deg); /* hue-rotate    */ } 

Après cela, les photos deviendront flippantes, mais peu importe - nous les repeindrons:


 .message-dark img { filter: invert(100) hue-rotate(180deg); } 


Il y a toujours des problèmes avec les images de contenu liées via l' background (nous savons qu'il est plus pratique d'ajuster le rapport d'aspect, mais qu'en est-il de la sémantique?). Supposons que nous puissions trouver tous ces éléments, les marquer explicitement et les repeindre.


La méthode est bonne en ce qu'elle conserve le rapport d'origine de luminosité et de contrastes. En revanche, il y a beaucoup de problèmes, et ils l'emportent plutôt sur les avantages:



  1. Les pages sombres s'éclaircissent.
  2. Les couleurs résultantes ne peuvent pas être contrôlées - quel filtre appliquer pour affiner l'arrière-plan de votre entreprise #bbbbb8 ? L'énigme.
  3. Après deux repeintes, les images s'estompent.
  4. Tout ralentit (en particulier sur les téléphones) - c'est logique, maintenant au lieu d'un simple rendu, le navigateur doit piloter le traitement des images sur chaque écran.

Cette méthode conviendrait aux lettres composées de texte dans des tons neutres, mais qui sont les esthètes qui obtiennent une boîte de réception complète d'un contenu si particulier? Mais les filtres peuvent repeindre des éléments dont le contenu n'est pas accessible - cadres, composants Web, images.


Thème réactif


Il est temps pour la magie! À partir des lacunes des deux premières approches, nous collectons une liste de contrôle:


  1. Rendre l'arrière-plan sombre, le texte clair, les bordures moyennes.
  2. Identifiez les pages déjà sombres et ne les repeignez pas.
  3. Conservez le rapport d'origine de luminosité et de contraste.
  4. Donnez la possibilité de personnaliser les couleurs.
  5. Laissez les tons tels qu'ils étaient au début.

Nous devons changer les couleurs des styles afin que l'arrière-plan soit sombre. Et pourquoi ne pas le faire littéralement? Nous prenons simplement tous les styles, recherchons les règles associées aux couleurs ( color , background - background , border , box-shadow , leurs sous-propriétés) et le remplaçons par «assombri» - assombrissez l'arrière-plan, éclaircissez le texte, assombrissez moins les bordures que l'arrière-plan, etc.


Cette méthode a un avantage incroyable qui réchauffera l'âme de tout développeur. Chaque propriété peut être configurée (oui, décrivez directement avec un code!) Ses propres règles de conversion des couleurs. Avec suffisamment d'imagination, vous pouvez intégrer n'importe quel thème externe, effectuer n'importe quelle correction des couleurs (par exemple, faire de la framboise claire ou gris-brun au lieu d'un thème sombre) et même ajouter un peu de contexte - par exemple, gérer différemment les bordures larges et étroites.


Les inconvénients sont standard pour tout-en-js. Oui, nous exécutons des scripts, rompons l'encapsulation des styles et analysons les expressions rationnelles CSS. Eh bien, contrairement au HTML, ce dernier n'est pas si honteux car la grammaire CSS (du niveau dont nous avons besoin) est toujours régulière.


Le plan de repeinture est le suivant:


  1. Nous normalisons les propriétés héritées du style ( bgcolor et friends), les déplaçons vers style="..." .
  2. Trouvez tous les styles en ligne.
  3. Dans chaque style, nous trouvons toutes les règles de couleur (couleur de background-color , color , box-shadow , etc.).
  4. De toutes les règles de couleur, on obtient les couleurs, on trouve le convertisseur souhaité (assombrissant pour le fond, clarifiant pour le texte).
  5. Nous appelons le convertisseur.
  6. Remettre les règles converties en CSS.

La liaison (normalisation, recherche de style, analyse) est assez simple. Nous allons voir comment fonctionne exactement notre convertisseur magique.


Conversions HSL


"Gradation de la couleur" n'est pas une action aussi simple qu'il y paraît, surtout si nous voulons garder le ton (le cyan devient bleu foncé, pas orange). Cela peut être fait en RVB normal, mais problématique. Les amateurs de conception algorithmique savent que même les gradients sont tordus. Mais travailler avec des couleurs en HSL est un pur plaisir: au lieu du rouge, du vert et du bleu, avec lesquels on ne sait pas quoi faire, nous avons trois autres canaux:


  • La teinte est juste le ton que nous voulons garder.
  • Saturaion - saturation, ce qui n'est pas très important pour nous maintenant.
  • Légèreté - la luminosité que nous changerons.

Il est commode d'imaginer un tel espace sous la forme d'un cylindre. Et notre tâche est de renverser ce cylindre. Les fonctions d'étalonnage des couleurs font quelque chose comme (h, s, l) => [h, s, 1 - l] .


Les couleurs avec lesquelles tout va bien


Parfois, la situation est réussie: le design exclusif de la lettre (ou d'une partie de celle-ci) est déjà sombre. Dans ce cas, vous n'avez pas besoin de changer quoi que ce soit, il vaut mieux être juste tranquillement heureux - probablement le designer a choisi les couleurs pas pire que notre algorithme. En HSL, regardez simplement la luminosité L. S'il est supérieur (pour le texte) ou inférieur (pour l'arrière-plan) le seuil (qui, bien sûr, est personnalisable), nous ne faisons rien.


Cirque dynamique


Bien que nous n'en ayons pas eu besoin (merci encore, désinfectant, vous m'avez sauvé de la folie!), Je vais toujours vous dire de quel type de modules complémentaires un thème adaptatif a besoin pour assombrir les pages entières, et pas seulement des lettres statiques stupides des années 90. Plus précisément, c'est une tâche pour ceux qui aiment l'odeur des sélecteurs le matin.


Styles en ligne dynamiques


Le cas le plus simple qui casse notre page assombrie change les styles en ligne. L'opération est fréquente, mais la correction est simple: ajoutez MutationObserver et réparez rapidement les styles en ligne lors du changement.


Styles externes


Travailler avec des styles de <link> depuis l'intérieur de la page est plutôt pénible en raison de l'asynchronie et de @import , et @import n'est plus amusant. Il semble que ce problème pourrait être résolu de façon assez élégante par le biais d'un travailleur Web (proxy pour *.css ).


Styles dynamiques


Enfin, rassemblant tous nos problèmes, nous rappelons que le script peut généralement ajouter, supprimer et réorganiser (spécificité! Cascade!) <style> et <link> , et même changer les règles dans <style> . Tout est résolu par le même MutationObserver pour les éléments de style, mais pour chaque changement, il y a plus de traitement.


Variables CSS


Une toute nouvelle vague de folie survient lorsque les variables CSS entrent en jeu. Nous ne pouvons pas obscurcir les variables elles-mêmes: même si nous supposons que nous devinerons par le format que la variable contient de la couleur (bien que je ne vous conseillerais pas de le faire), on ne sait pas dans quel rôle elle nous rencontrera - fond, texte, bordure, tout d'un coup? De plus, les valeurs des variables sont héritées, nous devons donc considérer non seulement les styles, mais aussi les éléments auxquels elles sont appliquées, et tout cela dégénère et explose rapidement.


Si les variables CSS atteignent le courant dominant, nous avons un problème. D'un autre côté, à ce moment-là, color() sera déjà démarré, avec lequel il sera possible de ne pas changer les couleurs dans JS, mais simplement de remplacer les couleurs par color(var(--bg) lightness(-50%)) .


Résumé



Dans notre cas, lorsque le désinfectant ne laisse que des styles en ligne, la gradation adaptative au niveau CSS fonctionne très bien: elle donne la meilleure qualité de gradation, ne casse pas les lettres et fonctionne relativement rapidement et facilement. Je ne sais pas si l'option avec tout le rembourrage pour la dynamique en vaut la peine. Heureusement, si vous travaillez avec du contenu généré par les utilisateurs et n'écrivez pas de navigateur, votre désinfectant devrait faire de même.


En pratique, le mode adaptatif doit être utilisé avec la redéfinition des styles: les styles ne sont généralement pas explicitement appliqués aux éléments standard comme <input> ou <a> , mais par défaut ils sont légers.


Comment assombrir les images


Repeindre des photos est un problème distinct qui me dérange personnellement. C'est intéressant, et j'ai enfin la chance d'utiliser l'expression «analyse spectrale». Il y a plusieurs problèmes courants avec les images sur un sujet sombre.


Premièrement, les images sont trop claires. Cela fonctionne de la même manière que les lettres non peintes avec lesquelles tout a commencé. Ce sont souvent (mais pas nécessairement) des photographies ordinaires. Étant donné que la mise en page des newsletters n'est pas très amusante, de nombreux gars exportent simplement la partie complexe de la lettre sous forme d'image, elle ne repeint pas et illumine la nuit mon perfectionnisme. De telles images doivent être assombries, mais pas inversées - sinon un terrible négatif sortira.



Deuxièmement, des images sombres avec une réelle transparence. Ce problème se retrouve souvent sur les logos - ils sont conçus pour un fond clair et, lorsque nous le remplaçons par un sombre, fusionnent avec lui. Ces images doivent être inversées.



Quelque part au milieu, il y a des images pour lesquelles le blanc représente un "fond transparent", mais maintenant elles se trouvent juste dans un étrange rectangle blanc. Dans un monde idéal, nous remplacerions un fond blanc par un fond transparent, mais si vous avez déjà travaillé avec une baguette magique dans un éditeur de photos, alors vous savez que le faire automatiquement n'est pas si facile.



Il est intéressant de noter que parfois les images n'ont aucune signification - ce sont des pixels de suivi et des «supports de format» dans une disposition particulièrement perverse. Ceux-ci peuvent être rendus invisibles en toute sécurité (disons, opacity: 0 ).



Heuristique d'introspection


Pour décider quoi faire de l'image, nous devons pénétrer à l'intérieur et analyser son contenu - et de manière simple et rapide. Selon notre ensemble de problèmes, la première version de l'algorithme se profile. Elle est là.


Nous considérons les pixels sombres, clairs et transparents dans l'image, et pas tous, mais de manière sélective - une optimisation évidente. Nous déterminons la luminosité globale de l'image (claire, sombre, moyenne) et la présence de transparence. Inversez les images sombres avec transparence, la lumière sans transparence - muet, ne touchez pas le reste.


La joie de cette merveilleuse heuristique a pris fin lorsque je suis tombé sur un bulletin de charité avec une photo d'une leçon dans une école africaine. Tout irait bien, mais le concepteur l'a centré, en ajoutant des pixels transparents le long des bords. Nous ne voulions pas nous retrouver au centre d'une nouvelle histoire sur la reconnaissance offensive des images, et nous avons décidé de ne pas du tout faire de traitement d'image dans la première version.


À l'avenir, des heuristiques supplémentaires, que j'appelle simplement «analyse spectrale», devraient protéger contre de tels problèmes - nous comptons le nombre de couleurs différentes dans l'image et inversons seulement s'il y en a peu. Le même critère peut être utilisé pour rechercher des images lumineuses graphiques et les repeindre également - cela semble tentant.



Résumé


Pour un sujet sombre à part entière dans le courrier, nous manquions de repeindre les lettres avec des styles et nous avons trouvé comment les organiser. Deux options simples en CSS pur - redéfinir les styles et un filtre CSS - n'ont pas fonctionné: la première est trop dure sur la conception d'origine, la seconde ne fonctionne tout simplement pas bien. En conséquence, nous utilisons une gradation adaptative - nous analysons les styles, remplaçons les couleurs par des couleurs plus appropriées et les récupérons. Maintenant, nous travaillons à développer le thème en images - pour cela, nous devons analyser leur contenu et n'en repeindre que quelques-uns.


Si vous avez besoin de repeindre du code HTML personnalisé sur un thème sombre, gardez à l'esprit trois méthodes:


  • Remplacement des styles - vous en avez besoin de toute façon pour votre application principale, à moindre coût et avec colère, mais il tue toutes les couleurs d'origine.
  • Le filtre CSS est cool, mais il fonctionne comme ça. Utilisez uniquement pour les éléments opaques (en termes d'accès) tels que les cadres ou les composants Web.
  • Conversion de styles - assombrit la très haute qualité, mais plus compliquée que les autres méthodes.

Même si vous ne faites jamais ça, j'espère que vous vous êtes amusé!


Liens utiles :


  1. Si vous êtes intéressé à discuter de ce sujet de manière animée et dans le contexte du développement pour Android, nous vous invitons à visiter le bureau de Yandex à Pétersbourg le 18 avril.


  2. Récemment, nous avons parlé de résoudre un autre problème des utilisateurs de messagerie - les problèmes de diffusion.


Source: https://habr.com/ru/post/fr446780/


All Articles