Mehr CSS-Diagramme, mit Grid & benutzerdefinierten Eigenschaften

Avatar of Miriam Suzanne
Miriam Suzanne am

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

Ich liebte Robins kürzlichen Beitrag, in dem er mit CSS Grid für Balkendiagramme experimentierte. Ich habe tatsächlich einen ähnlichen Ansatz für ein Kundenprojekt verwendet, um mit CSS Grid einen Tagesplaner zu erstellen. Es ist ein anderer Anwendungsfall, aber dieselbe grundlegende Technik: Rasterlayouts zur Visualisierung von Daten.

(Ich empfehle, zuerst Robins Artikel zu lesen, da ich auf seinem Diagramm aufbaue.)

Robins Ansatz beruht auf einer großen Sass-Schleife zur Generierung von 100 potenziellen Klassennamen, obwohl im endgültigen Diagramm weniger als 12 verwendet werden. In der Produktion möchten wir etwas Direkteres und Performanteres mit besserer Semantik. Daher habe ich zu Definitionslisten und CSS-Variablen (auch benutzerdefinierte Eigenschaften genannt) gegriffen, um meine Diagramme zu erstellen.

Hier ist das Endergebnis

Siehe den Pen Balkendiagramm in CSS-Grid + Variablen von Miriam Suzanne (@mirisuzanne) auf CodePen.

Lass uns eintauchen!

Markup zuerst

Robin hat ein konzeptionelles Experiment vorgeschlagen und daher viele reale Daten- und Barrierefreiheitsaspekte weggelassen. Da ich auf (fiktiven) Produktionscode abzielen möchte, möchte ich sicherstellen, dass er semantisch und barrierefrei ist. Ich habe die Idee der Jahresachse aus einem Kommentar zu Robins Diagrammen übernommen und alles in eine Definitionsliste verschoben. Jedes Jahr ist in der Liste mit einem entsprechenden Prozentsatz verbunden.

<dl class="chart">
  <dt class="date">2000</dt>
  <dd class="bar">45%</dd>

  <dt class="date">2001</dt> 
  <dd class="bar">100%</dd>
  
  <!-- etc… -->
</dl>

Es gibt wahrscheinlich andere Möglichkeiten, dies barrierefrei zu kennzeichnen, aber eine dl schien mir sauber und klar – mit allen Daten und zugehörigen Paaren als strukturiertem Text. Standardmäßig zeigt dies Jahres-/Prozentpaare in einem lesbaren Format an. Jetzt müssen wir es schön machen.

Grid-Setup

Ich habe von Robins Grid ausgegangen, aber mein Markup erfordert eine zusätzliche Zeile für die .date-Elemente. Ich füge diese am Ende meines grid-template-rows hinzu und platziere meine Datums-/Balkenelemente entsprechend.

.chart {
  display: grid;
  grid-auto-columns: 1fr;
  grid-template-rows: repeat(100, 1fr) 1.4rem;
  grid-column-gap: 5px;
}

.date {
  /* fill the bottom row */
  grid-row-start: -2;
}

.bar {
  /* end before the bottom row */
  grid-row-end: -2;
}

Normalerweise würde ich für diese letzte Zeile auto verwenden, aber ich benötigte eine explizite Höhe, damit das Hintergrundgitter richtig funktioniert. Nicht den Aufwand wert, wahrscheinlich, aber ich hatte Spaß.

Daten an CSS übergeben

An diesem Punkt hat CSS keinen Zugriff auf die relevanten Zahlen für die Formatierung eines Diagramms. Wir haben keine Hooks, um einzelne Balken auf unterschiedliche Höhen einzustellen. Robins Lösung beinhaltet individuelle Klassennamen für jeden Balkenwert mit einer Sass-Schleife zur Erstellung benutzerdefinierter Klassen für jeden Wert. Das funktioniert, aber wir landen mit einer langen Liste von Klassen, die wir vielleicht nie verwenden. Gibt es eine Möglichkeit, Daten direkter in CSS zu übergeben?

Der direkteste Ansatz könnte ein Inline-Style sein.

<dd class="bar" style="grid-row-start: 56">45%</dd>

Die Startposition ist die Gesamtzahl der Gitterlinien (eins mehr als die Anzahl der Zeilen, also 101 in diesem Fall) minus dem Gesamtwert des gegebenen Balkens: 101 - 45 = 56. Das funktioniert gut, aber jetzt sind unser Markup und CSS eng miteinander verknüpft. Mit CSS-Variablen können wir Rohdaten übergeben und CSS die Entscheidung treffen lassen, wie sie verwendet werden.

<dd class="bar" style="--start: 56">45%</dd>

Im CSS können wir das mit grid-row-start verknüpfen.

.bar {
  grid-row-start: var(--start);
}

Wir haben die Klassennamen-Schleife und die aufgeblähte 100-Klassen-Ausgabe durch eine einzige Zeile dynamisches CSS ersetzt. Variablen beseitigen auch die Gefahr, die normalerweise mit CSS-in-HTML verbunden ist. Während eine Inline-Eigenschaft wie grid-row-start praktisch unmöglich aus einer CSS-Datei überschrieben werden kann, kann die Inline-Variable einfach von CSS ignoriert werden. Es gibt keine Spezifitäts-/Kaskadenprobleme, um die man sich Sorgen machen müsste.

Datengetriebene Hintergründe

Als Bonus können wir mit den Daten mehr tun, als nur eine Gitterposition bereitzustellen – wir können sie wiederverwenden, um eine Fallback-Option zu formatieren, oder sogar die Balkenfarben basierend auf denselben Daten anpassen.

.bar {
  background-image: linear-gradient(to right, green, yellow, orange, red);
  background-size: 1600% 100%;
  
  /* turn the start value into a percentage for position on the gradient */
  background-position: calc(var(--start) * 1%) 0;
}

Ich begann mit einem horizontalen Farbverlauf von Grün über Gelb, Orange und dann Rot. Dann verwendete ich background-size, um den Verlauf viel breiter als den Balken zu machen – mindestens 200 % pro Farbe (800 %). Größere Verlaufbreiten machen den Übergang weniger sichtbar, daher wählte ich 1600 %, um ihn subtil zu halten. Schließlich kann ich mit calc(), um unsere Startposition (1-100) in einen Prozentsatz umzurechnen, die Hintergrundposition nach links oder rechts basierend auf dem Wert anpassen – und je nach Prozentsatz eine andere Farbe anzeigen.

Das Hintergrundgitter wird ebenfalls mit Variablen und Hintergrundverläufen generiert. Leider macht die Subpixel-Rundung es etwas unzuverlässig, aber Sie können mit dem Wert --line-every spielen, um den Detailgrad zu ändern. Schauen Sie sich um und sehen Sie, welche anderen Verbesserungen Sie vornehmen können!

Skalierung hinzufügen [ohne Firefox]

Im Moment übergeben wir eine Startposition anstelle eines reinen Wertes ("56" für "45%"). Diese Startposition basiert auf der Annahme, dass die Gesamtskala 100% beträgt. Um dies zu einem flexibleren Werkzeug zu machen, dachte ich, es wäre lustig, die gesamte Mathematik, einschließlich der Skala, in CSS zu kapseln. So würde es aussehen.

<dl class="chart" style="--scale: 100">
  <dt class="date">2000</dt>
  <dd class="bar" style="--value: 45">45%</dd>

  <dt class="date">2001</dt> 
  <dd class="bar" style="--value: 100">100%</dd>
  
  <!-- etc… -->
</dl>

Dann können wir den --start-Wert in CSS berechnen, bevor wir ihn anwenden.

.bar {
  --start: calc(var(--scale) + 1 - var(--value));
  grid-row-start: var(--start);
}

Mit der Gesamt-Skala und den Einzelwerten in CSS können wir beide individuell manipulieren. Ändern Sie die Skala auf 200% und beobachten Sie, wie sich das Diagramm entsprechend aktualisiert.

Siehe den Pen Balkendiagramm mit Verkauf – kein Firefox von Miriam Suzanne (@mirisuzanne) auf CodePen.

Sowohl Chrome als auch Safari handhaben es gut, aber Firefox scheint mit calc-Werten bei der Gitterplatzierung unzufrieden zu sein. Ich gehe davon aus, dass sie es irgendwann reparieren werden. Vorerst müssen wir einige Berechnungen aus unserem CSS weglassen.

Traurig, aber wir werden uns daran gewöhnen. 😉

Es gibt noch viel mehr, was wir tun könnten, indem wir Fallbacks für ältere Browser bereitstellen – aber ich denke, dies ist eine gangbare Option mit dem Potenzial, barrierefrei, semantisch, performant und schön zu sein. Danke, dass du diese Konversation begonnen hast, Robin!