Komponente „Sidenav“ erstellen

Eine grundlegende Übersicht über das Erstellen einer responsiven, ausklappbaren Seitennavigation

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

Wenn Sie sich lieber ein Video ansehen, finden Sie hier eine YouTube-Version dieses Beitrags:

Übersicht

Es ist schwierig, ein responsives Navigationssystem zu erstellen. Einige Nutzer verwenden eine Tastatur, andere haben leistungsstarke Desktop-Computer und wieder andere besuchen die Website über 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 auf iOS und Android

Webtaktiken

Bei der Entwicklung dieser Komponente hatte ich die Gelegenheit, einige wichtige Funktionen der Webplattform zu kombinieren:

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

Meine Lösung hat eine Seitenleiste und wird nur bei einem „mobilen“ Darstellungsbereich von 540px oder weniger umgeschaltet. 540px ist der Breakpoint für den Wechsel zwischen dem interaktiven Layout für Mobilgeräte und dem statischen Layout für Computer.

CSS-Pseudoklasse :target

Ein <a>-Link legt den URL-Hash auf #sidenav-open fest und der andere auf leer (''). Schließlich hat ein Element die id, die 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 Sie auf einen dieser Links klicken, ändert sich der Hash-Status der Seiten-URL. Mit einer Pseudoklasse wird dann die Sidenav ein- und ausgeblendet:

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

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

CSS-Raster

In der Vergangenheit habe ich nur Sidenav-Layouts und -Komponenten mit absoluter oder fester Position verwendet. Mit der grid-area-Syntax von Grid können wir jedoch mehrere Elemente derselben Zeile oder Spalte zuweisen.

Stapel

Das primäre Layoutelement #sidenav-container ist ein Raster, das eine Zeile und zwei Spalten erstellt, von denen jeweils eine den Namen stack hat. Wenn der Platz begrenzt ist, weist CSS alle untergeordneten Elemente des <main>-Elements demselben Raster zu. Dadurch werden alle Elemente im selben Bereich platziert und ein Stapel erstellt.

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

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

<aside> ist das animierte Element, das die seitliche Navigation enthält. Es hat zwei untergeordnete Elemente: den Navigationscontainer <nav> mit dem Namen [nav] und einen Hintergrund <a> mit dem Namen [escape], der zum Schließen des Menüs verwendet 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 zum Schließen im Negativraum zu finden.

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

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

Unser Layout ist jetzt bei einer mobilen Darstellungsbereichsgröße gestapelt. Bis ich neue Stile hinzufüge, wird der Artikel standardmäßig überlagert. Hier ist die UX, die ich im nächsten Abschnitt anstrebe:

  • Ö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, möchte ich die Barrierefreiheit im Blick behalten.

Bewegung für Bedienungshilfen

Nicht jeder möchte eine Slide-out-Bewegung. In unserer Lösung wird diese Einstellung angewendet, indem eine CSS-Variable --duration in einer Medienabfrage angepasst wird. Dieser Medienabfragewert stellt die Einstellung des Betriebssystems des Nutzers für Bewegungen dar (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 Seitennavigation geöffnet und geschlossen wird und ein Nutzer reduzierte Bewegung bevorzugt, verschiebe ich das Element sofort in die Ansicht und behalte den Status ohne Bewegung bei.

Transition, transform, translate

Sidenav out (Standard)

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

Hinweis: Ich habe dem typischen Offscreen-Code von -100vw ein weiteres 10vw hinzugefügt, um sicherzustellen, dass der box-shadow der seitlichen Navigation nicht in den Haupt-Viewport hineinragt, wenn er 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 als :target übereinstimmt, setze die translateX()-Position auf die Homebase 0 und beobachte, wie CSS das Element von seiner Out-Position -110vw zu seiner In-Position 0 über var(--duration) verschiebt, 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 des Übergangs

Das Ziel ist jetzt, das Menü vor Screenreadern zu verbergen, wenn es ausgeblendet ist, damit Systeme den Fokus nicht auf ein Menü außerhalb des Bildschirms legen. Dazu richte ich einen Sichtbarkeitsübergang ein, wenn sich der :target ändert.

  • Beim Einblenden sollte die Sichtbarkeit nicht übergehen, sondern sofort sichtbar sein, damit ich sehen kann, wie das Element eingeblendet wird und den Fokus erhält.
  • Beim Ausblenden wird die Sichtbarkeit verzögert, sodass sie am Ende des Übergangs zu hidden wechselt.

UX-Verbesserungen für Bedienungshilfen

Bei dieser Lösung wird die URL geändert, um den Status zu verwalten. Natürlich sollte hier das <a>-Element verwendet werden, das einige nützliche Bedienungshilfen bietet. Wir sollten unsere interaktiven Elemente mit Labels versehen, die ihre Funktion klar beschreiben.

<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 für Voiceover und Tastaturinteraktion.

Jetzt geben unsere primären Interaktionsschaltflächen sowohl für die Maus als auch für die Tastatur klar an, was sie tun.

:is(:hover, :focus)

Mit diesem praktischen funktionalen CSS-Pseudoselektor können wir unsere Hover-Stile schnell und einfach auch für den Fokus verwenden.

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

JavaScript hinzufügen

Zum Schließen escape drücken

Mit der Taste Escape auf Ihrer Tastatur sollte sich das Menü schließen, oder? Verbinden wir das.

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 gestapelt werden, fügen Sie dem Schließen-Button den folgenden JavaScript-Code inline 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, so als wäre das Menü nie geöffnet worden.

Fokus-UX

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 vereinfachen.

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

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

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

Fazit

Jetzt, da du weißt, wie ich es gemacht habe, wie würdest du es machen?! Das sorgt für eine interessante Komponentenarchitektur. Wer erstellt die erste Version mit Slots? 🙂

Lassen Sie uns unsere Ansätze diversifizieren und alle Möglichkeiten kennenlernen, wie wir im Web entwickeln können. Erstellen Sie einen Glitch, twittern Sie mir Ihre Version und ich füge sie dem Abschnitt Community-Remixe unten hinzu.

Community-Remixe