Überlappende Balkendiagramme

Avatar of Saleh Mubashar
Saleh Mubashar am

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

Wie der Name schon sagt, visualisieren überlappende Diagramme zwei verschiedene Datensätze in einer einzigen Grafik. Die Idee ist, dass die überlappenden Balken es uns ermöglichen, Daten zu vergleichen, zum Beispiel von Jahr zu Jahr. Sie sind auch nützlich für Dinge wie das Verfolgen des Fortschritts bei einem Ziel, bei dem ein Balken das Ziel repräsentiert und der andere den aktuellen Betrag zeigt.

Aber sie sind auch schön!

A two-by-two grid of overlapping chart examples.

Dein Verstand ist wahrscheinlich wie meiner und beginnt bereits darüber nachzudenken, wie du das programmieren würdest. So habe ich es angepackt.

Das HTML

Wir beginnen mit dem Markup, denn so wissen wir, was gestylt werden muss.

<div class="container">
  <div class="chart">
    <dl class="numbers">
      <dd><span>100%</span></dd>
      <!-- all the way to 0% -->
    </dl>
    <dl class="bars">
      <div>
          <dt>2018</dt>
          <dd>
            <div class="bar" data-percentage="50"></div>
            <div class="bar overlap" data-percentage="53"></div>
          </dd>
        </div>
      <div>
      <!-- more bars -->
    </dl>
  </div>
</div>

Wir werden Beschreibungslisten (<dl>) verwenden, da dies ein semantischerer Ansatz ist als normale geordnete und ungeordnete Listen. Ein weiterer Grund ist, dass wir innerhalb jedes Balkens eine Beschriftung einfügen. Normale Listen haben keinen Tag, um einen Titel oder eine Beschreibung hinzuzufügen, im Gegensatz zu Definitionslisten. Einfach ausgedrückt, es ergibt einfach mehr Sinn und ist auch besser lesbar.

Die erste Beschreibungsliste, .numbers, ist die y-Achse. .bars ist, wo die Daten visualisiert werden, und ich habe eine Definitionsliste erstellt, um auch die x-Achse zu erstellen. Jedes Listenelement enthält einen .bar und die Beschriftung als Definitionsbegriff (dt).

Und was ist mit dem Datenattribut? data-percentage wird verwendet, um die Höhe des Balkens anzugeben, was letztendlich seinen Wert auf der y-Achse darstellt. Wir könnten ihn manuell in CSS für jeden Balken festlegen, aber das ist repetitiv und viel zusätzlicher Code, der durch ein paar Zeilen CSS ersetzt werden kann.

Die grundlegenden Diagrammstile

Wir arbeiten mit vielen zweidimensionalen Richtungen, daher wird Flexbox unser Freund sein, um alles auszurichten. Wir können das .chart-Element zu einem flexiblen Container machen, der die y-Achsenbeschriftungen und das Diagramm nebeneinander in row-Richtung positioniert.

.chart {
  display: flex;
}

Wir müssen die Richtung nicht einmal angeben, da Flexbox standardmäßig auf row eingestellt ist. Tun wir das und fügen dann Flexbox zur Liste der Beschriftungen entlang der y-Achse hinzu, während wir dabei sind, da wir wissen, dass diese in column-Richtung verlaufen werden.

.numbers {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0 15px 0 0;
  padding: 0;
}

Ob du es glaubst oder nicht, wir können Flexbox wieder für die Balken verwenden, da auch diese in row-Richtung verlaufen.

.bars {
  display: flex;
  flex: auto; /* fill up the rest of the `.chart` space */
  gap: 60px;
}

Ich habe das so eingerichtet, dass die .bars automatisch den verbleibenden Platz einnehmen, der von der y-Achse .numbers übrig gelassen wird.

Du hast es wahrscheinlich im HTML bemerkt, aber „bar“ sind eigentlich zwei Balken, bei denen sich einer über den anderen legt. Ich habe diese in einem generischen <div> verpackt, das wir als weiteren flexiblen Container verwenden können, der den Definitionsbegriff (<dt>), den wir als Beschriftung verwenden, und die Definitionsdetails (<dd>), die beide Balkenwerte enthalten, enthält.

.bars > div {
  align-items: center;
  display: flex;
  flex-direction: column;
  flex: 1;
  position: relative;
}

Jeder Balken wird die gleiche Breite haben, daher flex: 1. Wir positionieren das Element relativ, während wir dabei sind, denn wir werden jeden Balken absolut positionieren und wir wollen sicherstellen, dass sie in ihren Containern bleiben.

Jeder Balken hat eine prozentuale Höhe, die den Werten entlang der vertikalen y-Achse entspricht. Du erinnerst dich vielleicht auch, dass wir jedem Balken ein data-percentage-Attribut gegeben haben – wir werden ein wenig JavaScript einfügen, das die Höhe jedes Balkens anhand dieser Werte festlegt.

var bars = document.querySelectorAll("dd .bar");
bars.forEach((bar) => {
  var height = bar.getAttribute("data-percentage");
  bar.style.height = height + "%";
});

Das ist unser grundlegendes Diagramm!

Wir wollen es so weit bringen, dass wir die Balken übereinander liegen sehen. Das ist als nächstes!

Überlappende Balken

Der Trick, um einen Balken über einen anderen zu legen, ist lustig, weil wir oft versuchen, Dinge in CSS visuell zu *verhindern*, dass sie sich überlappen. Aber in diesem Fall wollen wir das tatsächlich.

Die Balken überlappen sich bereits; es ist nur schwer zu erkennen. Beachte im HTML, dass der zweite .bar in jedem Set eine zusätzliche .overlap-Klasse hat. Verwenden wir das, um die Balken zu unterscheiden. Du kannst deine eigenen Stile dafür wählen. Ich füge den .overlap-Balken etwas Polsterung hinzu, damit sie breiter sind als die anderen Balken. Dann ändere ich die Stapelreihenfolge mit z-index, damit die .overlap-Balken unter den anderen Balken liegen.

Fügen wir eine Legende hinzu

Legende. Ein so tolles Wort, nicht wahr? Vollgepackt mit allen möglichen Bedeutungen. In diesem Fall ist es mehr als nur eine nette Geste, denn visuell quetschen wir zwei Balken in Räume, die normalerweise für einen Balken reserviert sind. Eine Legende liefert Kontext, der erklärt, was jeder Balken darstellt.

<figure class="legend">
  <div class="type1">Estimate</div>
  <div class="type2">Actual</div>
</figure>

Die Verwendung einer <figure> fühlt sich für mich richtig an. Sie werden oft verwendet, um Bilder zu umschließen, aber die Spezifikation besagt, dass sie „verwendet werden, um Illustrationen, Diagramme, Fotos, Code-Listings usw. zu annotieren“, und wir arbeiten mit einem Diagramm. Wir könnten wahrscheinlich eine ungeordnete Liste verwenden, um die Elemente zu halten, aber ich habe mich für ein unsemantisches <div> entschieden. Wenn jemand eine Meinung dazu hat, wie man das am besten markiert, bin ich ganz Ohr in den Kommentaren!

Auch hier ist das Styling ganz dir überlassen

Barrierefreiheitsaspekte

Wir haben einen Großteil unserer Anstrengungen in die Entscheidungen für das Markup und das Styling unseres überlappenden Balkendiagramms gesteckt. Es ist bisher großartig, aber wir sind definitiv noch nicht fertig, denn es gibt noch mehr, was wir tun können, um dies zu einer *zugänglicheren* Erfahrung zu machen. Nicht jeder ist ein sehender Web-Surfer, daher gibt es zusätzliche Arbeit zu tun, um die Inhalte in diesen Kontexten zu vermitteln.

Genauer gesagt, müssen wir

  1. sicherstellen, dass unsere Farben genügend Kontrast zueinander haben,
  2. Tastaturnutzern erlauben, zu jedem überlappenden Balken zu tabben, und
  3. sicherstellen, dass Screenreader den Inhalt ankündigen.

Farbkontraste

Wir brauchen genügend Kontrast zwischen

  • den überlappenden Balken
  • den Balken und dem Diagrammhintergrund
  • dem Beschriftungstext und Hintergrund

Ich habe im Voraus ein wenig recherchiert über die Farben, die ich in den bisher gezeigten Beispielen verwendet habe, und sichergestellt, dass es genügend Kontrast zwischen Vorder- und Hintergrundfarben gibt, um die WCAG AA-Konformität zu erreichen.

Hier ist, was ich verwende

  • Überlappende Balken: (#25DEAA und #696969: 3,16:1 Verhältnis)
  • Balken und Diagrammhintergrund (#696969 und #111: 3,43:1 Verhältnis)
  • Y-Achsenbeschriftungstext und Hintergrund (#fff und #333: 12,63:1 Verhältnis)

Tabbing zwischen Balken

Damit Tastaturnutzer jeden einzelnen Balken mit der Tab-Taste auswählen können, können wir zum HTML-Attribut tabindex greifen. Wir können das folgende JavaScript innerhalb der for-each-Funktion verwenden, um diese Eigenschaft zu jedem Balken (beiden) hinzuzufügen. Wir setzen den Tab-Index auf 0.

bar.setAttribute("tabindex", 0);

Wir können auch etwas CSS hinzufügen, um die Outline zu verbessern, wenn der Balken ausgewählt ist, während wir dabei sind.

.bar:focus {
  outline: 1.5px solid #f1f1f1;
}

Inhalt auf Screenreadern ankündigen

Ein weiterer wichtiger Aspekt der Barrierefreiheit ist sicherzustellen, dass Screenreader die Balken und ihre Prozentsätze ankündigen können.

Wir arbeiten mit zwei verschiedenen Diagrammen in einem: einem Diagramm, das „geschätzte“ Werte anzeigt, und einem, das „tatsächliche“ Werte anzeigt. Es wäre gut, wenn der Benutzer wüsste, welcher Balken angekündigt wird, also kennzeichnen wir sie mit dem aria-label-Attribut.

<div class="bar" data-percentage="50" aria-label="Estimate">50%</div>

Beachten Sie, dass wir auch den Wert des Balkens direkt im HTML haben. Dieser wird angekündigt, aber wir wollen ihn trotzdem visuell ausblenden. Wir könnten dafür transparenten Text verwenden, aber eine andere Möglichkeit ist der klassische .visually-hidden-Trick, indem wir den Wert in einen span packen.

<div class="bar" data-percentage="50" aria-label="Estimate">
  <span class="visually-hidden">50%</span>
</div>
.visually-hidden {
  clip: rect(0 0 0 0); 
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap; 
  width: 1px;
}

Während wir über die Ankündigung von Inhalten sprechen, können wir wahrscheinlich verhindern, dass die y-Achsenbeschriftungen vorgelesen werden. Es ist nicht so, dass der Benutzer Informationen verpasst, da die tatsächlichen Prozentsätze für jeden Balken bereits verfügbar und angekündigt sind. Wir können das aria-hidden-Attribut dafür verwenden.

<dl class="numbers" aria-hidden="true">
  <dd><span>100%</span></dd>
  <dd><span>80%</span></dd>
  <dd><span>60%</span></dd>
  <dd><span>40%</span></dd>
  <dd><span>20%</span></dd>
  <dd><span>0%</span></dd>
</dl>

Ich denke auch, dass es in Ordnung ist, wenn Screenreader die Legende ignorieren, da sie eine visuelle Hilfe ist.

<figure class="legend" aria-hidden="true">
  <div class="type1">Estimate</div>
  <div class="type2">Actual</div>
</figure>

Die finale Demo

Das ist ein Abschluss!

Da haben wir es, ein Diagramm mit überlappenden Balken! Es ist eine schöne Art, Daten zu vergleichen, und ich hoffe, du findest eine Verwendung dafür in einem Projekt.

Gibt es andere Möglichkeiten, wie wir das hätten angehen können? Natürlich! Alles, was wir hier behandelt haben, ist lediglich die Darstellung meines Denkprozesses. Ich stelle mir vor, dass einige von euch einen anderen Ansatz gewählt hätten – wenn das auf dich zutrifft, teile es bitte mit! Es wäre toll, andere CSS-Layout-Techniken und Perspektiven auf die Barrierefreiheit zu sehen.