CSS content-Property: generierte Inhalte, attr(), counter() und Icons
AI generated
{ }
@
CSS · Pseudo-Elemente · Icons · Barrierefreiheit
Die CSS content-Property
Generierte Inhalte, attr()-Werte, Zaehler und Icons ohne zusaetzliches HTML

content bei ::before und ::after wird oft nur fuer leere dekorative Elemente genutzt, kann aber deutlich mehr: attr() liest HTML-Attribute direkt aus, counter() nummeriert Listen und Kapitel automatisch, und content: url() bindet Bild-Icons ein, ganz ohne zusaetzliches Markup. Wer diese Moeglichkeiten kennt, spart HTML-Elemente, muss dabei aber die Zugaenglichkeits-Grenzen von generiertem Inhalt genau verstehen.

15 Min. Lesezeit ::before · ::after attr() · counter() · content: url()

1. content bei ::before und ::after: mehr als nur reiner Text

Die content-Eigenschaft ist zwingend erforderlich, damit ein ::before- oder ::after-Pseudo-Element ueberhaupt sichtbar wird, selbst ein leerer String content: "" reicht dafuer aus. In der Praxis wird content aber meist nur fuer feste Text-Strings oder leere dekorative Elemente eingesetzt, obwohl die Spezifikation deutlich mehr Datentypen erlaubt: HTML-Attribute, automatische Zaehler und sogar externe Bild-Ressourcen.

Der entscheidende Unterschied zu echtem HTML-Inhalt ist, dass generierter Inhalt niemals Teil des DOM wird. Er erscheint visuell und wird in bestimmten Faellen von Screenreadern vorgelesen, laesst sich aber nicht mit JavaScript selektieren, nicht kopieren und nicht ueber document.querySelector ansprechen. Diese Eigenschaft macht content ideal fuer rein praesentationelle Ergaenzungen, aber ungeeignet fuer inhaltlich relevante Informationen.

2. content mit reinem Text: Quotes, Escaping und typische Einsatzzwecke

Der einfachste Fall ist ein fester String in Anfuehrungszeichen, etwa fuer typografische Anfuehrungszeichen bei Zitaten, Trennzeichen zwischen Breadcrumb-Elementen oder ein Sternchen bei Pflichtfeldern in Formularen. Sonderzeichen lassen sich entweder direkt als Unicode-Zeichen im CSS einfuegen oder ueber Escape-Sequenzen wie \2192 fuer einen Pfeil nach rechts, was bei Encoding-Problemen im Editor die robustere Variante ist.

Wichtig ist, dass reiner Text-Content bei jedem Rendering neu erzeugt wird und nicht Teil der eigentlichen Seitensprache im Sinne von uebersetzbaren Strings ist. Fuer mehrsprachige Websites sollte deshalb kein inhaltlich bedeutsamer Text ueber content eingefuegt werden, sondern nur rein dekorative oder sprachneutrale Symbole wie Trennzeichen oder Pfeile.


.breadcrumb li:not(:last-child)::after {
  content: " \2192 "; /* Pfeil nach rechts als Trenner */
  color: #94a3b8;
}

.pflichtfeld label::after {
  content: " *";
  color: #dc2626;
}

3. attr(): HTML-Attribute direkt in generierten Inhalt einfuegen

Die Funktion attr() liest den Wert eines HTML-Attributs des aktuellen Elements aus und fuegt ihn als Text in content ein. Ein haeufiger Einsatz ist die Anzeige von data-Attributen, etwa ein Tooltip-Text aus data-tooltip, ohne dass dafuer ein zusaetzliches HTML-Element noetig waere, das den Text dupliziert.

Der grosse Vorteil gegenueber dupliziertem Markup ist, dass sich Aenderungen am data-Attribut per JavaScript sofort auf den sichtbaren Inhalt auswirken, ganz ohne dass CSS oder der generierte Inhalt selbst angefasst werden muessen. Die Grenze von attr() ist, dass es aktuell nur Strings liefert, keine strukturierten Werte, und dass es sich nicht fuer sicherheitskritische oder rein inhaltliche Informationen eignet, weil Screenreader-Unterstuetzung fuer attr()-Werte inkonsistent ist.


<span class="tooltip-trigger" data-tooltip="Kostenloser Versand ab 50 Euro">
  Versandkosten
</span>

<style>
.tooltip-trigger::after {
  content: attr(data-tooltip);
  display: none;
  position: absolute;
  background: #1e293b;
  color: white;
  padding: 0.5rem;
  border-radius: 0.375rem;
}
.tooltip-trigger:hover::after {
  display: block;
}
</style>

4. counter(): automatische Nummerierung fuer Listen und Kapitel

CSS-Zaehler mit counter-reset, counter-increment und der counter()-Funktion innerhalb von content erzeugen automatische Nummerierungen, die bei jeder Aenderung der Reihenfolge oder Anzahl der Elemente korrekt bleiben, ohne dass eine einzige Zahl manuell im HTML gepflegt werden muss. Das ist besonders nuetzlich bei verschachtelten Strukturen wie mehrstufigen FAQ-Listen oder Tutorial-Kapiteln.

Verschachtelte Zaehler koennen sogar mehrstufige Nummerierungen wie 2.3 erzeugen, indem ein aeusserer und ein innerer Zaehler kombiniert werden. Der Browser verwaltet dabei automatisch die Verschachtelungstiefe, solange counter-reset auf dem umschliessenden Element und counter-increment auf jedem gezaehlten Kindelement korrekt gesetzt sind.


.kapitel-liste {
  counter-reset: kapitel;
}

.kapitel-liste .kapitel {
  counter-increment: kapitel;
}

.kapitel-liste .kapitel::before {
  content: "Kapitel " counter(kapitel) ": ";
  font-weight: bold;
}

5. Icon-Fonts gegen content-generierte SVG- und Unicode-Icons

Icon-Fonts binden Icons als Zeichen einer speziellen Schriftart ein und nutzen dafuer haeufig content mit einem Unicode-Codepunkt, der in der Icon-Schriftart auf ein Glyphen-Symbol statt eines Buchstabens zeigt. Das funktioniert visuell zuverlaessig, hat aber einen strukturellen Nachteil: Screenreader lesen den zugrunde liegenden Unicode-Codepunkt oft als bedeutungslosen Buchstaben oder gar nicht vor, was ohne zusaetzliche aria-hidden-Behandlung verwirrende Ausgaben erzeugt.

Als moderne Alternative bindet content: url("icon.svg") eine echte SVG-Datei als generierten Inhalt ein, ohne die Font-Datei-Abhaengigkeit und ohne das Risiko fehlerhafter Zeichenkodierung. Der Nachteil gegenueber Icon-Fonts ist, dass sich SVG-Icons ueber content: url() nicht per CSS einfaerben lassen, waehrend Icon-Font-Zeichen einfach mit color gefaerbt werden koennen, weil sie technisch Text sind.


/* Icon-Font-Ansatz: einfaerbbar, aber Codepunkt-Risiko */
.icon-warenkorb::before {
  content: "\e901";
  font-family: "IconFont";
  color: #4a1d96; /* faerbbar */
}

/* SVG-Ansatz: robuster, aber nicht einfaerbbar per CSS */
.icon-warenkorb-svg::before {
  content: url("/icons/warenkorb.svg");
}

6. content: url() fuer Bild-Icons ohne zusaetzliches img-Element

content: url() laedt eine Bilddatei, meist SVG oder PNG, und rendert sie als generierten Inhalt des Pseudo-Elements, ohne dass dafuer ein <img>-Tag im HTML noetig ist. Das ist praktisch fuer rein dekorative Icons, die konsequent nur ueber CSS gesteuert werden sollen, etwa ein Haekchen-Symbol vor jedem Listenpunkt einer Feature-Liste.

Wichtig ist, dass ueber content: url() geladene Bilder in ihrer Originalgroesse gerendert werden und sich nicht mit width oder height auf dem Pseudo-Element skalieren lassen. Fuer skalierbare Icon-Groessen ist deshalb entweder ein SVG mit passend gesetzter Original-Groesse noetig, oder ein Wechsel zu background-image mit background-size, das echte Groessenkontrolle bietet.


.feature-liste li::before {
  content: url("/icons/check-16.svg"); /* feste 16px-Groesse im SVG selbst */
  margin-right: 0.5rem;
  vertical-align: middle;
}

7. Zugaenglichkeits-Implikationen: was Screenreader mit generiertem Inhalt tun

Das Verhalten von Screenreadern gegenueber content-generiertem Text ist uneinheitlich zwischen Browsern und Assistenztechnologien: Manche lesen reinen Text-Content vor, andere ignorieren ihn komplett, wieder andere lesen ihn nur unter bestimmten Konfigurationen. Diese Unsicherheit bedeutet in der Praxis: content darf niemals die einzige Quelle fuer inhaltlich wichtige Information sein, etwa eine Fehlermeldung oder ein Pflichtfeld-Hinweis, der ausschliesslich per CSS eingeblendet wird.

Fuer rein dekorative generierte Inhalte, etwa Trennzeichen, Icon-Symbole oder visuelle Zaehler-Nummern, die keine eigene Bedeutung tragen, ist das inkonsistente Vorlesen unproblematisch, weil ohnehin niemand erwartet, dass sie vorgelesen werden. Kritisch wird es nur, wenn ein Entwickler versehentlich echten Inhalt in content auslagert, der dann fuer Screenreader-Nutzer teilweise unsichtbar wird, obwohl er visuell klar erkennbar ist.

8. Praktisches Rezept: Tooltip-Pfeile und Zaehler-Badges mit content

Ein CSS-Tooltip mit sichtbarem Pfeil, der auf das Ausloeser-Element zeigt, laesst sich vollstaendig ueber zwei Pseudo-Elemente bauen: ::before fuer die Tooltip-Box selbst mit content: attr(data-tooltip), und ::after fuer einen kleinen gedrehten Quadrat-Pfeil mit leerem content: "", der nur durch Border-Tricks wie ein Dreieck aussieht.

Ein Warenkorb-Badge mit Artikelanzahl kombiniert counter() oder attr() mit absoluter Positionierung: content: attr(data-count) zeigt die Zahl aus einem data-count-Attribut an, das per JavaScript oder Alpine.js aktualisiert wird, waehrend das Pseudo-Element selbst nur fuer die visuelle Kreisform und Positionierung zustaendig ist.


<button class="warenkorb-icon" data-count="3">
  Warenkorb
</button>

<style>
.warenkorb-icon {
  position: relative;
}
.warenkorb-icon::after {
  content: attr(data-count);
  position: absolute;
  top: -6px;
  right: -6px;
  background: #dc2626;
  color: white;
  border-radius: 9999px;
  font-size: 0.7rem;
  padding: 0.1rem 0.4rem;
}
</style>

9. Wann content nicht die richtige Wahl ist

Jeder Inhalt, der fuer das Verstaendnis der Seite notwendig ist, gehoert ins eigentliche HTML-Markup, nicht in generierten CSS-Inhalt. Das gilt fuer Produktnamen, Preise, Fehlermeldungen, Formularhinweise und alles, was ein Nutzer kopieren, per Suchfunktion im Browser finden oder von einem Screenreader zuverlaessig vorgelesen bekommen soll. Generierter Inhalt ist ein Werkzeug fuer Praesentation, nicht fuer Semantik.

Methode Einfaerbbar per CSS Skalierbar Screenreader-Verhalten
Icon-Font (content + Codepunkt) Ja, ueber color Ja, ueber font-size Uneinheitlich, oft Codepunkt hoerbar
content: url() mit SVG Nein Nein, feste Bildgroesse Meist ignoriert, unproblematisch fuer Deko
Inline SVG im HTML Ja, ueber fill/stroke Ja, ueber width/height Kontrollierbar via aria-hidden/title
background-image Nein Ja, ueber background-size Immer ignoriert, rein dekorativ
attr() fuer Text/Zahlen Ja, wie normaler Text Ja, ueber font-size Uneinheitlich, nicht fuer wichtige Infos geeignet

Mironsoft

Modernes CSS, Layout-Architektur und Rendering-Performance

CSS, das wartbar bleibt statt mit jeder Änderung zu brechen?

Wir prüfen bestehende Stylesheets auf Spezifitäts-Chaos und Layout-Thrashing und bauen daraus eine CSS-Architektur mit Cascade Layers, Custom Properties und modernen Layout-Primitiven, die auch nach dem zehnten Feature noch verständlich ist.

CSS-Audit

Spezifität, Cascade-Konflikte und ungenutzte Selektoren systematisch aufdecken.

Architektur-Refactoring

Cascade Layers, Custom Properties und Design Tokens sauber einführen.

Performance-Tuning

Layout-Thrashing, teure Selektoren und Rendering-Engpässe gezielt beheben.

10. Zusammenfassung

CSS content-Property: Das Wichtigste auf einen Blick

Grundprinzip

content bei ::before/::after erzeugt rein visuellen, nicht im DOM verankerten Inhalt, ideal fuer Praesentation, nicht Semantik.

attr() und counter()

attr() liest HTML-Attribute direkt aus, counter() erzeugt automatische, immer korrekte Nummerierung ohne manuelle Pflege.

Icons

Icon-Fonts sind einfaerbbar aber mit Screenreader-Risiko, SVG ueber content: url() ist robuster aber nicht per CSS faerbbar.

Zugaenglichkeit

Inhaltlich wichtige Informationen gehoeren immer ins echte HTML, generierter Inhalt bleibt rein dekorativ.

11. FAQ: CSS content-Property: Das Wichtigste auf einen Blick

1Wird content bei ::before/::after Teil des DOM?
Nein, generierter Inhalt erscheint nur visuell und laesst sich nicht mit JavaScript selektieren, kopieren oder ueber document.querySelector ansprechen.
2Was macht attr() in content genau?
attr() liest den Wert eines HTML-Attributs des aktuellen Elements aus und fuegt ihn als Text in den generierten Inhalt ein, zum Beispiel content: attr(data-tooltip).
3Wie funktioniert automatische Nummerierung mit counter()?
counter-reset setzt einen Zaehler auf einem umschliessenden Element zurueck, counter-increment erhoeht ihn auf jedem gezaehlten Kindelement, und counter() innerhalb von content zeigt den aktuellen Wert an.
4Sind Icon-Fonts oder content: url() mit SVG die bessere Wahl?
Icon-Fonts lassen sich einfach per color einfaerben, haben aber ein Screenreader-Risiko durch den zugrunde liegenden Codepunkt. SVG ueber content: url() ist robuster, aber nicht per CSS einfaerbbar.
5Kann ich SVG-Icons ueber content: url() mit CSS einfaerben?
Nein. Bilder, die ueber content: url() geladen werden, verhalten sich wie ein eingebettetes Bild und lassen sich nicht mit fill oder color aus dem Pseudo-Element heraus faerben.
6Lesen Screenreader content-generierten Text vor?
Uneinheitlich. Manche Browser und Assistenztechnologien lesen ihn vor, andere ignorieren ihn komplett. Deshalb darf content niemals die einzige Quelle fuer wichtige Information sein.
7Sollte ich Fehlermeldungen ueber content anzeigen?
Nein. Fehlermeldungen und andere inhaltlich wichtige Informationen gehoeren ins echte HTML-Markup, damit sie zuverlaessig kopierbar, durchsuchbar und fuer Screenreader zugaenglich sind.
8Warum ist ein leerer content: "" trotzdem noetig?
Ohne eine content-Deklaration wird das Pseudo-Element ::before oder ::after ueberhaupt nicht gerendert, selbst wenn andere Eigenschaften wie width oder background gesetzt sind.
9Kann ich mit content mehrstufige Nummerierungen wie 2.3 erzeugen?
Ja, durch Verschachtelung zweier Zaehler, einen aeusseren fuer die Hauptnummer und einen inneren fuer die Unternummer, kombiniert in einem gemeinsamen content-Wert.
10Eignet sich content fuer mehrsprachige Websites?
Nur fuer sprachneutrale Symbole wie Pfeile oder Trennzeichen. Inhaltlich bedeutsamer, uebersetzbarer Text sollte immer aus dem HTML oder einem Uebersetzungssystem kommen, nicht aus CSS.