Accessibility, Semantik und Modals im Jahr 2023

Lasst uns zu Beginn des Jahres einfach mal über Accessibility, Semantik und die Implementierung eines einfachen Modals, wie man es einfach im Jahr 2023 tun sollte, reden.

Ich arbeite aktuell parallel, sofern es meine Zeit zulässt, an einem privaten Projekt, welches mir schon lange im Kopf herum schwirrt. Für die Umsetzung des Frontends stehen mir als Entwickler glücklicherweise verschiedene Möglichkeiten zur Verfügung, die allesamt auf ihre eigene Art und Weise ihre Vor- und Nachteile haben. Bei all diesen verschiedenen Möglichkeiten stellte ich mir persönlich immer wieder die gleiche Frage. Warum verdammt ist das Resultat so aufwendig? Wieso muss ich mir bei manchen Umsetzungen erst eine Megabyte große Bibliothek herunter laden? Warum ist das Resultat eine einzige, schlecht nachvollziehbare Suppe von Div-Elementen? Warum so viel Aufwand für eine eigentlich einfache Sache?

Semantik und Accessibility

Webseiten – im weitesten Sinne – werden mit HTML gestaltet. HTML zeichnet mit der Verwendung von entsprechenden Tags Bestandteile einer Website aus. Mit der Verwendung der richtigen HTML Tags bekommen die Bestandteile einer Website die richtige Bedeutung. Das aus semantischen Gründen am richtigste Element für ein Modal wäre demnach das mit HTML5 eingeführte dialog Element.

Wenn wir über Accessibility reden, meinen wir die Zugänglichkeit einer Website. Wir möchten also so vielen Menschen als auch Maschinen wie möglich den Zugang zu den Inhalten einer Website so leicht wie möglich machen. Semantik spielt hier eine Rolle, indem wir Bereiche einer Website als das auszeichnen, was sie sind. Somit können beeinträchtigte Personen, wie z.B. Menschen mit einer Sehbehinderung, die auf einen Screenreader angewiesen sind, die Website ebenso gut betrachten, wie nicht eingeschränkte Menschen. Mit dem Thema Accessibility versucht man jeden Besucher einer Website unabhängig seiner Möglichkeiten und Umstände die gleichen Möglichkeiten zu geben die Inhalte einer Website zu erfahren.

Betrachtet man die aktuellen technischen Implementierungen von Modals, fällt schnell auf, dass diese sich größtenteils nicht auf das dialog Element beziehen. In vielen Varianten beschränkt man sich auf verschachtelte div Container, die im allerbesten Fall die entsprechenden aria Attribute aufweisen. Angefangen bei Boostrap, über die Material UI bis hin zu Fluent UI von Microsoft. Man sucht das dialog Element hier vergebens. Dabei handelt es sich hier um nicht gerade kleine Bibliotheken, die zunächst das Laden von mehreren 100kb Code verlangen, bevor das Modal funktional ist.

Historie

Obwohl das dialog Element mit HTML5 eingeführt wurde, ist die Verfügbarkeit in der breiten Masse der am Markt verfügbaren, relevanten Browser relativ neu. Somit erklärt sich das Fehlen des dialog Elements in großen Bibliotheken, die sich über Jahre hinweg weiter entwickelt haben. Wahrscheinlich würde das Refactoring der Modal Komponenten einen zu hohen Aufwand mit sich bringen, um das dialog Element statt der bisherigen Umsetzung nutzen zu können. Allerdings frage ich mich, wieso das dialog Element z.B. in dem komplett überarbeiteten Fluent UI Repository nicht vorhanden ist? Wieso nutzt man auch hier wieder stark verschachtelte Elemente mit überbordender Funktionalität, die sowohl Semantik als auch Accessibility missen lassen?

Historisch gesehen scheint also die Verfügbarkeit des dialog Elements in Browsern eine wesentliche Rolle zu spielen. Wenn ich persönlich zurück blicke, vermisse ich bei den aktuellen Implementierungen von Modal Dialogen den Fokus auf das Wesentliche. Um die Jahrtausendwende, als Bandbreiten im MBit Bereich die große Ausnahme waren, versuchte man das Markup einer Website so schmal und performant wie möglich zu halten. Semantik spielte eine größere Rolle, als sie es heute zu tun scheint. An Stylesheets in Megabyte Größe war nicht zu denken. Alles musste performant sein. Für den Benutzer als auch für Suchmaschinen mussten Websiten einfach schnell verfügbar sein. Bin ich eine Ausnahme, wenn ich diese Sinn für schlanken, performanten Code heute weitestgehend vermisse?

Modal als Code Beispiel

Mittlerweile ist die Verfügbarkeit des HTML5 dialog Elements in den allermeisten Browsern gegeben. Die Browser kennen dieses Element also und wissen damit umzugehen. Es spricht also absolut nichts mehr dagegen das dialog Element konsequent zu nutzen. Das HTML Code Beispiel hält sich sogar sehr in Grenzen.

<!DOCTYPE html>
<html lang="de">
    <head>
        <meta charset="utf-8">
        <title>Anmelden mit dem Dialog Element</title>
    </head>
    <body>
        <main>
            <header>
                <h1>Semantics for the win!</h1>
                <button id="example-open-button">Anmelden</button>
            </header>
            <dialog id="example-dialog">
                <form method="dialog">
                    <h4>Login</h4>
                    <p>
                        <label>Username</label>
                        <input type="text" name="username" id="username" value="" required>
                    </p>
                    <p>
                        <label>Password</label>
                        <input type="password" name="password" id="password" value="" required>
                    </p>
                    <div>
                        <ul id="example-link-container">
                            <li>
                                <a href="#">Passwort vergessen</a>
                            </li> 
                            <li>
                                <a href="#">Registrieren</a>
                            </li>
                        </ul>
                        <button id="example-cancel-button">Abbrechen</button>
                        <button id="example-submit-button">Anmelden</button>
                    </div>
                </form>
            </dialog>
            <output id="example-output"></output>
        </main>
    </body>
</html>

Gar nicht so viel, oder? Der gezeigte HTML Code bildet ein einfaches Modal mit einem Formular zum Anmelden eines Benutzers ab. Kommen wir nun zur Funktionalität. Zum dialog Element wird eine standardisierte API mitgeliefert, die der Browser direkt versteht.

Jedes dialog Elementbesitzt eine open eigenschaft. Ist das Modal zu sehen, ist die open Eigenschaft gesetzt. Zudem verfügt jedes dialog Element über Funktionen zum öffnen und schließen. Hierbei unterscheidet das Element zwischen einer einfachen show Methode, die lediglich einen Dialog öffnet, bei dem Interaktionen mit den Elementen im Hintergrund möglich sind, und der showModal Methode, die den Dialog als Modal öffnet, bei dem keine Interaktion mit den Elementen im Hintergrund möglich ist.

Funktionalität

const dialog = document.getElementById('example-dialog');
const output = document.getElementById('example-output');

const cancelButton = document.getElementById('example-cancel-button');
const openButton = document.getElementById('example-open-button');
const submitButton = document.getElementById('example-submit-button');

const username = document.getElementById('username');

// Event Listener für den Anmelden Button zum Öffnen des Modals
openButton.addEventListener('click', () => {
    dialog.showModal();
    dialog.classList.add('fadeUp');
});

// Event Listener für den Abbrechen Button zum Schließen des Modals
cancelButton.addEventListener('click', () => {
    dialog.close();
});

// Beim Schließen des Modals wird der eingegeben Name dargestellt
dialog.addEventListener('close', () => {
    output.value = `Herzlich willkommen ${username.value}!`;
    dialog.classList.remove('fadeUp');
});

Auch das JavaScript ist wegen der vorhandenen API sehr schmal und funktional. Das Modal öffnet sich. Das Modal schließt sich. Fertig. Jetzt fehlt nur noch ein wenig Glanz, denn die Standard Implementierung in den Browsern ist nicht unbedingt vorzeigbar. Also fügen wir dem Ganzen noch ein wenig CSS hinzu.

Stilvoll blendet das Modal ein

Das dialog Element macht gebrauch von der ::backdrop CSS Eigenschaft, um den Hintergrund abzudunkeln. Die ::backdrop Eigenschaft gehört zur Fullscreen API. Dessen Support durch die aktuellen Browser ist allerdings eher schwierig, als dass man hier wirklich gute Effekte mittels CSS darstellen kann. Allerdings gibt es ein kleines Workaround mittels box-schadow.

@keyframes modal-in {
    0% { translate: -50%; scale: 0.5; }
    100% { opacity: 1; scale: 1; visibility: visible; }
 }

*, ::after, ::before, html {
    margin: 0;
    padding: 0;
}

body {
    color: rgba(0, 0, 0, 0.85);
    font-family: Helvetica, Arial, sans-serif;
    font-size: 16px;
    font-style: normal;
    font-weight: 400px;
    height: 100vh;
    line-height: 1.5;
    position: relative;
    width: 100%;
}

h1, h4 {
    font-family: Arial, Helvetica, sans-serif;
    font-size: 20px;
}

button, input {
    border: 1px solid rgba(243, 146, 0);
    font-size: 16px;
    padding: 0.365rem 0.5rem;
}

button {
    background-color: rgba(243, 146, 0);
    color: #fff;
    margin-top: 1rem;
}

button:focus, button:hover {
    background-color: rgba(243, 146, 0, 0.75);
    cursor: pointer;
    transition: background-color 0.5s ease-in-out;
}

input {
    color: rgba(0, 0, 0, 0.5);
    width: calc(100% - 1rem);
}

input:focus-visible, button:focus-visible {
    outline: 1px solid rgba(243, 146, 0, 0.966)
}

label {
    display: block;
    font-size: 0.8rem;
    text-align: left;
}

main {
    display: block;
    text-align: center;
}

dialog {
    background: #fff;
    border: 3px solid #000;
    border-radius: 0.25rem;
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0);
    left: 50%;
    opacity: 0;
    padding: 48px 40px;
    position: fixed;
    top: 50%;
    transition: 0.3s ease-out;
    translate: -50% -50%;
    visibility: hidden;
    width: 500px;
}

dialog.fadeUp {
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.8);
    transition:  0.3s ease-out;
}

dialog h4 {
    color: #005270;
}

dialog p {
    color: rgba(0, 0, 0, 0.5);
    margin-top: 1rem;
}

dialog::backdrop {
    background-color: transparent;
}

dialog[open] {
    animation: modal-in 0.3s;
    opacity: 1;
    visibility: visible;
}

dialog ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    list-style: none;
}

dialog ul li a {
    color: rgba(0, 0, 0, 0.5);
    font-size: 0.8rem;
    padding: 0.365rem 0;
    text-decoration: none;
}

Die gezeigten CSS Definitionen lassen das Modal sanft von unten einbleinden und der Hintergrund fadet beim Öffnen des Modals langsam von transparent ins dunkle. Eigentlich so, wie man es von großen Bibliotheken auch gewohnt ist.

Fazit

Je mehr ich mich mit aktuellen Frontend Technologien beschäftige, desto öfter hinterfrage ich, warum das alles so aufwendig sein muss? Das alles ist weit entfernt von Sparsamkeit. Versteht mich nicht falsch. Die Großen machen einiges richtig. Aber wenn ich mir das Resultat mancher UI Bibliotheken ansehe, frage ich mich schon, was aus Tugenden wie Sparsamkeit im Umgang mit Quelltext, Semantik und Accessibility geworden ist? Wieso sind Webseiten plötzlich mehrere Megabyte groß? Was ist passiert? Sicherlich spielen Themen wie Bandbreite heutzutage eine weniger wichtige Rolle, wie vor ein paar Jahren noch. Andererseits verbrauchen große, gut besuchte Webseiten viel mehr Strom allein durch ihre Code-Basis, die erstmal auf ein Endgerät transportiert werden muss. Aus ökologischen Aspekten macht das aus meiner Sicht immer weniger Sinn.

Wie seht ihr große UI Bibliotheken? Kommt Euch das auch alles ein bisschen über das Ziel hinaus geschossen vor? Schreibt es mir in die Kommentare.

Kommentar verfassen

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.