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!
Wenn man
<meter>verwendet, müsste man die Längen nicht in das CSS codieren.Meter ist sicherlich eine (untergenutzte) Markup-Option, mit oder ohne meinen Ansatz – eine von vielen gültigen und semantischen Möglichkeiten, ein Balkendiagramm zu formatieren. Aber das ignoriert alles andere, was wir mit der einzelnen Variable tun können, *zusätzlich zum Setzen von Längen*. Es geht hier nicht um den "richtigen" Ansatz für Balkendiagramme, sondern darum, Daten auf direktere und dynamischere Weise zu ermöglichen, Stile zu beeinflussen.
Was halten Sie davon, gedrehte s in einer Tabelle zu verwenden? Ich denke, das ist semantischer, einfacher zu implementieren und hat eine bessere Unterstützung.
Ich weiß, der Punkt war CSS. Frage rein aus Interesse, um Gedanken zu bekommen.
Ich würde gerne eine Demo sehen, um zu verstehen, was Sie mit "gedrehte s in einer Tabelle verwenden" meinen. Wie genau sehen Sie das als einfacher zu implementieren? Ich denke, eine Tabelle wäre großartig – sogar so formatiert, wie ich es im Artikel tue.
Es gibt viele Möglichkeiten, ein Diagramm zu erstellen. Wie Sie bemerken: Mich interessiert hauptsächlich, wie Variablen mehr datengesteuerte Formatierung ohne Bloat und einen einzigen Inline-Wert ermöglichen, der dynamisch von verschiedenen CSS-Eigenschaften verwendet werden kann. Diese Flexibilität existiert nirgendwo außerhalb von benutzerdefinierten Eigenschaften.
Dies ist eine der praktischsten Anwendungen von CSS-benutzerdefinierten Eigenschaften, die ich bisher gesehen habe, und könnte ohne viel Aufwand schrittweise verbessert werden. Gut gemacht!
Schönes Tutorial zum Zeichnen von Diagrammen mit reinem CSS.
Sie können OPERA 47.0.263 zu Ihrer Liste von Browsern hinzufügen, die
calc-Werte erfolgreich verarbeiten.Viele Grüße
Chris2mop
Danke, super Beitrag!
Nebenbei bemerkt, rein aus der Perspektive der Barrierefreiheit denke ich, dass Definitionslisten keine Zuordnungen zu Betriebssystem-Barrierefreiheits-APIs haben, was bedeutet, dass Screenreader-Benutzer nicht von den implizierten Beziehungen profitieren würden.