Semantisches HTML · Fehler vermeiden

div vs. span – wann nutze ich was?

<div> und <span> sind die beiden bedeutungsfreien Elemente in HTML: Der Unterschied zwischen ihnen ist rein technisch – <div> ist ein Blockelement, <span> ein Inline-Element. Beide tragen im Accessibility-Tree die Rolle generic und sind damit die richtige Wahl, wenn es ausschließlich um Layout oder Styling geht, und die falsche, sobald ein Bereich eine benennbare Bedeutung hat.

Das Wichtigste in Kürze

  • <div> = Block: beginnt auf neuer Zeile, nimmt die volle verfügbare Breite ein. Zum Gruppieren mehrerer Elemente.
  • <span> = Inline: bleibt im Textfluss, so breit wie sein Inhalt. Für Ausschnitte innerhalb einer Zeile.
  • Beide haben laut ARIA in HTML die Rolle generic – für Screenreader und Crawler sind sie durchsichtig.
  • Ein <span> darf kein <div> enthalten. Inline-Elemente nehmen keine Blockelemente auf; der Browser repariert das stillschweigend, das HTML ist aber ungültig.
  • Der Fehler ist nie „zu viele <div>“, sondern ein <div> an der Stelle eines vorhandenen semantischen Elements – klickbare <div> statt <button> sind der Klassiker.
  • Ein nachgebauter <div>-Button verstößt gegen mindestens zwei WCAG-Kriterien auf einmal: 2.1.1 Tastatur (nicht fokussierbar, nicht bedienbar) und 4.1.2 Name, Rolle, Wert (keine Rolle, kein Zustand).
  • display: block per CSS macht aus einem <span> kein Blockelement im HTML-Sinn – die Verschachtelungsregeln gelten weiter.
  • WCAG-Bezug: 1.3.1 Info und Beziehungen (Stufe A) verlangt, dass visuell vermittelte Strukturen programmatisch bestimmbar sind. Generische Container vermitteln nichts.

Der eine technische Unterschied

  • <div> ist ein Blockelement: Es beginnt auf einer neuen Zeile und nimmt standardmäßig die volle Breite ein. Gedacht zum Gruppieren größerer Bereiche oder mehrerer Elemente.
  • <span> ist ein Inline-Element: Es bleibt im Textfluss und nimmt nur so viel Platz, wie sein Inhalt braucht. Gedacht für einen Ausschnitt innerhalb einer Zeile.
<!-- div: gruppiert einen Block, z. B. für ein Layout-Raster -->
<div class="karten-raster">
  <article>…</article>
  <article>…</article>
</div>

<!-- span: fasst einen Ausschnitt im Text an, z. B. zum Einfärben -->
<p>Status: <span class="badge">aktiv</span></p>

Das war es auch schon an technischem Unterschied. Alles Weitere ist eine Frage der Entscheidung: nehmen oder nicht nehmen. Ich würd mir dafür einen einzigen Satz merken, und der kommt gleich.

Die Reihenfolge im Kopf

Zweistufiges Entscheidungsdiagramm. Erste Frage: Hat der Bereich eine benennbare Bedeutung? Bei Ja führt der Pfeil zu einer Sammlung semantischer Elemente von nav und article über button und ul bis abbr, mit dem Hinweis, dass Rolle, Tastaturbedienung und Ansage damit erledigt sind. Zweite Frage: Geht es nur um Layout oder Styling? Bei Ja führt der Pfeil zur Entscheidung Block oder Inline, also div oder span, beide mit der Rolle generic. Darunter zwei gerenderte Beispiele: ein div beginnt auf neuer Zeile und nimmt die ganze Breite ein, ein span bleibt mitten in der Textzeile stehen.
Erst die Bedeutung prüfen, dann den Container wählen: Block oder Inline ist die letzte Frage, nicht die erste.

Meine Reihenfolge im Kopf ist immer dieselbe: Gibt es ein semantisches Element für das, was hier steht? Wenn ja, nehme ich das. Erst wenn es wirklich nur ums Gruppieren oder Stylen geht, kommt <div> oder <span> – und dann entscheidet Block oder Inline.

Der Test dazu in einem Satz: Könntest du dem Bereich einen Namen geben, der etwas über den Inhalt sagt? „Hauptnavigation“, „Produktbeschreibung“, „Senden-Button“ – für all das gibt es Elemente. Fällt dir nur ein Name für das Aussehen ein – „zwei-spalten“, „abstand-oben“, „wrapper“ –, ist <div> die richtige Wahl.

Beide sind semantisch neutral

Das ist der entscheidende Punkt: Weder <div> noch <span> sagen etwas über die Bedeutung ihres Inhalts aus. In der Barrierefreiheits-Schnittstelle des Browsers tragen sie die Rolle generic – ein Screenreader kündigt sie nicht an, überspringt sie in der Elementliste und bietet keine Sprungmöglichkeit dorthin.

Das ist kein Mangel, sondern die Aufgabe dieser Elemente. Ein Layout-Raster soll nicht angekündigt werden. Genau darum sind sie die letzte Wahl: nicht weil sie schlecht sind, sondern weil sie nichts beitragen, wo etwas beizutragen wäre.

Wann <div> und <span> richtig sind

Diese Fälle sind völlig legitim und brauchen kein schlechtes Gewissen:

  • Ein Layout-Wrapper, um mehrere Elemente per CSS Grid oder Flexbox anzuordnen.
  • Ein Hook fürs Styling, wenn es keine inhaltliche Bedeutung gibt – etwa ein Container, der nur einen Rahmen und Innenabstand trägt.
  • Ein Inline-Ausschnitt (<span>), um ein Wort einzufärben, per Skript anzusteuern oder als Ziel für eine Animation zu setzen.
  • Eine Gruppe innerhalb einer Beschreibungsliste – der HTML-Standard erlaubt ausdrücklich ein <div> um jedes <dt>/<dd>-Paar, damit sich Gruppen einzeln gestalten lassen (siehe Listen richtig nutzen).
<!-- Korrekt: reiner Layout-Container -->
<div class="zwei-spalten">
  <main>…</main>
  <aside>…</aside>
</div>

<!-- Korrekt: span als Styling-Hook für eine Einheit -->
<p>Preis: <span class="waehrung">19,90 €</span></p>

Wann ein semantisches Element gehört

Hier wird <div> oder <span> zum Fehler, weil es eine passende Alternative gibt. Vier Fälle, die in jedem zweiten Projekt auftauchen:

<!-- Falsch: klickbares div -->
<div class="button" onclick="senden()">Senden</div>

<!-- Richtig: echter Button (fokussierbar, tastaturbedienbar, mit Rolle) -->
<button type="button">Senden</button>
<!-- Falsch: div als Navigation -->
<div class="nav">…</div>

<!-- Richtig: nav mit Liste -->
<nav aria-label="Hauptnavigation">
  <ul>
    <li><a href="/index.html">Start</a></li>
  </ul>
</nav>
<!-- Falsch: gemalte Aufzählung -->
<div class="liste">
  <div class="eintrag">– Wahrnehmbar</div>
  <div class="eintrag">– Bedienbar</div>
</div>

<!-- Richtig: echte Liste, Screenreader sagt „Liste, 2 Einträge“ -->
<ul>
  <li>Wahrnehmbar</li>
  <li>Bedienbar</li>
</ul>
<!-- Falsch: span als Überschrift, nur groß gestylt -->
<span class="titel-gross">Unsere Leistungen</span>

<!-- Richtig: echte Überschrift, erscheint in der Überschriftenliste -->
<h2>Unsere Leistungen</h2>

Was ein <div>-Button wirklich kostet

Der nachgebaute Button ist das Lehrbuchbeispiel, und es lohnt sich, einmal durchzuzählen, was da eigentlich fehlt. Ein <div class="button"> ist:

  1. nicht fokussierbar – die Tastatur erreicht es nicht (2.1.1 Tastatur, Stufe A),
  2. nicht mit Enter oder Leertaste bedienbarclick feuert bei einem <div> nicht auf Tastendruck,
  3. ohne Rolle – der Screenreader sagt nichts, kein „Schaltfläche“ (4.1.2 Name, Rolle, Wert, Stufe A),
  4. nicht in der Formularlogik – es löst kein submit aus, kennt kein disabled, kein form-Attribut.

Der Nachbau in ARIA sieht dann so aus:

<!-- Der „Preis“ des Nachbaus: vier Zusatzangaben plus Tastaturlogik im Skript -->
<div class="button" role="button" tabindex="0" aria-disabled="false">Senden</div>

Und selbst damit fehlt noch der Tastatur-Handler für Enter und Leertaste, die Unterscheidung zwischen beiden (ein Button reagiert auf Leertaste beim Loslassen, ein Link gar nicht), das Verhalten bei aria-disabled und die Zustandspflege. Der <button> bringt das alles mit – und ist kürzer. Die Abgrenzung zwischen Button und Link steht unter Buttons vs. Links, das Prinzip dahinter unter die erste Regel von ARIA.

Verschachtelung: die Regel, die CSS nicht ändert

Ein <span> darf Inline-Inhalt enthalten – Text, weitere <span>, <a>, <strong>, <img>. Ein <div> darf es nicht enthalten:

<!-- Ungültig: Blockelement in einem Inline-Element -->
<span class="karte"><div>Inhalt</div></span>

<!-- Gültig -->
<div class="karte"><div>Inhalt</div></div>

Der Browser richtet das beim Parsen stillschweigend gerade, der W3C-Validator meldet es. Und ein häufiges Missverständnis dazu: display: block im CSS ändert nur die Darstellung, nicht die Kategorie im HTML. Ein <span style="display: block"> bleibt ein Inline-Element mit Inline-Inhaltsmodell – die Verschachtelungsregel gilt unverändert.

Randnotiz – warum das auch für Maschinen zählt. Ob ein Bereich eine Navigation, ein Beitrag oder nur ein Layout-Kasten ist, erschließt sich Web-Crawlern (Suchmaschinen) und KI-Crawlern (etwa GPTBot, ClaudeBot) allein aus dem Element. Eine Seite, die alles in <div> verpackt, zwingt Software zum Raten: Was ist Hauptinhalt, was Beiwerk, was gehört zusammen? Semantische Elemente machen die Rolle eindeutig – ein Vorteil, der weit über Screenreader hinausgeht. Mehr dazu unter Semantik & SEO.

Wie viele <div> sind zu viele?

Es gibt keine Zahl. Die Frage ist auch falsch gestellt: Ein Layout braucht Container, und ein Raster aus vier <div> ist unauffällig. Problematisch wird es an zwei Stellen.

Erstens, wenn <div> anstelle vorhandener Semantik steht – dann ist schon eines zu viel. Zweitens, wenn Container sich türmen, ohne etwas zu tun:

<!-- Drei Ebenen, eine Aufgabe -->
<div class="wrapper">
  <div class="inner">
    <div class="content">
      <p>Text</p>
    </div>
  </div>
</div>

Solche Türme entstehen selten absichtlich; sie wachsen. Ein Wrapper fürs Layout, einer für den Abstand, einer „für später“. Wie man das erkennt und abbaut, steht unter „Div-Soup“ vermeiden.

Ein praktischer Prüfschritt: Nimm ein <div> gedanklich heraus und schau, ob etwas kaputt geht. Wenn nicht, war es überflüssig. Mit modernen CSS-Möglichkeiten – gap, Grid, logische Eigenschaften – braucht man für Abstände und Ausrichtung deutlich weniger Verschachtelung als vor zehn Jahren.

Der Sonderfall: <span> mit lang

Ein Einsatz, bei dem <span> mehr tut als nur zu stylen: Sprachwechsel im Fließtext. Das lang-Attribut braucht ein Element, an dem es hängen kann, und für ein einzelnes Wort ist <span> genau das richtige:

<p>Der Fachbegriff dafür ist <span lang="en">progressive enhancement</span>.</p>

Screenreader schalten damit auf die englische Aussprache um. Das ist WCAG 3.1.2 Sprache von Teilen (Stufe AA) – und ein Fall, in dem ein generisches Element eine sehr konkrete Wirkung hat. Wenn ein passenderes Element zur Hand ist, nimm allerdings das: Eine Abkürzung gehört in <abbr>, ein Fachbegriff bei erster Erwähnung in <dfn>, ein Zitat in <q> (siehe Textauszeichnung).

Häufiger Fehler in der Praxis

Der Fehler, der mich am meisten Zeit kostet, ist nicht der klickbare <div> – der fällt in jedem Prüfwerkzeug auf. Es ist die <span>-Überschrift in Komponenten-Bibliotheken. Eine Card-Komponente rendert ihren Titel als <span class="card-title">, weil die Schriftgröße im Design festgelegt ist und eine echte Überschrift „zu groß“ wäre. Optisch tadellos. In der Überschriftenliste des Screenreaders taucht die Karte nicht auf, und auf einer Übersichtsseite mit dreißig Karten ist das der Unterschied zwischen „dreißig Sprungziele“ und „einmal alles durchhören“.

Die Lösung ist banal: <h3> nehmen und im CSS auf die gewünschte Größe setzen. Die Überschriftenebene und die Schriftgröße sind zwei verschiedene Dinge – WCAG 2.4.6 Überschriften und Beschriftungen interessiert sich für die Ebene, nicht für die Pixel.

Der zweite Dauerbrenner: <div> mit onclick in einer Tabelle, damit die ganze Zeile klickbar ist. Auch hier gibt es ein Muster, das ohne Semantikverlust auskommt – siehe Klickbare Cards.

Häufige Fehler

  • Klickbare <div> statt <button> oder <a>.
  • <div> als Navigation, Liste oder Artikel, obwohl es passende Elemente gibt.
  • <span> als Überschrift, nur groß gestylt – fehlt in der Überschriftenliste.
  • <div> in einem <span> – ungültig, auch wenn der Browser es repariert.
  • display: block an einem <span> und dann Blockinhalt hineinschreiben.
  • role="button" und tabindex="0" an einem <div>, statt <button> zu nehmen.
  • Verschachtelte <div>-Türme, wo gap oder Grid ohne Zusatzelement auskommen.
  • <span> ohne lang bei fremdsprachigen Begriffen, wo es hilfreich wäre.

Häufige Fragen

Ist es schlimm, viele <div> zu verwenden?

Nicht per se. Layouts brauchen Container, und dafür ist <div> gedacht. Problematisch wird es erst, wenn <div> anstelle eines vorhandenen semantischen Elements steht. Die Menge allein sagt wenig – ein einziger klickbarer <div> schadet mehr als zwanzig Layout-Wrapper.

<div> oder <span> – wie entscheide ich schnell?

Block oder Inline. Geht es um einen Bereich, der auf eigener Zeile steht und andere Elemente umschließt, nimm <div>. Geht es um einen Ausschnitt innerhalb einer Textzeile, nimm <span>. Falls die Antwort „hängt vom CSS ab“ lautet: <div>, außer der Inhalt steht mitten im Satz.

Darf ein <div> in einem <span> stehen?

Nein. <span> hat ein Inline-Inhaltsmodell und darf keine Blockelemente aufnehmen. Der Browser korrigiert die Verschachtelung beim Parsen, aber das HTML ist ungültig und die resultierende DOM-Struktur ist nicht die, die du geschrieben hast. Umgekehrt ist ein <span> in einem <div> selbstverständlich erlaubt.

Macht role="generic" an <div> einen Unterschied?

Nein, das ist die Rolle, die <div> und <span> ohnehin haben – das Attribut wäre reine Dopplung. Wenn du eine Rolle brauchst, brauchst du in aller Regel ein anderes Element.

Was ist mit <div role="button"> – geht das nicht auch?

Es kann funktionieren, kostet aber tabindex="0", einen Tastatur-Handler für Enter und Leertaste, die Zustandspflege über aria-disabled und einen Test mit mindestens zwei Screenreadern. Ein <button> liefert das alles mit weniger Code. Der einzige Grund für den Nachbau ist eine Umgebung, in der <button> technisch nicht verfügbar ist – und die begegnet mir praktisch nie.

Verwandte Themen

Quellen

Gratis E-Book PDF Das Praxishandbuch

Kostenloses E-Book

Das Praxishandbuch für sauberes, zugängliches Web

Alles rund um semantisches HTML, Barrierefreiheit, WCAG & BFSG, GEO und SEO — praxisnah und am echten Code. In mehreren Feedbackschleifen von Leserinnen und Lesern verbessert.

  • 3.000+ Downloads
  • 7. Auflage
  • 37 Seiten
  • PDF

Kein Spam. Abmeldung jederzeit mit einem Klick möglich.