Komponente „Sidenav“ erstellen

Eine grundlegende Übersicht zum Erstellen einer responsiven ausziehbaren Seitenleiste

In diesem Beitrag möchte ich Ihnen zeigen, wie ich einen Prototyp für eine Sidenav-Komponente für das Web entwickelt habe, die responsiv, zustandsorientiert ist, die Tastaturnavigation unterstützt, mit und ohne JavaScript funktioniert und browserübergreifend funktioniert. Demo ansehen

Wenn du lieber ein Video ansiehst, findest du hier eine YouTube-Version dieses Beitrags:

Übersicht

Es ist schwierig, ein responsives Navigationssystem zu erstellen. Einige Nutzer arbeiten mit einer Tastatur, andere haben einen leistungsstarken Desktop-Computer und wieder andere nutzen ein kleines Mobilgerät. Alle Besucher sollten das Menü öffnen und schließen können.

Demo für responsives Layout von Desktop zu Mobilgerät
Helles und dunkles Design unter iOS und Android

Web-Taktiken

Bei dieser explorativen Komponente konnte ich einige wichtige Funktionen der Webplattform kombinieren:

  1. Preisvergleichsportal :target
  2. CSS-Raster
  3. CSS-Transformationen
  4. CSS-Medienabfragen für Darstellungsbereich und Nutzereinstellungen
  5. JS für focus UX-Optimierungen

Meine Lösung hat eine Seitenleiste, die nur bei einem mobilen Darstellungsbereich von 540px oder weniger eingeblendet wird. 540px ist der Wendepunkt für den Wechsel zwischen dem interaktiven mobilen Layout und dem statischen Desktop-Layout.

CSS-Pseudoklasse :target

Mit einem <a>-Link wird der URL-Hash auf #sidenav-open und mit dem anderen auf leer ('') festgelegt. Im letzten Schritt wird in einem Element der id festgelegt, der mit dem Hash übereinstimmt:

<a href="#sidenav-open" id="sidenav-button" title="Open Menu" aria-label="Open Menu">

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

<aside id="sidenav-open">
  …
</aside>

Wenn ich auf einen dieser Links klicke, ändert sich der Hash-Status der Seiten-URL. Mit einer Pseudoklasse blende ich dann die Seitenleiste ein und aus:

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
  }

  #sidenav-open:target {
    visibility: visible;
  }
}

CSS-Raster

Früher habe ich nur absolute oder fixierte Seitenleistenlayouts und ‑komponenten verwendet. Mithilfe der grid-area-Syntax von Grid können wir jedoch mehreren Elementen dieselbe Zeile oder Spalte zuweisen.

Stacks

Das primäre Layoutelement #sidenav-container ist ein Raster mit einer Zeile und zwei Spalten, von denen jeweils eine stack heißt. Wenn der Platz begrenzt ist, weist CSS allen Kindern des <main>-Elements denselben Rasternamen zu und platziert alle Elemente im selben Bereich, wodurch ein Stapel entsteht.

#sidenav-container {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;
  min-height: 100vh;
}

@media (max-width: 540px) {
  #sidenav-container > * {
    grid-area: stack;
  }
}

Das <aside> ist das animierte Element, das die seitliche Navigation enthält. Sie hat zwei untergeordnete Elemente: den Navigationscontainer <nav> mit dem Namen [nav] und einen Hintergrund-<a> mit dem Namen [escape], mit dem das Menü geschlossen wird.

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;
}

Passen Sie 2fr und 1fr an, um das gewünschte Verhältnis für das Menü-Overlay und die Schaltfläche „Schließen“ im Negativraum zu finden.

Eine Demo, die zeigt, was passiert, wenn Sie das Seitenverhältnis ändern.

CSS-3D-Transformationen und ‑Übergänge

Unser Layout ist jetzt bei einer mobilen Darstellungsgröße gestapelt. Bis ich neue Stile hinzufüge, wird der Artikel standardmäßig überlagert. Hier sind einige UX-Designs, auf die ich im nächsten Abschnitt stehe:

  • Öffnen und Schließen animieren
  • Nur mit Bewegung animieren, wenn der Nutzer damit einverstanden ist
  • visibility animieren, damit der Tastaturfokus nicht auf das Element außerhalb des Bildschirms wechselt

Wenn ich mit der Implementierung von Bewegungsanimationen beginne, steht die Barrierefreiheit an erster Stelle.

Barrierefreie Bewegung

Nicht jeder möchte, dass sich das Display heraus- und wieder hereinschieben lässt. In unserer Lösung wird diese Einstellung durch Anpassen einer --duration-CSS-Variablen in einer Medienabfrage angewendet. Dieser Medienabfragewert repräsentiert die Betriebssystempräferenz eines Nutzers für Bewegung (falls verfügbar).

#sidenav-open {
  --duration: .6s;
}

@media (prefers-reduced-motion: reduce) {
  #sidenav-open {
    --duration: 1ms;
  }
}
Eine Demo der Interaktion mit und ohne Dauer.

Wenn die Seitenleiste jetzt geöffnet und geschlossen wird und ein Nutzer weniger Bewegung bevorzugt, bringe ich das Element sofort in den Blick, wobei der Status ohne Bewegung beibehalten wird.

Übergänge, Transformationen, Übersetzungen

Seitenleiste ausgeblendet (Standard)

Um den Standardstatus der Seitenleiste auf Mobilgeräten auf „Ausblenden“ festzulegen, positioniere ich das Element mit transform: translateX(-110vw).

Hinweis: Ich habe dem üblichen Code für ausgeblendete Elemente -100vw eine weitere 10vw hinzugefügt, damit die box-shadow der Seitenleiste nicht in den Haupt-Viewport hineinragt, wenn sie ausgeblendet ist.

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);
  }
}
Sidenav in

Wenn das #sidenav-Element mit :target übereinstimmt, legen Sie die Position translateX() auf die Startposition 0 fest. Sehen Sie dann zu, wie das Element von seiner Aus-Position -110vw über var(--duration) an seine „An“-Position 0 geschoben wird, wenn der URL-Hash geändert wird.

@media (max-width: 540px) {
  #sidenav-open:target {
    visibility: visible;
    transform: translateX(0);
    transition:
      transform var(--duration) var(--easeOutExpo);
  }
}

Sichtbarkeit der Umstellung

Ziel ist es nun, das Menü für Screenreader auszublenden, wenn es nicht angezeigt wird, damit die Systeme den Fokus nicht auf ein Menü außerhalb des Bildschirms legen. Dazu lege ich einen Sichtbarkeitsübergang fest, wenn sich :target ändert.

  • Wenn Sie hineingehen, ändern Sie die Sichtbarkeit nicht. Das Element sollte sofort sichtbar sein, damit ich sehen kann, wie es hereingleitet, und den Fokus darauf legen kann.
  • Ändern Sie die Sichtbarkeit beim Ausblenden, verzögern Sie den Vorgang aber so, dass am Ende der Ausblendung hidden angezeigt wird.

Verbesserungen der UX für Barrierefreiheit

Bei dieser Lösung muss die URL geändert werden, damit der Status verwaltet werden kann. Natürlich sollte hier das <a>-Element verwendet werden. Es bietet einige praktische Funktionen für Barrierefreiheit. Wir versehen unsere interaktiven Elemente mit Labels, die ihren Zweck klar formulieren.

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
  <svg>...</svg>
</a>
Eine Demo der UX-Interaktion mit Sprachausgabe und Tastatur.

Jetzt ist die Absicht unserer primären Interaktionsschaltflächen sowohl für die Maus als auch für die Tastatur klar erkennbar.

:is(:hover, :focus)

Mit diesem praktischen CSS-Funktions-Pseudoselektor können wir unsere Hover-Stile schnell einbeziehen, indem wir sie auch mit Fokus teilen.

.hamburger:is(:hover, :focus) svg > line {
  stroke: hsl(var(--brandHSL));
}

JavaScript einbinden

Zum Schließen escape drücken

Das Menü sollte mit der Taste Escape auf der Tastatur geschlossen werden, richtig? Verbinden wir das jetzt.

const sidenav = document.querySelector('#sidenav-open');

sidenav.addEventListener('keyup', event => {
  if (event.code === 'Escape') document.location.hash = '';
});
Browserverlauf

Damit durch das Öffnen und Schließen nicht mehrere Einträge im Browserverlauf protokolliert werden, fügen Sie der Schaltfläche „Schließen“ das folgende JavaScript-Snippet hinzu:

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>

Dadurch wird der URL-Verlaufseintrag beim Schließen entfernt, sodass es so aussieht, als wäre das Menü nie geöffnet worden.

Fokus auf UX legen

Mit dem nächsten Snippet können wir den Fokus auf die Schaltflächen zum Öffnen und Schließen legen, nachdem sie geöffnet oder geschlossen wurden. Ich möchte das Umschalten so einfach wie möglich machen.

sidenav.addEventListener('transitionend', e => {
  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
      ? document.querySelector('#sidenav-close').focus()
      : document.querySelector('#sidenav-button').focus();
})

Wenn die seitliche Navigationsleiste geöffnet wird, fokussieren Sie die Schaltfläche zum Schließen. Wenn die Seitenleiste geschlossen wird, legen Sie den Fokus auf die Schaltfläche „Öffnen“. Dazu rufe ich in JavaScript focus() für das Element auf.

Fazit

Jetzt, da Sie wissen, wie ich das gemacht habe, wie würden Sie es machen? Das macht die Komponentenarchitektur interessant. Wer erstellt die erste Version mit Slots? 🙂

Lassen Sie uns unsere Ansätze diversifizieren und alle Möglichkeiten kennenlernen, wie man im Web entwickeln kann. Erstelle einen Glitch, tweete mir deine Version und ich füge sie unten in den Abschnitt Community-Remixe ein.

Remixe der Community