Semantisches HTML · Fehler vermeiden
„Div-Soup“ vermeiden
Div-Soup bezeichnet Markup, das fast ausschließlich aus verschachtelten <div>-Elementen besteht: Die Seite sieht korrekt aus, im Quelltext steht aber nichts mehr darüber, was die einzelnen Bereiche eigentlich sind. Beheben lässt sich das ohne Neubau – man ersetzt vorhandene Container durch die passenden semantischen Elemente, in einer Reihenfolge, die den größten Gewinn zuerst bringt.
Das Wichtigste in Kürze
- Der Begriff (auch „Divitis“) beschreibt Seiten, deren Bedeutung nur in Klassennamen steckt – für Software also nirgends.
- Div-Soup ist selten Absicht. Sie wächst: ein Wrapper fürs Layout, einer für den Abstand, einer „für später“.
- Der Schaden ist konkret: keine Landmarks, keine Überschriftenliste, keine Tastaturbedienung bei nachgebauten Bedienelementen.
- Der schnellste Test: CSS abschalten. Bleibt eine lesbare Struktur, ist die Semantik in Ordnung. Zerfällt alles zu einem Textblock, fehlt sie.
- Aufräumen kostet fast keinen zusätzlichen Code – die semantische Variante ist meist genauso lang wie die Div-Soup, nur mit anderen Elementnamen.
- Reihenfolge mit dem besten Verhältnis von Aufwand zu Wirkung: Struktur-Elemente setzen → klickbare
<div>ersetzen → gemalte Listen zu echten Listen → Überschriften prüfen. -
Nicht jedes
<div>muss weg. Layout-Container sind der bestimmungsgemäße Einsatz. - WCAG-Bezug: 1.3.1 Info und Beziehungen (Stufe A) verlangt, dass visuell erkennbare Strukturen programmatisch bestimmbar sind. Genau das leistet Div-Soup nicht.
Wie Div-Soup entsteht
Selten setzt sich jemand hin und schreibt absichtlich zwanzig verschachtelte <div>. Div-Soup wächst schleichend, und meistens aus nachvollziehbaren Gründen:
- Layout-Schulden. Ein Wrapper wird gebraucht, um zu zentrieren. Ein zweiter für den Abstand, weil
marginkollabiert. Ein dritter für den Rahmen. Vor zehn Jahren war das oft technisch nötig – mitgap, Grid und logischen Eigenschaften ist es das nicht mehr. - Generierter Code. Page-Builder, Design-Tool-Exporte und manche Komponenten-Bibliotheken erzeugen Container-Ebenen, die niemand geschrieben hat.
- Copy-Paste-Erosion. Ein Baustein wird kopiert, angepasst, wieder kopiert. Die Elemente bleiben, ihre Begründung geht verloren.
- Fehlende Kenntnis der Alternativen.
<details>,<search>,<figure>,<dl>und<output>sind vielen kein Begriff – wer sie nicht kennt, baut sie nach.
<!-- Div-Soup: optisch eine Seite, semantisch ein Nichts -->
<div class="page">
<div class="top">
<div class="logo">…</div>
<div class="menu">
<div class="menu-item">…</div>
</div>
</div>
<div class="main">
<div class="post">
<div class="post-title">Beitrag</div>
<div class="post-body">…</div>
</div>
</div>
<div class="bottom">…</div>
</div>
Dieselbe Seite, semantisch
Fast jedes <div> oben hat eine semantische Entsprechung. Tauscht man sie aus, erklärt sich die Struktur von selbst:
<header>
<a class="logo" href="/index.html">…</a>
<nav aria-label="Hauptnavigation">
<ul>
<li><a href="/semantisches-html.html">…</a></li>
</ul>
</nav>
</header>
<main id="inhalt">
<article>
<h1>Beitrag</h1>
<p>…</p>
</article>
</main>
<footer>…</footer>
Der Code ist nicht länger, aber ungleich aussagekräftiger. Jeder Bereich benennt jetzt seine Rolle, ohne dass jemand Klassennamen lesen muss. Und das ist der Punkt, der Diskussionen über Aufwand meist beendet: Aufräumen heißt hier umbenennen, nicht dazuschreiben.
Was Div-Soup konkret kostet
- Barrierefreiheit: Ohne
<header>,<nav>,<main>& Co. fehlen die Landmarks, über die Screenreader-Nutzende navigieren. Ohne echte Überschriften fehlt die zweite Navigationsebene. Wer beides nicht hat, muss jede Seite von oben durchhören. - Tastaturbedienung: Ein
<div>mitonclickerreicht die Tabulatortaste nicht. Das ist kein Komfortproblem, sondern ein Ausschluss – siehe 2.1.1 Tastatur (Stufe A). - Wartbarkeit: Semantischer Code erklärt sich selbst. Bei Div-Soup musst du Klassennamen und CSS lesen, um zu verstehen, was ein Bereich sein soll – und hoffen, dass der Name noch stimmt.
- Robustheit: Wer Buttons, Listen und Aufklappbereiche aus
<div>nachbaut, muss Fokus, Tastatur und Zustände mit Skripten ergänzen. Jede dieser Zeilen ist eine Fehlerquelle, die es beim nativen Element nicht gibt. - Auffindbarkeit: Suchmaschinen und KI-Systeme leiten aus Elementen ab, welcher Teil einer Seite der Inhalt ist. Ohne
<main>bleibt das Raten.
Randnotiz – Maschinen raten dann nur noch. Eine Seite aus lauter
<div>ist nicht nur für Screenreader problematisch. Auch Web-Crawler (Suchmaschinen wie Googlebot) und KI-Crawler (etwa GPTBot, ClaudeBot) leiten die Bedeutung von Bereichen aus den Elementen ab. Fehlt die Semantik, müssen sie aus Klassennamen und Position schätzen – was Hauptinhalt ist, wo die Navigation steckt, was zusammengehört. Semantisches Markup nimmt ihnen dieses Raten ab; wie sich das auf Sichtbarkeit auswirkt, steht unter Semantik & SEO.
So findest du Div-Soup in fünf Minuten
- CSS abschalten. In den Entwicklerwerkzeugen alle Stylesheets deaktivieren (Firefox: Ansicht → Webseiten-Stil → Kein Stil). Ergibt die Seite ohne Styles noch eine erkennbare Struktur aus Überschriften, Listen und Bereichen? Wenn alles zu einem grauen Textblock zerfällt, fehlt Semantik.
- Landmarks zählen. Das Accessibility-Panel der Entwicklerwerkzeuge oder eine Landmark-Erweiterung öffnen. Erwartet werden mindestens ein
banner, einmainund eincontentinfo. Null Landmarks sind das eindeutigste Symptom. - Überschriftenliste prüfen. Genau eine
<h1>, danach<h2>,<h3>ohne Ebenensprung. Fehlt die Liste komplett, sind die Überschriften wahrscheinlich<div>oder<span>mit großer Schrift – siehe Überschriften-Hierarchie. - Einmal durchtabben. Von oben nach unten mit Tab durch die Seite. Erreicht der Fokus jedes Element, das man anklicken kann? Was übersprungen wird, ist ein nachgebautes Bedienelement.
-
<div>zählen und ins Verhältnis setzen. In der Konsole:
// Anteil generischer Container am gesamten Markup
const alle = document.querySelectorAll('body *').length;
const generisch = document.querySelectorAll('body div, body span').length;
console.log(`${generisch} von ${alle} Elementen sind div oder span
(${Math.round((generisch / alle) * 100)} %)`);
Es gibt keinen Grenzwert, den man zitieren könnte, und ich würd auch keinen erfinden. Als Erfahrungswert: Über zwei Drittel generische Container sind ein Signal, sich die Seite genauer anzusehen. Aussagekräftiger als die Quote sind aber die Punkte 1 bis 4 – null Landmarks bei 30 % <div> ist schlimmer als 70 % <div> mit sauberem Gerüst.
Schritt für Schritt aufräumen: Reihenfolge nach Wirkung
Du musst nicht alles auf einmal umbauen, und du solltest es auch nicht. Diese Reihenfolge bringt den größten Gewinn pro Änderung:
- Die vier Struktur-Elemente setzen.
<header>,<nav>,<main>,<footer>– vier Umbenennungen, vier Landmarks. Details unter Struktur-Elemente. -
Klickbare
<div>ersetzen. Jedes<div onclick>wird<button>oder<a>. Das ist die Änderung mit der größten Wirkung auf die Bedienbarkeit; die Abgrenzung steht unter Buttons vs. Links. - Überschriften echt machen.
<div class="titel">wird<h2>, das CSS bleibt. Erst damit entsteht die Überschriftenliste. - Gemalte Aufzählungen zu Listen. Reihen aus
<div>oder Absätze mit Bindestrichen werden<ul>/<ol>– siehe Listen richtig nutzen. -
Nachgebaute Widgets prüfen. Aufklappbereiche können oft
<details>und<summary>werden, Suchformulare bekommen ein<search>, Bild-Container mit Beschriftung eine<figure>. - Container-Türme abbauen. Zuletzt: Nimm ein
<div>gedanklich heraus. Geht nichts kaputt, war es überflüssig. - Validieren. Der W3C-Validator findet Verschachtelungsfehler, die beim Umbau entstehen – ein zweites
<main>, ein<div>in einem<span>, eine Unterliste am falschen Platz.
Was nicht auf die Liste gehört: jedes verbleibende <div> mit Gewalt ersetzen. Ein Grid-Container ist ein <div>, und das ist richtig so. Der HTML-Standard formuliert es als „element of last resort“ – letzte Wahl, nicht verbotenes Element. Die Abgrenzung klärt div vs. span.
Div-Soup im Framework
In React, Vue oder Svelte entsteht Div-Soup auf einem eigenen Weg: durch Komponentengrenzen. Jede Komponente rendert ihr eigenes Wurzelelement, und weil <div> der sichere Standard ist, addieren sich diese Wurzeln zu Ebenen, die niemand geplant hat. Drei Gegenmittel:
- Fragmente nutzen.
<>…</>in React,<template>in Vue – eine Komponente muss kein Wurzelelement erzeugen. Damit fällt pro Komponente eine Ebene weg. - Das Wurzelelement zur Verfügung stellen. Wenn eine Komponente ohnehin einen Container braucht, sollte er konfigurierbar sein:
<Card as="article">statt hart verdrahtetem<div>. - Landmarks zentral setzen.
<header>,<main>und<footer>gehören ins Layout, nicht in einzelne Komponenten. Sonst entstehen doppelte Banner, sobald zwei Bausteine denselben Bereich mitbringen.
Ein Sonderfall, der oft übersehen wird: Utility-First-CSS erzeugt sehr lange Klassenlisten, aber nicht zwangsläufig Div-Soup. Ein <nav class="flex gap-4 …"> ist semantisch tadellos. Das Problem ist nicht die Länge des class-Attributs, sondern der Elementname davor.
Häufiger Fehler in der Praxis
Der Fehler, den ich bei Aufräumarbeiten am häufigsten sehe, ist gut gemeint: ARIA statt Elementwechsel. Aus <div class="nav"> wird <div class="nav" role="navigation">, aus <div class="button"> wird <div role="button" tabindex="0">. Die Rolle stimmt danach, und in einem automatischen Prüfwerkzeug sinkt die Fehlerzahl. Das Verhalten fehlt weiterhin – Enter und Leertaste, disabled, Formularabsenden, Kontextmenü beim Link.
Der Umbau auf <nav> und <button> ist in aller Regel weniger Arbeit als die ARIA-Variante und liefert mehr. Das ist genau die Aussage der ersten Regel von ARIA: Kein ARIA ist besser als schlechtes ARIA.
Zweiter Dauerbrenner: Aufräumen ohne Testen. Ein <div class="post-title"> wird zur <h2>, und plötzlich springt die Schriftgröße, weil das CSS auf .post-title hört, die Standardregeln für h2 aber trotzdem greifen. Das ist harmlos und in einer Zeile behoben – aber es erklärt, warum solche Umbauten manchmal zurückgerollt werden. Ein Blick auf die Seite nach jeder Gruppe von Änderungen spart diesen Umweg.
Häufige Fehler
- Rollen per ARIA nachrüsten, statt das Element zu wechseln.
-
Klickbare
<div>stehen lassen, weil „das Prüfwerkzeug nichts meldet“. -
<section>als Ersatz für<div>– ohne Thema und zugänglichen Namen bringt es nichts. -
Alle
<div>entfernen wollen. Layout-Container sind der richtige Einsatz. - Überschriften nach Schriftgröße wählen statt nach Ebene.
-
Mehrere
<main>oder doppelte<header>durch Komponenten, die Landmarks selbst mitbringen. - Nach dem Umbau nicht validieren – Verschachtelungsfehler bleiben unbemerkt.
Häufige Fragen
Sind <div> also grundsätzlich schlecht?
Nein. <div> ist für reine Layout-Container genau richtig, und der HTML-Standard nennt es ausdrücklich als Element der letzten Wahl – nicht als Fehler. Das Problem ist nicht das Element, sondern sein Einsatz anstelle vorhandener Semantik.
Muss ich bestehende Projekte komplett umschreiben?
Nicht zwingend. Schon Schritt 1 und 2 – Struktur-Elemente setzen und klickbare <div> ersetzen – bringen den größten Teil des Gewinns und lassen sich in wenigen Stunden erledigen. Den Rest kannst du nach und nach aufräumen, am besten immer dann, wenn du eine Datei aus anderem Grund ohnehin anfasst.
Gibt es eine Zahl, ab der es Div-Soup ist?
Keine, die sich belegen ließe. Aussagekräftiger sind die Symptome: null Landmarks, keine Überschriftenliste, nicht erreichbare Bedienelemente. Eine Seite mit 70 % <div> und sauberem Gerüst ist unproblematisch, eine mit 30 % <div> und ohne <main> nicht.
Erkennen automatische Prüfwerkzeuge Div-Soup?
Nur teilweise. Werkzeuge wie axe oder Lighthouse finden fehlende Alternativtexte, Kontrastfehler und einige Rollenprobleme. „Hier hätte ein <article> hingehört“ können sie nicht wissen. Div-Soup entdeckt man mit dem CSS-Test, der Landmark-Liste und der Tastatur – Näheres unter Barrierefreiheit selbst testen.
Wird meine Seite durch semantisches HTML langsamer?
Nein, im Gegenteil. Die semantische Variante hat in der Praxis eher weniger Elemente, weil Container-Ebenen wegfallen. Ein kleinerer DOM bedeutet weniger Layout- und Style-Arbeit im Browser – nachzulesen unter Semantik, Barrierefreiheit, SEO und Performance.
Verwandte Themen
- div vs. span – wann generische Container die richtige Wahl sind
- Struktur-Elemente – Schritt 1 beim Aufräumen
- Was ist semantisches HTML? – das Fundament
- HTML validieren – der letzte Schritt nach jedem Umbau
- 1.3.1 Info und Beziehungen – das Kriterium, das Div-Soup verfehlt
- Die erste Regel von ARIA – warum Rollen nachrüsten die schlechtere Lösung ist
Quellen
- 4.4.16 The div element (WHATWG HTML Standard – „element of last resort, for when no other element is suitable“)
- Page Structure Tutorial (W3C Web Accessibility Initiative – welche Elemente statt generischer Container gehören)
- ARIA in HTML (W3C –
divundspanalsgeneric, Bedingungen für Landmark-Rollen) - Read Me First (W3C ARIA Authoring Practices – die erste Regel: kein ARIA statt falsches ARIA)