CSS-Variablen + calc() + rgb() = Erzwingen von kontrastreichen Farben

Avatar of Josh Bader
Josh Bader am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

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.