Eine grundlegende Übersicht zum Erstellen einer adaptiven und barrierefreien Toast-Komponente.
In diesem Beitrag möchte ich erklären, wie man eine Toast-Komponente erstellt. Demo ansehen
Wenn du lieber ein Video ansiehst, findest du hier eine YouTube-Version dieses Beitrags:
Übersicht
Toasts sind nicht interaktive, passive und asynchrone Kurznachrichten für Nutzer. Sie werden in der Regel als Feedbackmuster für die Benutzeroberfläche verwendet, um den Nutzer über die Ergebnisse einer Aktion zu informieren.
Interaktionen
Im Gegensatz zu Benachrichtigungen, Warnungen und Aufforderungen sind Toasts nicht interaktiv. Sie können nicht geschlossen oder beibehalten werden. Benachrichtigungen sind für wichtigere Informationen, synchrone Nachrichten, die eine Interaktion erfordern, oder Nachrichten auf Systemebene (im Gegensatz zu Seitenebene) gedacht. Toasts sind passiver als andere Benachrichtigungsstrategien.
Markieren & Zeichnen
Das Element <output>
ist eine gute Wahl für das Toast-Element, da es von Screenreadern angesagt wird. Korrekte HTML-Code bietet eine solide Grundlage, die wir mit JavaScript und CSS erweitern können. Und es wird viel JavaScript geben.
Ein Toast
<output class="gui-toast">Item added to cart</output>
Sie können inklusiver werden, indem Sie role="status"
hinzufügen. Dies dient als Fallback, wenn der Browser <output>
-Elementen nicht die implicit Rolle gemäß der Spezifikation zuweist.
<output role="status" class="gui-toast">Item added to cart</output>
Ein Toastbehälter
Es können mehrere Toast-Nachrichten gleichzeitig angezeigt werden. Ein Container wird verwendet, um mehrere Toasts zu orchestrieren. Dieser Container verwaltet auch die Position der Toast-Nachrichten auf dem Bildschirm.
<section class="gui-toast-group">
<output role="status">Wizard Rose added to cart</output>
<output role="status">Self Watering Pot added to cart</output>
</section>
Layouts
Ich habe mich entschieden, Toasts an der inset-block-end
des Darstellungsbereichs anzupinnen. Wenn weitere Toasts hinzugefügt werden, werden sie von diesem Bildschirmrand aus gestapelt.
GUI-Container
Der Toast-Container übernimmt die gesamte Layoutarbeit für die Darstellung von Toasts. Sie ist fixed
zum Viewport und verwendet die Logikeigenschaft inset
, um anzugeben, an welche Ränder angepinnt werden soll, sowie ein wenig padding
von derselben block-end
-Kante.
.gui-toast-group {
position: fixed;
z-index: 1;
inset-block-end: 0;
inset-inline: 0;
padding-block-end: 5vh;
}
Der Toast-Container positioniert sich nicht nur im Darstellungsbereich, sondern ist auch ein Rastercontainer, der Toasts ausrichten und verteilen kann. Elemente werden mit justify-content
als Gruppe und mit justify-items
einzeln zentriert.
Geben Sie gap
ein, damit Toasts nicht anfassen.
.gui-toast-group {
display: grid;
justify-items: center;
justify-content: center;
gap: 1vh;
}
Toast auf der Benutzeroberfläche
Ein einzelner Toast hat einige padding
, einige weichere Ecken mit border-radius
und eine min()
-Funktion zur Unterstützung der Größenanpassung für Mobilgeräte und Computer. Mit der responsiven Größe im folgenden CSS wird verhindert, dass Toasts breiter als 90 % des Darstellungsbereichs oder 25ch
werden.
.gui-toast {
max-inline-size: min(25ch, 90vw);
padding-block: .5ch;
padding-inline: 1ch;
border-radius: 3px;
font-size: 1rem;
}
Stile
Nachdem Sie das Layout und die Position festgelegt haben, fügen Sie CSS hinzu, das die Anpassung an die Nutzereinstellungen und -interaktionen unterstützt.
Toast container
Toasts sind nicht interaktiv. Wenn Sie darauf tippen oder wischen, passiert nichts. Allerdings werden derzeit Cursorereignisse für sie erfasst. Mit dem folgenden CSS können Sie verhindern, dass die Toasts Klicks stehlen.
.gui-toast-group {
pointer-events: none;
}
Toast-Meldung in der Benutzeroberfläche
Den Toasts ein helles oder dunkles adaptives Design mit benutzerdefinierten Eigenschaften, HSL und einer bevorzugten Medienabfrage zuweisen.
.gui-toast {
--_bg-lightness: 90%;
color: black;
background: hsl(0 0% var(--_bg-lightness) / 90%);
}
@media (prefers-color-scheme: dark) {
.gui-toast {
color: white;
--_bg-lightness: 20%;
}
}
Animation
Ein neues Toast sollte mit einer Animation präsentiert werden, wenn es auf dem Bildschirm erscheint.
Wenn du weniger Bewegung zulassen möchtest, kannst du die translate
-Werte standardmäßig auf 0
festlegen, den Bewegungswert aber in einer Medienabfrage mit Bewegungspräferenz auf eine bestimmte Länge aktualisieren. Alle sehen eine Animation, aber nur einige Nutzer sehen, dass die Meldung eine gewisse Strecke zurücklegt.
Hier sind die Keyframes, die für die Toast-Animation verwendet wurden. CSS steuert den Eingang, das Warten und das Beenden des Toasts in einer einzigen Animation.
@keyframes fade-in {
from { opacity: 0 }
}
@keyframes fade-out {
to { opacity: 0 }
}
@keyframes slide-in {
from { transform: translateY(var(--_travel-distance, 10px)) }
}
Das Toast-Element richtet dann die Variablen ein und orchestriert die Keyframes.
.gui-toast {
--_duration: 3s;
--_travel-distance: 0;
will-change: transform;
animation:
fade-in .3s ease,
slide-in .3s ease,
fade-out .3s ease var(--_duration);
}
@media (prefers-reduced-motion: no-preference) {
.gui-toast {
--_travel-distance: 5vh;
}
}
JavaScript
Nachdem die Stile und die für Screenreader zugängliche HTML-Datei fertig sind, ist JavaScript erforderlich, um Toasts basierend auf Nutzerereignissen zu erstellen, hinzuzufügen und zu löschen. Die Entwicklerfreundlichkeit der Toast-Komponente sollte minimal sein und eine einfache Einstiegsmöglichkeit bieten, z. B. so:
import Toast from './toast.js'
Toast('My first toast')
Toastgruppe und Toasts erstellen
Wenn das Toast-Modul aus JavaScript geladen wird, muss ein Toast-Container erstellt und der Seite hinzugefügt werden. Ich habe das Element vor body
hinzugefügt. Dadurch werden z-index
-Stapelprobleme unwahrscheinlich, da sich der Container für alle Textelemente über dem Container befindet.
const init = () => {
const node = document.createElement('section')
node.classList.add('gui-toast-group')
document.firstElementChild.insertBefore(node, document.body)
return node
}
Die Funktion init()
wird intern im Modul aufgerufen und das Element wird als Toaster
gespeichert:
const Toaster = init()
Das HTML-Element „Toast“ wird mit der Funktion createToast()
erstellt. Die Funktion benötigt Text für das Toast-Element, erstellt ein <output>
-Element, fügt ihm einige Klassen und Attribute hinzu, legt den Text fest und gibt den Knoten zurück.
const createToast = text => {
const node = document.createElement('output')
node.innerText = text
node.classList.add('gui-toast')
node.setAttribute('role', 'status')
return node
}
Ein oder mehrere Toasts verwalten
JavaScript fügt dem Dokument jetzt einen Container für Toasts hinzu und ist bereit, erstellte Toasts hinzuzufügen. Die Funktion addToast()
steuert die Verarbeitung einer oder mehrerer Toast-Meldungen. Zuerst wird die Anzahl der Toasts und die Bewegung geprüft. Anhand dieser Informationen wird dann entweder der Toast angehängt oder eine ausgefallene Animation ausgeführt, damit die anderen Toasts für den neuen Toast „Platz machen“.
const addToast = toast => {
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
Toaster.children.length && motionOK
? flipToast(toast)
: Toaster.appendChild(toast)
}
Wenn Sie den ersten Toast hinzufügen, fügt Toaster.appendChild(toast)
der Seite einen Toast hinzu, der die CSS-Animationen auslöst: Animieren, 3s
, Animieren.
flipToast()
wird aufgerufen, wenn bereits Toasts vorhanden sind. Dabei wird eine von Paul Lewis entwickelte Methode namens FLIP verwendet. Es geht darum, den Unterschied in den Positionen des Containers vor und nach dem Hinzufügen des neuen Toasts zu berechnen.
Stellen Sie sich das so vor, als würden Sie
den aktuellen und zukünftigen Standort des Toasters
animieren und von dort zum Ort animieren.
const flipToast = toast => {
// FIRST
const first = Toaster.offsetHeight
// add new child to change container size
Toaster.appendChild(toast)
// LAST
const last = Toaster.offsetHeight
// INVERT
const invert = last - first
// PLAY
const animation = Toaster.animate([
{ transform: `translateY(${invert}px)` },
{ transform: 'translateY(0)' }
], {
duration: 150,
easing: 'ease-out',
})
}
Das CSS-Raster sorgt für die Aufhebung des Layouts. Wenn ein neuer Toast hinzugefügt wird, platziert das Raster ihn am Anfang und verteilt ihn mit den anderen. Der Container wird mit einer Webanimation von der alten Position aus animiert.
JavaScript-Code zusammenführen
Wenn Toast('my first toast')
aufgerufen wird, wird ein Toast erstellt und der Seite hinzugefügt (möglicherweise sogar der Container für den neuen Toast animiert). Ein Promise wird zurückgegeben und der erstellte Toast wird für den Abschluss der CSS-Animation (die drei Keyframes) für die Promise-Auflösung beobachtet.
const Toast = text => {
let toast = createToast(text)
addToast(toast)
return new Promise(async (resolve, reject) => {
await Promise.allSettled(
toast.getAnimations().map(animation =>
animation.finished
)
)
Toaster.removeChild(toast)
resolve()
})
}
Ich fand den Teil mit der Promise.allSettled()
-Funktion und der toast.getAnimations()
-Zuordnung etwas verwirrend. Da ich für die Toast-Meldung mehrere Keyframe-Animationen verwendet habe, muss jede davon von JavaScript angefordert und jedes finished
-Versprechen auf Abschluss geprüft werden, damit ich sicher sein kann, dass alle abgeschlossen sind.
allSettled
erfüllt diese Anforderung. Sobald alle Versprechen erfüllt sind, wird es als abgeschlossen aufgelöst. Wenn Sie await Promise.allSettled()
verwenden, kann das Element in der nächsten Codezeile sicher entfernt werden, da davon ausgegangen wird, dass der Toast seinen Lebenszyklus abgeschlossen hat. Durch den Aufruf von resolve()
wird das Toast-Versprechen auf hoher Ebene erfüllt, sodass Entwickler nach dem Einblenden des Toasts Aufräumarbeiten durchführen oder andere Aufgaben erledigen können.
export default Toast
Schließlich wird die Funktion Toast
aus dem Modul exportiert, damit sie von anderen Skripts importiert und verwendet werden kann.
Toast-Komponente verwenden
Wenn Sie die Funktion „Toast“ oder die Entwicklerfreundlichkeit von „Toast“ verwenden möchten, importieren Sie die Funktion Toast
und rufen Sie sie mit einem Nachrichtenstring auf.
import Toast from './toast.js'
Toast('Wizard Rose added to cart')
Wenn der Entwickler z. B. eine Bereinigung durchführen möchte, kann er nach der Anzeige des Pop-up die Asynchrone-Funktion und await verwenden.
import Toast from './toast.js'
async function example() {
await Toast('Wizard Rose added to cart')
console.log('toast finished')
}
Fazit
Wie würden Sie es machen?
Lassen Sie uns unsere Ansätze diversifizieren und alle Möglichkeiten kennenlernen, wie Sie im Web entwickeln können. Erstelle eine Demo, tweete mir Links und ich füge sie unten in den Abschnitt „Community-Remixe“ hinzu.
Remixe der Community
- @_developit mit HTML/CSS/JS: Demo und Code
- Joost van der Schee mit HTML/CSS/JS: Demo und Code