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 Seitenleiste für das Web erstellt habe, die responsiv, zustandsorientiert ist, die Tastaturnavigation unterstützt, mit und ohne JavaScript funktioniert und plattformübergreifend ist. 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 verwenden eine Tastatur, andere haben leistungsstarke 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

Web-Taktiken

Bei dieser explorativen Komponente konnte ich einige wichtige Funktionen einer 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 aktiviert 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. Es 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 zum 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 für die Größe eines mobilen Darstellungsbereichs gestapelt. Bis ich neue Stile hinzufüge, wird der Artikel standardmäßig überlagert. Im nächsten Abschnitt möchte ich eine UX schaffen, die

  • Ö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 Auge behalten.

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 Media-Abfragewert entspricht der Betriebssystemeinstellung für Bewegungen eines Nutzers (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);
  }
}
Seitenleiste 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 die :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 ausdrücken.

<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-Funktionspseudoselektor können wir unsere Hover-Stile schnell inklusiv gestalten, indem wir sie auch für den Fokus festlegen.

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

Focus 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 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 Seitenleiste geöffnet wird, legen Sie den Fokus auf 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