
Mein Name ist Vladimir, ich bin mit einem mobilen Frontend in Yandex.Mail beschäftigt. In unserer Anwendung gab es bereits ein dunkles Thema, aber nicht genug: Wir konnten die Benutzeroberfläche und einfache Buchstaben neu streichen. Aber formatierte Buchstaben blieben hell und kontrastierten mit einer dunklen Oberfläche, die meine Augen nachts müde machen könnte.
Heute werde ich den Lesern von Habr erzählen, wie wir dieses Problem gelöst haben. Sie lernen zwei einfache Methoden kennen, die uns nicht gepasst haben - unsere Hauptmethode für das adaptive Neulackieren von Seiten und schließlich die Richtung für die nächste Iteration: das Neulackieren von Bildern. Obwohl die Aufgabe selbst - das Neulackieren von Seiten mit beliebiger Formatierung - spezifisch ist, denke ich, dass unsere Erfahrung auch für Sie nützlich sein wird.
Einfache Wege
Bevor wir zu unserem magischen „Repainter“ kommen, haben wir zwei einfache Optionen wie einen Korken ausprobiert: Hängen Sie einen zusätzlichen dunklen Stil oder einen CSS-Filter an das Element. Sie passten nicht zu uns, aber vielleicht sind sie in einigen Fällen sogar noch besser (weil es einfach = cool ist).
Stile überschreiben
Der einfachste Weg, das dunkle Thema der Anwendung selbst in CSS logisch zu erweitern: Wir hängen dunkle Stile an einen Container für Buchstaben (im Allgemeinen für den Inhalt einer anderen Person, der neu gestrichen werden muss):
.message--dark { background-color: black; color: white; }
Wenn die Elemente im Buchstaben jedoch ihre eigenen Stile haben, definieren sie unseren Stammstil neu. Nein !important
wird nicht helfen. Sie können eine Idee quetschen, indem Sie die Vererbung abhacken:
.message--dark * { background-color: black !important; color: white !important; border-color: #333 !important; }
In diesem Fall können Sie nicht darauf verzichten !important
, da der Selektor selbst nicht sehr spezifisch ist. Darüber hinaus müssen Inline-Stile neu definiert werden (und Inline-Stile mit !important
wird trotzdem gecrawlt, es ist nichts zu tun).
Unser Stil ist ziemlich ungeschickt und färbt alles gleich, daher kommt ein weiteres Problem heraus: Wahrscheinlich wollte der Designer etwas sagen, indem er Farben (Elementprioritäten und andere Designer-Dinge) arrangierte, aber wir haben diese ganze Idee aufgegriffen und verworfen.

Wenn Sie Designer weniger respektieren als ich und sich dennoch für diese Methode entscheiden, vergessen Sie nicht, nicht offensichtliche Kleinigkeiten zu beenden:
box-shadow
- nur die Farbe kann nicht neu definiert werden, Sie müssen alle Schatten entfernen oder mit hellen leben.- Farben semantischer Elemente - Links, Eingabeelemente.
- Inline-SVG - anstelle des
background
müssen die fill
und anstelle des stroke
werden. Dies ist jedoch nicht genau, je nachdem, welches SVG es sein kann, und umgekehrt.
Technisch gesehen ist die Methode nicht schlecht: Dies sind drei Codezeilen (okay, dreißig für die Produktions-Redi-Version mit Eckfällen), Kompatibilität mit allen Browsern der Welt, sofort einsatzbereite Verarbeitung dynamischer Seiten und keine Bindung an die Art und Weise, wie Stile im Originaldokument verbunden werden. Ein besonderer Bonus ist, dass Sie die Farben im Stil einfach so anpassen können, dass sie zur Hauptanwendung passen (z. B. Hintergrund #bbbbb8
anstelle von Schwarz).
Übrigens haben wir Buchstaben auf diese Weise neu gestrichen, aber wenn wir Stile im Buchstaben fanden, hatten wir Angst und ließen das Buchstabenlicht.
CSS-Filter
Sehr witzige und elegante Option. Sie können die Seite mit einem CSS-Filter neu malen:
.message--dark { filter: invert(100) hue-rotate(180deg); }
Danach werden die Fotos gruselig, aber es spielt keine Rolle - wir werden sie wieder neu streichen:
.message-dark img { filter: invert(100) hue-rotate(180deg); }

Es gibt immer noch Probleme mit Inhaltsbildern, die über den background
gebunden sind (wir wissen, dass es bequemer ist, das Seitenverhältnis anzupassen, aber was ist mit der Semantik?). Angenommen, wir können alle diese Elemente finden, explizit markieren und neu streichen.
Die Methode ist insofern gut, als sie das ursprüngliche Verhältnis von Helligkeit und Kontrasten beibehält. Auf der anderen Seite gibt es viele Probleme, die die Vorteile eher überwiegen:

- Dunkle Seiten werden heller.
- Die resultierenden Farben können nicht gesteuert werden.
#bbbbb8
Filter muss angewendet werden, um den Hintergrund für Ihre Unternehmensnummer #bbbbb8
? Das Rätsel. - Nach zwei Neulackierungen verblassen die Bilder.
- Alles verlangsamt sich (insbesondere auf Telefonen) - es ist logisch, dass der Browser anstelle eines einfachen Renderns die Bildverarbeitung auf jedem Bildschirm steuern muss.
Diese Methode wäre für Buchstaben geeignet, die aus Text in neutralen Tönen bestehen. Aber wer sind diese Ästhetiker, die einen vollständigen Posteingang mit solch eigenartigen Inhalten erhalten? Filter können jedoch Elemente neu streichen, auf deren Inhalt nicht zugegriffen werden kann - Frames, Webkomponenten, Bilder.
Responsive Theme
Es ist Zeit für Magie! Aus den Mängeln der ersten beiden Ansätze sammeln wir eine Checkliste:
- Machen Sie den Hintergrund dunkel, den Text hell, die Ränder mittel.
- Identifizieren Sie bereits dunkle Seiten und streichen Sie sie nicht neu.
- Behalten Sie das ursprüngliche Verhältnis von Helligkeit und Kontrast bei.
- Geben Sie die Möglichkeit, Farben anzupassen.
- Lassen Sie die Töne wie am Anfang.
Wir müssen die Farben der Stile ändern, damit der Hintergrund dunkel ist. Und warum nicht buchstäblich? Wir nehmen einfach alle Stile, suchen nach den Regeln für die Farben ( color
, background
, Rahmen, box-shadow
, deren Untereigenschaften) und ersetzen sie durch „abgedunkelt“ - verdunkeln Sie den Hintergrund, hellen Sie den Text auf, verdunkeln Sie die Ränder weniger als den Hintergrund usw.
Diese Methode hat einen unglaublichen Vorteil, der die Seele eines jeden Entwicklers wärmt. Jede Eigenschaft kann konfiguriert werden (ja, direkt mit einem Code beschreiben!). Ihre eigenen Regeln für die Farbkonvertierung. Mit ausreichender Vorstellungskraft können Sie in jedes externe Thema integrieren, Farbkorrekturen vornehmen (z. B. helle oder graubraune Himbeeren anstelle eines dunklen Themas) und sogar einen kleinen Kontext hinzufügen - beispielsweise breite und schmale Ränder unterschiedlich behandeln.
Nachteile sind Standard für alles in js. Ja, wir führen Skripte aus, unterbrechen die Kapselung von Stilen und analysieren CSS-Regexp. Nun, im Gegensatz zu HTML ist letzteres nicht so beschämend, weil die CSS-Grammatik (der von uns benötigten Ebene) immer noch regelmäßig ist.
Der Neulackierungsplan lautet wie folgt:
- Wir normalisieren die Legacy-Eigenschaften des Stils (
bgcolor
und friends) und verschieben sie in style="..."
. - Hier finden Sie alle Inline-Stile.
- In jedem Stil finden wir alle Farbregeln (
background-color
, color
, box-shadow
usw.). - Aus allen Farbregeln erhalten wir die Farben, wir finden den gewünschten Konverter (Dunkler für den Hintergrund, Klärer für Text).
- Wir nennen den Konverter.
- Zurücksetzen der konvertierten Regeln in CSS.
Die Bindung (Normalisierung, Stilsuche, Analyse) ist recht einfach. Wir werden herausfinden, wie genau unser magischer Konverter funktioniert.
HSL-Konvertierungen
"Dimmen der Farbe" ist keine so einfache Aktion, wie es scheint, besonders wenn wir den Ton beibehalten möchten (Cyan wird dunkelblau, nicht orange). Dies kann in normalem RGB erfolgen, ist jedoch problematisch. Fans des algorithmischen Designs wissen, dass selbst die Farbverläufe dort schief sind. Die Arbeit mit Farben in HSL ist jedoch ein reines Vergnügen: Anstelle von Rot, Grün und Blau, bei denen nicht klar ist, was zu tun ist, haben wir drei weitere Kanäle:
- Der Farbton ist genau der Ton, den wir beibehalten möchten.
- Saturaion - Sättigung, die uns jetzt nicht sehr wichtig ist.
- Helligkeit - die Helligkeit, die wir ändern werden.
Es ist zweckmäßig, sich einen solchen Raum in Form eines Zylinders vorzustellen. Und unsere Aufgabe ist es, diesen Zylinder auf den Kopf zu stellen. Die Farbkorrekturfunktionen machen so etwas wie (h, s, l) => [h, s, 1 - l]
.
Die Farben, mit denen alles gut ist
Manchmal ist die Situation erfolgreich: Das exklusive Design des Briefes (oder eines Teils davon) ist bereits dunkel. In diesem Fall müssen Sie nichts ändern. Es ist besser, einfach nur ruhig glücklich zu sein. Wahrscheinlich hat der Designer die Farben nicht schlechter als unseren Algorithmus ausgewählt. Schauen Sie sich in HSL nur die L - Helligkeit an. Wenn der Schwellenwert höher (für Text) oder niedriger (für Hintergrund) ist (was natürlich anpassbar ist), tun wir nichts.
Dynamischer Zirkus
Obwohl wir es nicht brauchten (nochmals vielen Dank, Desinfektionsmittel, Sie haben mich vor dem Wahnsinn gerettet!), Werde ich Ihnen trotzdem sagen, welche Art von Add-Ons ein adaptives Thema benötigt, um ganze Seiten abzudunkeln, und nicht nur dumme statische Buchstaben aus den neunziger Jahren. Genauer gesagt ist dies eine Aufgabe für diejenigen, die den Geruch von Selektoren am Morgen mögen.
Dynamische Inline-Stile
Der einfachste Fall, der unsere abgedunkelte Seite beschädigt, ist das Ändern von Inline-Stilen. Die Operation ist häufig, aber die Korrektur ist einfach: Fügen Sie MutationObserver
und reparieren Sie die Inline-Stile beim Ändern schnell.
Externe Stile
Das Arbeiten mit Stilen von <link>
aus dem Inneren der Seite ist aufgrund von Asynchronität und @import
ziemlich schmerzhaft, und CORS macht keinen Spaß mehr. Es scheint, dass dieses Problem durch einen Web-Worker (Proxy für *.css
) recht elegant gelöst werden könnte.
Dynamische Stile
Wenn wir schließlich alle unsere Probleme zusammenfassen, erinnern wir uns, dass das Skript im Allgemeinen <style>
und <link>
hinzufügen, löschen und neu anordnen kann (Spezifität! Kaskade!) Und sogar die Regeln in <style>
ändern kann. Alles wird von demselben MutationObserver
für Stilelemente gelöst, aber für jede Änderung wird mehr verarbeitet.
CSS-Variablen
Eine ganz neue Runde des Wahnsinns kommt, wenn CSS-Variablen ins Spiel kommen. Wir können die Variablen selbst nicht verschleiern: Selbst wenn wir davon ausgehen, dass wir anhand des Formats erraten werden, dass die Variable Farbe enthält (obwohl ich Ihnen nicht raten würde, dies zu tun), ist nicht bekannt, in welcher Rolle sie uns treffen wird - Hintergrund, Text, Rahmen auf einmal? Darüber hinaus werden die Werte der Variablen vererbt, sodass wir nicht nur die Stile berücksichtigen müssen, sondern auch die Elemente, auf die sie angewendet werden. All dies eskaliert schnell und explodiert.
Wenn CSS-Variablen in den Mainstream gelangen, haben wir ein Problem. Andererseits wird zu diesem Zeitpunkt bereits color()
gestartet, mit dem es möglich sein wird, die Farben in JS nicht zu ändern, sondern die Farben einfach durch color(var(--bg) lightness(-50%))
ersetzen.
Zusammenfassung

In unserem Fall funktioniert das adaptive Dimmen auf CSS-Ebene einwandfrei, wenn das Desinfektionsmittel nur Inline-Stile belässt: Es bietet die beste Dimmqualität, bricht keine Buchstaben und arbeitet relativ schnell und einfach. Ich bin mir nicht sicher, ob sich die Option mit all der Füllung für die Dynamik lohnt. Wenn Sie mit benutzergenerierten Inhalten arbeiten und keinen Browser schreiben, sollte Ihr Desinfektionsprogramm dies ebenfalls tun.
In der Praxis sollte der adaptive Modus zusammen mit der Neudefinition von Stilen verwendet werden: Stile werden normalerweise nicht explizit auf Standardelemente wie <input>
oder <a>
angewendet, sind jedoch standardmäßig leicht.
So verdunkeln Sie Bilder
Das Neulackieren von Bildern ist ein separates Problem, das mich persönlich stört. Das ist interessant und ich habe endlich die Möglichkeit, den Ausdruck „Spektralanalyse“ zu verwenden. Bei Bildern in einem dunklen Motiv treten häufig Probleme auf.
Erstens sind die Bilder zu hell. Es funktioniert genauso wie unbemalte Buchstaben, mit denen alles begann. Oft (aber nicht unbedingt) sind dies gewöhnliche Fotografien. Da das Layout der Newsletter nicht sehr lustig ist, exportieren viele Leute einfach den schwierigen Teil des Briefes als Bild, es malt nicht neu und beleuchtet nachts meinen Perfektionismus. Solche Bilder müssen abgedunkelt, aber nicht invertiert werden - sonst kommt ein schreckliches Negativ heraus.

Zweitens dunkle Bilder mit echter Transparenz. Dieses Problem tritt häufig bei Logos auf - sie sind für einen hellen Hintergrund konzipiert und werden, wenn wir sie durch einen dunklen ersetzen, mit diesem zusammengeführt. Solche Bilder müssen invertiert werden.

Irgendwo in der Mitte gibt es Bilder, für die Weiß einen "transparenten Hintergrund" darstellt, aber jetzt stehen sie nur noch in einem seltsamen weißen Rechteck. In einer idealen Welt würden wir einen weißen Hintergrund durch einen transparenten ersetzen. Wenn Sie jedoch jemals mit einem Zauberstab in einem Bildbearbeitungsprogramm gearbeitet haben, wissen Sie, dass dies nicht so einfach ist.

Es ist interessant, dass Bilder manchmal überhaupt keine Bedeutung haben - dies sind Tracking-Pixel und „Formathalter“ in einem besonders perversen Layout. Diese können sicher unsichtbar gemacht werden (z. B. opacity: 0
).

Introspektionsheuristik
Um zu entscheiden, was mit dem Bild geschehen soll, müssen wir hineingehen und seinen Inhalt analysieren - und das auf einfache und schnelle Weise. Entsprechend unseren Problemen taucht die erste Version des Algorithmus auf. Da ist sie.
Wir betrachten dunkle, helle und transparente Pixel im Bild und nicht alle, sondern selektiv - offensichtliche Optimierung. Wir bestimmen die Gesamthelligkeit des Bildes (hell, dunkel, mittel) und das Vorhandensein von Transparenz. Dunkle Bilder mit Transparenz umkehren, Licht ohne Transparenz - stumm schalten, den Rest nicht berühren.
Die Freude an dieser wunderbaren Heuristik endete, als ich auf einen Wohltätigkeits-Newsletter mit einem Foto einer Lektion in einer afrikanischen Schule stieß. Alles wäre in Ordnung, aber der Designer zentrierte es und fügte transparente Pixel entlang der Ränder hinzu. Wir wollten uns nicht im Zentrum einer neuen Geschichte über die anstößige Erkennung von Bildern befinden, und wir haben beschlossen, in der ersten Version überhaupt keine Bildverarbeitung durchzuführen.
In Zukunft sollten zusätzliche Heuristiken, die ich nur als „Spektralanalyse“ bezeichne, vor solchen Problemen schützen - wir zählen die Anzahl der verschiedenen Farben im Bild und invertieren nur, wenn es nur wenige davon gibt. Das gleiche Kriterium kann verwendet werden, um nach grafischen Lichtbildern zu suchen und diese auch neu zu streichen - es klingt verlockend.

Zusammenfassung
Für ein vollwertiges dunkles Thema in der Mail fehlten uns das Neulackieren von Buchstaben mit Stilen, und wir fanden heraus, wie wir es arrangieren sollten. Zwei einfache Optionen in reinem CSS - die Neudefinition von Stilen und ein CSS-Filter - haben nicht funktioniert: Die erste ist zu hart für das ursprüngliche Design, die zweite funktioniert einfach nicht gut. Aus diesem Grund verwenden wir adaptives Dimmen - wir analysieren Stile, ersetzen Farben durch geeignetere und sammeln sie zurück. Jetzt arbeiten wir daran, das Thema in Bilder zu erweitern - dafür müssen wir deren Inhalt analysieren und nur wenige neu streichen.
Wenn Sie jemals benutzerdefiniertes HTML für ein dunkles Thema neu streichen müssen, beachten Sie drei Methoden:
- Überschreiben von Stilen - Sie benötigen es trotzdem für Ihre Hauptanwendung, billig und verärgert, aber es tötet alle Originalfarben.
- CSS-Filter ist cool, aber es funktioniert so lala. Nur für undurchsichtige (in Bezug auf den Zugriff) Elemente wie Frames oder Webkomponenten verwenden.
- Konvertieren von Stilen - verdunkelt sehr hohe Qualität, ist jedoch komplizierter als andere Methoden.
Auch wenn Sie dies nie tun, hoffe ich, dass Sie Spaß hatten!
Nützliche Links :
Wenn Sie daran interessiert sind, dieses Thema lebhaft und im Kontext der Entwicklung für Android zu diskutieren, laden wir Sie ein, am 18. April das Büro von Yandex in Petersburg zu besuchen .
Kürzlich haben wir darüber gesprochen , ein anderes Problem von Mail-Benutzern zu lösen - Mailing-Probleme.