Wie Sie vielleicht wissen, sind die jüngsten Updates und Ergänzungen zu CSS äußerst leistungsfähig. Von Flexbox über Grid bis hin zu – worum es hier geht – benutzerdefinierten Eigenschaften (auch bekannt als CSS-Variablen) machen robuste und dynamische Layouts und Benutzeroberflächen einfacher als je zuvor und eröffnen viele andere Möglichkeiten, von denen wir früher nur geträumt haben.
Neulich dachte ich, es müsse einen Weg geben, benutzerdefinierte Eigenschaften zu verwenden, um den Hintergrund eines Elements einzufärben, während ein ausreichender Kontrast zur Vordergrundfarbe (entweder Weiß oder Schwarz) erhalten bleibt, um die WCAG AA Zugänglichkeitsstandards zu erfüllen.
Es ist erstaunlich effizient, dies in JavaScript mit ein paar Codezeilen zu tun
var rgb = [255, 0, 0];
function setForegroundColor() {
var sum = Math.round(((parseInt(rgb[0]) * 299) + (parseInt(rgb[1]) * 587) + (parseInt(rgb[2]) * 114)) / 1000);
return (sum > 128) ? 'black' : 'white';
}
Dies nimmt die Rot-, Grün- und Blauwerte (RGB) der Hintergrundfarbe eines Elements, multipliziert sie mit einigen speziellen Zahlen (299, 587 und 144), addiert sie und teilt die Summe dann durch 1.000. Wenn diese Summe größer als 128 ist, wird Schwarz zurückgegeben, andernfalls erhalten wir Weiß. Nicht schlecht.
Das einzige Problem ist, dass wir beim Nachbilden in CSS keinen Zugriff auf eine native `if`-Anweisung haben, um die Summe auszuwerten. Wie können wir das also in CSS ohne eine solche nachbilden?
Glücklicherweise kann CSS, wie HTML, sehr fehlertolerant sein. Wenn wir einen Wert größer als 255 an die RGB-Funktion übergeben, wird er auf 255 begrenzt. Das Gleiche gilt für Zahlen unter 0. Selbst negative ganze Zahlen werden auf 0 begrenzt. Anstatt also zu prüfen, ob unsere Summe größer oder kleiner als 128 ist, ziehen wir 128 von unserer Summe ab, was uns eine positive oder negative ganze Zahl ergibt. Wenn wir sie dann mit einem großen negativen Wert (z. B. -1.000) multiplizieren, erhalten wir entweder sehr große positive oder negative Werte, die wir dann an die RGB-Funktion übergeben können. Wie ich bereits sagte, wird dies auf die vom Browser gewünschten Werte begrenzt.
Hier ist ein Beispiel mit CSS-Variablen
:root {
--red: 28;
--green: 150;
--blue: 130;
--accessible-color: calc(
(
(
(
(var(--red) * 299) +
(var(--green) * 587) +
(var(--blue) * 114)
) / 1000
) - 128
) * -1000
);
}
.button {
color:
rgb(
var(--accessible-color),
var(--accessible-color),
var(--accessible-color)
);
background-color:
rgb(
var(--red),
var(--green),
var(--blue)
);
}
Wenn meine Mathematik stimmt (und es ist sehr möglich, dass sie nicht stimmt), erhalten wir eine Gesamtsumme von 16.758, was weit größer als 255 ist. Wenn wir diese Gesamtsumme für alle drei Werte an die `rgb()`-Funktion übergeben, setzt der Browser die Textfarbe auf Weiß.
Wenn Sie ein paar Regler hinzufügen, um die `color`-Werte anzupassen, erhalten Sie ein dynamisches UI-Element, das die Textfarbe basierend auf seiner `background-color` ändern kann, während es weiterhin eine gute Note bei WCAG AA erzielt.
Sehen Sie sich den Pen an
CSS Only Accessible Button von Josh Bader (@joshbader)
auf CodePen.
Dieses Konzept praktisch anwenden
Unten ist ein Pen, der zeigt, wie diese Technik verwendet werden kann, um eine Benutzeroberfläche zu gestalten. Ich habe die Variable `--accessible-color` dupliziert und in die spezifischen CSS-Regeln verschoben, die sie benötigen, und um sicherzustellen, dass Hintergründe basierend auf ihren Vordergründen zugänglich bleiben, habe ich die Variable `--accessible-color` an mehreren Stellen mit -1 multipliziert. Die Farben können mit den Bedienelementen unten rechts geändert werden. Klicken Sie auf das Zahnradsymbol, um darauf zuzugreifen.
Sehen Sie sich den Pen an
CSS Variable Accessible UI von Josh Bader (@joshbader)
auf CodePen.
Es gibt andere Wege, dies zu tun
Vor einiger Zeit erklärte Facundo Corradini, wie etwas sehr Ähnliches in diesem Beitrag gemacht werden kann. Er verwendet eine leicht andere Berechnung in Kombination mit der `hsl`-Funktion. Er geht auch detailliert auf einige Probleme ein, die er bei der Entwicklung des Konzepts hatte.
Einige Farbtöne werden sehr problematisch (insbesondere Gelb und Cyan), da sie viel heller als andere angezeigt werden (z. B. Rot und Blau), obwohl sie den gleichen Helligkeitswert haben. Infolgedessen werden einige Farben als dunkel behandelt und erhalten eine weiße Schrift, obwohl sie extrem hell sind.
Was zum Teufel ist in CSS los?
Er erwähnt weiter, dass Edge seine großen Zahlen nicht abschneidet, und während meines Tests habe ich festgestellt, dass es manchmal funktionierte und manchmal nicht. Wenn jemand herausfinden kann, warum das so sein könnte, kann er es gerne in den Kommentaren mitteilen.
Darüber hinaus erklärt Ana Tudor, wie `filter` + `mix-blend-mode` dabei helfen können, Text gegen komplexere Hintergründe abzuheben. Und wenn ich komplex sage, meine ich *komplex*. Sie demonstriert sogar, wie sich die Textfarbe ändert, wenn sich Teile der Hintergrundfarbe ändern – ziemlich fantastisch!
Außerdem erklärt Robin Rendle, wie `mix-blend-mode` zusammen mit Pseudoelementen verwendet werden kann, um Textfarben basierend auf ihrer `background-color` automatisch umzukehren.
Betrachten Sie dies also als einen weiteren Ansatz, den Sie in die Mischung einbringen können. Es ist unglaublich großartig, dass benutzerdefinierte Eigenschaften diese Art von Möglichkeiten für uns eröffnen und es uns ermöglichen, dasselbe Problem auf verschiedene Weise zu lösen.
Ziemlich clever
Das ist großartig! Ich liebe den Pen, den Sie zu „Dieses Konzept praktisch anwenden“ gemacht haben.
Danke für den Artikel! Woher haben Sie die „speziellen Zahlen“?
Das W3C hat sie hier aufgelistet... https://www.w3.org/TR/AERT/#color-contrast
Ich habe keine Ahnung warum, aber es funktioniert nicht auf Gecko 56
Die Probleme beim Begrenzen von Zahlen könnten mit dem Betriebssystem und der Hardwarekonfiguration zusammenhängen. Die Art und Weise, wie Farbfunktionen implementiert werden, kann leicht davon abhängen, ob der Browser Zugriff auf Hardwarebeschleunigung hat oder nicht. In der Vergangenheit habe ich ähnliche Fehler für den „Gooey-Effekt“ (in diesem Fall den Alpha-Wert) beobachtet, obwohl dies inzwischen behoben zu sein scheint.
Josh, das ist eine coole Idee, aber ich befürchte, deine Mathematik ist nicht korrekt – weder in JavaScript noch in CSS. Die von dir erwähnten „speziellen Zahlen“ werden auf dieser W3C-Seite schlecht erklärt. Tatsächlich müssen Rot, Blau und Grün zuerst normalisiert (in einen Bereich von 0 bis 1 gesetzt) und dann linearisiert (von sRGB in LinearRGB konvertiert) werden, bevor sie in diese Formel eingegeben werden. Der letztere Schritt fehlt in vielen Implementierungen, einschließlich beispielsweise der `yiq()` Sass-Funktion von Bootstrap, aber es ist absolut die einzig korrekte Berechnung, und es gibt W3C- und WCAG-Dokumente, die korrekt darauf verweisen. Leider erfordert dies eine `pow()`-Funktion, die JavaScript hat, CSS aber nicht, sodass eine genaue Emulation davon mit `calc()` nicht funktionieren wird.
Dieses W3C-Dokument zeigt korrekt an, dass die RGB-Werte linearisiert werden müssen. Das vorherige Dokument, das Sie verlinkt haben, erwähnt „Dieser Algorithmus ist aus einer Formel zur Umwandlung von RGB-Werten in YIQ-Werte übernommen“, ohne zu erklären, dass YIQ-Farbe davon ausgeht, dass die Gamma bereits linear ist.
Und laut caniuse.com ist `–var()` für IE11 immer noch keine Option, was ich durch wütende Gegenreaktionen von verschiedenen Kunden feststelle, die ihn immer noch verwenden...
Tolle Idee! Wie könnten wir das in SCSS übersetzen? Das würde das Problem der IE11-Kompatibilität umgehen.
Ich habe später tatsächlich die YIQ-Funktion von Bootstrap unter https://getbootstrap.com/docs/4.3/getting-started/theming/#color-contrast gefunden. Ich nehme an, das ist dasselbe?
Es ist dieselbe Idee, aber leider mathematisch falsch ♂️
Lu, das war meine Sorge. Es wäre großartig, wenn wir die Funktion überschreiben könnten, um sie richtig zum Laufen zu bringen.
@Glenn
Dieser Pen enthält die korrekte Mathematik, auch eine Kopie von Bootstraps yiq() zum Vergleich. Seien Sie sich jedoch bewusst, dass dieser Code eine Sass `pow()`-Funktion benötigt. Ich verwende mathsass, um eine bereitzustellen, über eine Pen-Abhängigkeit (siehe Einstellungen). Diese Bibliothek ist gut, läuft aber in SassScript, daher ist sie langsam. Ideal wäre es, einige JavaScript-Mathematikfunktionen mit der `functions` Node API von Sass zu verknüpfen. Ich habe eine einfache Reihe davon, die ich in einen Gist stellen könnte, wenn Sie interessiert sind.
Wow, danke dafür, Lu. Ich bin definitiv interessiert. Ich denke, was auch immer die Lösung ist, sie muss einfach in ein neues Projekt integriert werden können, sonst werden meine Kollegen sie nicht verwenden – sie interessieren sich nicht so sehr für diese Dinge wie ich.
Wirklich tolle Arbeit!
Leider mag MS Edge `calc()` mit CSS-Variablen derzeit nicht :/
Muss warten, bis ich es in Produktion verwenden kann.
Großartige Arbeit, bis auf eine Sache: Ihre Farbauswahl-Schieberegler haben keine zugängliche farbige Beschriftung, die den Wert des Schiebereglers anzeigt! Wenn Sie also die quartäre Farbe (die für den Hintergrund der Farbauswahl verwendet wird) sehr hell machen, können Sie die numerischen Werte der Schieberegler nicht sehen. Ich teste dies in Chrome, daher ist es möglich, dass andere Browser Dinge anders handhaben; Sie sollten jedoch in Erwägung ziehen, einen dunklen Hintergrund hinter die Schieberegler zu legen, wenn Sie die Farbe ihres Anzeigetextes nicht steuern können.
Sie müssten die große Formel nicht wiederholen, wenn Sie sie auf `*` anwenden statt auf `:root`, indem Sie generische `–red`, `–green`, `–blue`-Variablen verwenden. Dann setzen Sie überall dort, wo Sie sie verwenden möchten, die generischen Rot-, Grün- und Blau-Variablen für diesen Selektor.
Hier ist ein Fork des Codepen: https://codepen.io/joyously/pen/QWMdXXB