Grundlegende Übersicht zum Erstellen einer responsiven Slide-out-Seitennavigation
In diesem Beitrag möchte ich Ihnen zeigen, wie ich eine responsive, zustandsbehaftete Sidenav-Komponente für das Web prototypisiert habe, die die Tastaturnavigation unterstützt, mit und ohne JavaScript funktioniert und browserübergreifend eingesetzt werden kann. Demo ansehen
Wenn du lieber ein Video ansehen möchtest, findest du hier eine YouTube-Version dieses Beitrags:
Übersicht
Es ist schwierig, ein responsives Navigationssystem zu erstellen. Einige Nutzer verwenden eine Tastatur, andere einen leistungsstarken Computer und wieder andere ein kleines Mobilgerät. Jeder Besucher sollte das Menü öffnen und schließen können.
Web-Taktiken
Bei der Entwicklung dieser Komponente konnte ich einige wichtige Webplattformfunktionen kombinieren:
- Preisvergleichsportal
:target
- CSS-Grid
- CSS-Transformationen
- CSS-Medienabfragen für Darstellungsbereich und Nutzereinstellungen
- JS für
focus
UX-Verbesserungen
Meine Lösung hat eine Seitenleiste und wird nur bei einem Darstellungsbereich von 540px
oder weniger ein- und ausgeblendet.
540px
ist der Breakpoint, an dem zwischen dem interaktiven Layout für Mobilgeräte und dem statischen Layout für Computer gewechselt wird.
CSS-Pseudoklasse :target
Mit einem <a>
-Link wird der URL-Hash auf #sidenav-open
und mit dem anderen auf leer (''
) festgelegt.
Außerdem hat ein Element die id
, die dem Hash entspricht:
<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 blende ich dann die Seitenleiste ein und aus:
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
CSS-Grid
Bisher habe ich nur Sidenav-Layouts und ‑Komponenten mit absoluter oder fester Position verwendet. Mit dem grid-area
-Syntax von Grid können wir jedoch mehreren Elementen dieselbe Zeile oder Spalte zuweisen.
Stapel
Das primäre Layoutelement #sidenav-container
ist ein Raster mit einer Zeile und zwei Spalten, von denen jeweils eine den Namen stack
hat. Wenn der Platz begrenzt ist, weist CSS allen untergeordneten Elementen des <main>
-Elements denselben Grid-Namen zu. Dadurch werden alle Elemente im selben Bereich platziert und es entsteht ein Stapel.
#sidenav-container {
display: grid;
grid: [stack] 1fr / min-content [stack] 1fr;
min-height: 100vh;
}
@media (max-width: 540px) {
#sidenav-container > * {
grid-area: stack;
}
}
Menühintergrund
<aside>
ist das animierende Element, das die Seitennavigation 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 zugehörige Schaltfläche zum Schließen zu finden.
CSS-3D-Transformationen und ‑Übergänge
Unser Layout ist jetzt bei einer Darstellungsbereichsgröße für Mobilgeräte gestapelt. Bis ich neue Stile hinzufüge, wird es standardmäßig über unserem Artikel eingeblendet. Hier sind einige UX-Aspekte, die ich im nächsten Abschnitt anstrebe:
- Öffnen und Schließen animieren
- Nur animieren, wenn der Nutzer damit einverstanden ist
- Animieren Sie
visibility
, damit der Tastaturfokus nicht auf das Element außerhalb des Bildschirms gelangt.
Wenn ich mit der Implementierung von Bewegungsanimationen beginne, möchte ich die Barrierefreiheit von Anfang an berücksichtigen.
Barrierefreie Bewegung
Nicht jeder möchte, dass sich die Inhalte auf diese Weise einblenden. In unserer Lösung wird diese Einstellung angewendet, indem eine --duration
-CSS-Variable in einer Media-Query angepasst wird. Dieser Media-Query-Wert gibt die Einstellung des Betriebssystems eines Nutzers für Bewegung an (falls verfügbar).
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
Wenn die Seitenleiste jetzt ein- und ausgeblendet wird, wird das Element bei Nutzern, die eine reduzierte Bewegung bevorzugen, sofort in die Ansicht verschoben, wobei der Status ohne Bewegung beibehalten wird.
Übergang, Transformation, Übersetzung
Sidenav out (Standard)
Um den Standardstatus unserer Seitenleiste auf Mobilgeräten auf „Offscreen“ zu setzen, positioniere ich das Element mit transform: translateX(-110vw)
.
Ich habe dem typischen Offscreen-Code von -100vw
ein weiteres 10vw
hinzugefügt, damit das box-shadow
der Seitenleiste nicht in den Haupt-Viewport ragt, 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 einblenden
Wenn das #sidenav
-Element mit :target
übereinstimmt, legen Sie die translateX()
-Position auf die Homebase 0
fest. Wenn der URL-Hash geändert wird, schiebt CSS das Element über var(--duration)
von seiner „Out“-Position -110vw
zu seiner „In“-Position 0
.
@media (max-width: 540px) {
#sidenav-open:target {
visibility: visible;
transform: translateX(0);
transition:
transform var(--duration) var(--easeOutExpo);
}
}
Sichtbarkeit des Übergangs
Das Ziel ist nun, das Menü für Screenreader auszublenden, wenn es nicht sichtbar ist, damit Systeme den Fokus nicht auf ein Menü legen, das sich außerhalb des Bildschirms befindet. Dazu lege ich einen Übergang für die Sichtbarkeit fest, wenn sich :target
ändert.
- Beim Einblenden soll die Sichtbarkeit nicht überblendet werden. Das Element soll sofort sichtbar sein, damit ich sehen kann, wie es eingeblendet wird und den Fokus erhält.
- Beim Verlassen des Bildschirms sollte die Einblendung der Übergangsanimation verzögert werden, sodass sie erst am Ende der Übergangsanimation zu
hidden
wechselt.
UX-Verbesserungen für Bedienungshilfen
Links
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, das einige nützliche Barrierefreiheitsfunktionen bietet. Wir versehen unsere interaktiven Elemente mit Labels, die die Intention 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>
Die primären Interaktionsschaltflächen geben jetzt sowohl für Maus als auch für Tastatur deutlich an, was sie bewirken.
: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 einfügen
Zum Schließen escape
drücken
Sollte das Menü nicht durch Drücken der Taste Escape
auf Ihrer Tastatur geschlossen werden? 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 gespeichert werden, fügen Sie dem Schließen-Button den folgenden Inline-JavaScript-Code 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
Im nächsten Snippet wird der Fokus auf die Schaltflächen zum Öffnen und Schließen gelegt, nachdem sie geöffnet oder geschlossen wurden. Ich möchte das Umschalten einfach gestalten.
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, fokussieren Sie die Schaltfläche zum Schließen. Wenn die Seitenleiste geschlossen wird, muss der Fokus auf die Schaltfläche zum Öffnen gelegt werden. Dazu rufe ich focus()
für das Element in JavaScript auf.
Fazit
Jetzt wissen Sie, wie ich es gemacht habe. Wie würden Sie es machen? Das ist eine interessante Komponentenarchitektur. Wer wird die erste Version mit Slots erstellen? 🙂
Wir möchten unsere Ansätze diversifizieren und alle Möglichkeiten kennenlernen, die das Web bietet. Erstelle einen Glitch, schreibe mir auf Twitter deine Version und ich füge sie unten im Bereich Community-Remixe hinzu.
Community-Remixe
- @_developit mit benutzerdefinierten Elementen: Demo und Code
- @mayeedwin1 mit HTML/CSS/JS: Demo und Code
- @a_nurella mit einem Glitch-Remix: Demo und Code
- @EvroMalarkey mit HTML/CSS/JS: Demo und Code