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.
Web-Taktiken
Bei dieser explorativen Komponente konnte ich einige wichtige Funktionen der Webplattform kombinieren:
- Preisvergleichsportal
:target
- CSS-Raster
- CSS-Transformationen
- CSS-Medienabfragen für Darstellungsbereich und Nutzereinstellungen
- 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;
}
}
Menühintergrund
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.
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;
}
}
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
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. 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>
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
- @_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