Zum Inhalt springen
OneKitly

Markdown-Aufgabenlisten und was tatsächlich wo dargestellt wird

Veröffentlicht am 2.7.2025 · 10 Min. Lesezeit · Text- & Sprach-Tools

Daniel Okonkwo

Daniel OkonkwoFront-end-Entwickler und Tech-Redakteur bei OneKitly

Web-Performance · Dateiformate

Anhand von 4 Quellen geprüft

Profil ansehen
Kurz gesagt

Eine Markdown-Aufgabenliste — ein Listeneintrag, der mit [ ] oder [x] beginnt — gehört nicht zu CommonMark. Wir haben die CommonMark-Spezifikation 0.31.2 vom 28. Januar 2024 durchsucht: Die Ausdrücke „task list“ und „checkbox“ kommen darin null Mal vor. Aufgabenlisten sind in der Spezifikation GitHub Flavored Markdown 0.29-gfm vom 6. April 2019, Abschnitt 5.3, als Erweiterung definiert. Das ist die ganze Erklärung dafür, dass dieselbe Datei auf GitHub Kontrollkästchen und anderswo wörtliche Klammern zeigt. Wir haben eine Zeile, „- [ ] Milch kaufen“, durch vier Renderer geschickt: Nur der GitHub-Variante erzeugte ein input-Element vom Typ checkbox; die CommonMark-Referenzimplementierung und zwei weitere Konfigurationen erzeugten einen Listeneintrag mit dem wörtlichen Text [ ] Milch kaufen. Die Markerregel ist exakt und unnachgiebig: eine optionale Folge von Leerzeichen, eine öffnende Klammer, entweder ein Leerraumzeichen oder der Buchstabe x in beliebiger Schreibung, eine schließende Klammer und dann mindestens ein Leerraumzeichen vor dem Inhalt. „- [x]Milch“ ohne Leerzeichen erscheint überall als Text, „- []“ erscheint als Text, und „- [ ]“ mit zwei Leerzeichen ebenso. Dieselbe Vorsicht gilt für Tabellen, Durchstreichung und Autolinks, ebenfalls Erweiterungen, und für Fußnoten, die in keiner der Spezifikationen stehen.

Aufgabenlisten stehen nicht in CommonMark. Sie sind eine Erweiterung von GitHub Flavored Markdown, weshalb dieselbe Datei hier Kontrollkästchen und dort wörtliche Klammern zeigt. Die genaue Markerregel, was Verschachtelung bewirkt, und eine Tabelle darüber, was CommonMark ist, was GFM und was keines von beidem — gegen beide Spezifikationen und vier Renderer geprüft.

Eine Zeile, vier Renderer, zwei verschiedene Dokumente

Wir haben eine einzige Zeile genommen — Bindestrich, Leerzeichen, leeres Klammerpaar, Leerzeichen, etwas Text — und sie auf vier Arten dargestellt. Mit eingeschalteter GitHub-Variante wurde daraus ein Listeneintrag mit einem input-Element vom Typ checkbox, deaktiviert. Mit derselben Bibliothek bei ausgeschalteter Variante, mit der CommonMark-Referenzimplementierung und mit einer dritten Bibliothek in ihrer Standardkonfiguration wurde daraus ein Listeneintrag mit den Zeichen [ ] gefolgt vom Text. Nichts schlug fehl. Vier korrekte Renderer erzeugten aus einer Datei zwei verschiedene Dokumente.

Der Grund ist dokumentiert, nicht geheimnisvoll. Wir haben die CommonMark-Spezifikation 0.31.2 vom 28. Januar 2024 nach „task list“ und „checkbox“ durchsucht: Beide kommen null Mal vor. Die Spezifikation GitHub Flavored Markdown 0.29-gfm vom 6. April 2019 hat einen Abschnitt 5.3 mit dem Titel „Task list items (extension)“. Das Wort Erweiterung leistet in diesem Titel die ganze Arbeit: GFM ist CommonMark plus fünf benannte Zusätze, und ein Renderer, der nur CommonMark umsetzt, ist nicht defekt, wenn er deine Kontrollkästchen als Klammern ausgibt. Er ist korrekt.

Die Markerregel, Zeichen für Zeichen

GFM-Abschnitt 5.3 definiert einen Aufgabenlisteneintrag als Listeneintrag, dessen erster Block ein Absatz ist, der mit einem Aufgabenlisten-Marker beginnt, gefolgt von mindestens einem Leerraumzeichen vor jedem weiteren Inhalt. Der Marker ist eine beliebige Anzahl Leerzeichen, eine öffnende Klammer, entweder ein Leerraumzeichen oder der Buchstabe x in Klein- oder Großschreibung, und eine schließende Klammer. Jedes Wort dieses Satzes trägt, und die Fehlschläge sind lautlos: Wir haben jede Verletzung ausgeführt, und alle erschienen in allen vier Renderern als gewöhnlicher Text.

Lass das Leerzeichen nach der schließenden Klammer weg und nichts geschieht: „- [x]Milch kaufen“ ist Text. Setz zwei Leerzeichen zwischen die Klammern und nichts geschieht: Der Marker fasst genau ein Zeichen. Lass die Klammern leer und nichts geschieht. Nimm einen anderen Buchstaben als x und nichts geschieht. Rücke den Marker vom Absatzanfang weg — „- Milch [ ] kaufen“ — und nichts geschieht. Streiche die Liste, und „[ ] Milch kaufen“ ist ein Absatz. Was funktioniert, ist großzügiger als erwartet: Der Buchstabe darf ein großes X sein, der Aufzählungspunkt ein Bindestrich, ein Sternchen oder ein Plus, die Liste darf nummeriert sein, und ein Tabulator zählt als das Leerraumzeichen nach der Klammer.

Verschachtelung und die Erst-Block-Regel

Aufgabenlisten lassen sich beliebig verschachteln, und die Spezifikation zeigt das an einem ausgeführten Beispiel. Wir haben einen Elterneintrag mit zwei eingerückten Kindern laufen lassen: Kontrollkästchen beim Elternteil, verschachtelte Liste innerhalb desselben Listeneintrags, Kontrollkästchen bei jedem Kind. Auch das Mischen markierter, unmarkierter und gewöhnlicher Einträge in derselben Liste funktioniert: Der gewöhnliche bleibt gewöhnlich, die markierten werden Kontrollkästchen — das macht eine Checkliste als gemischte Tagesordnung lesbar.

Die Forderung, dass der erste Block ein Absatz sein muss, übersieht man leicht. Setz ein Zitat an den Anfang — „- > [ ] zitiert“ — und kein Renderer erzeugt ein Kontrollkästchen, auch der GitHub-Variante nicht, denn der erste Block ist ein Zitat, und der Marker kommt nie zum Zug. Füge nach dem markierten Absatz einen zweiten hinzu, und das Kontrollkästchen überlebt: Es steht vor dem ersten Absatz, der zweite folgt im selben Eintrag. Das sind keine Renderer-Marotten, sondern folgt unmittelbar aus dem Satz in Abschnitt 5.3.

Die benachbarten Erweiterungen und eine, die in keiner Spezifikation steht

Tabellen sind Abschnitt 4.10 der GFM-Spezifikation, ebenfalls eine Erweiterung. Unsere dreizeilige Pipe-Tabelle kam unter GitHub-Variante und unter den Standardeinstellungen einer anderen Bibliothek als echtes table-Element heraus und unter der CommonMark-Referenzimplementierung als ein einziger Absatz mit wörtlichen Strichen. Die Durchstreichung ist Abschnitt 6.5 und seltsamer, als sie aussieht: Die Spezifikation erlaubt eine oder zwei Tilden, also wird ~hier~ durchgestrichen, drei oder mehr aber nicht, also bleibt ~~~nein~~~ wörtlich. Eine getestete Bibliothek strich mit zwei Tilden durch, mit einer nicht, und gab ein Durchstreichungselement statt des in der GFM-Spezifikation gezeigten Gelöscht-Elements aus. Zwei Renderer können beide Durchstreichung unterstützen und sich dennoch über Eingabe und Ausgabe uneinig sein.

Autolinks gibt es in zwei Formen, und nur eine gehört zum Kern. Eine Adresse in spitzen Klammern ist CommonMark und wurde in jedem getesteten Renderer zum Link. Eine nackte Adresse ist Abschnitt 6.9 von GFM, eine Erweiterung mit eigenen Regeln: Vor eine www-Adresse wird das Schema http gesetzt, und abschließende Interpunktion bleibt außerhalb des Links, sodass „Besuch www.commonmark.org.“ die Adresse verlinkt und den Punkt draußen lässt. Nur die GitHub-Konfiguration erzeugte diese Links; die anderen ließen den Text unangetastet.

Fußnoten sind der lehrreiche Fall, denn sie stehen in keiner der beiden Spezifikationen. Das Wort footnote kommt in der CommonMark-Spezifikation genau einmal und in der GFM-Spezifikation genau einmal vor, im selben historischen Einleitungssatz über Implementierungen, die Konventionen für Fußnoten und Tabellen hinzufügten. Alle vier Renderer ließen [^1] als wörtlichen Text stehen und machten aus der Definitionszeile einen verwaisten Absatz. GitHub selbst stellt Fußnoten dar, und genau darin liegt die Falle: Eine Funktion, die man hat funktionieren sehen, gehört deshalb noch nicht zum Format.

„Markdown“ ist nicht ein Format

Zwei Spezifikationen mit verschiedenen Versionsnummern und fünf Jahren Abstand, dazu ein Optionssatz je Bibliothek — das ist kein einziges Format. Die Uneinigkeiten reichen weit unter die Ebene der Funktionen. Beim harten Zeilenumbruch gaben zwei unserer Renderer ein selbstschließendes Umbruch-Tag aus und zwei die HTML5-Form. Bei einem script-Element in der Quelle reichten zwei es unverändert in die Ausgabe durch und einer maskierte es als Text; die GFM-Spezifikation hat eine ganze Erweiterung, Abschnitt 6.11, die neun bestimmte Tags filtert, indem sie deren öffnende spitze Klammer durch eine Entität ersetzt. Nichts davon ist ein Fehler.

Die praktische Regel daraus ist kurz: Schreibe für den Renderer, auf den du tatsächlich zielst, und wisse, welcher das ist. Eine README auf einer Code-Plattform, ein Dokumentations-Generator, ein Chat-Client und ein Static-Site-Builder sind vier Ziele mit vier Funktionsumfängen, und ein Dokument, das in einem korrekt erscheint, gibt keine Garantie für die anderen. Ist das Ziel unbekannt, bleib innerhalb von CommonMark: Überschriften, Hervorhebung, Listen, Code, Links, Autolinks in spitzen Klammern und Zitate sind überall gleich.

Teste den Hin- und Rückweg und zähl aus der Quelle

Der billigste Test einer Checkliste ist Arithmetik. Wir haben eine verschachtelte Sprint-Liste mit fünf Einträgen geschrieben, zwei davon als erledigt markiert, sie aus der Markdown-Quelle mit einem Muster gezählt, das den Marker am Eintragsanfang erkennt, und 2 von 5 erhalten. Dasselbe Dokument mit GitHub-Variante gerendert und die angehakten input-Elemente im HTML gezählt ergab ebenfalls 2 von 5. Mit der CommonMark-Referenzimplementierung gerendert ergab es null input-Elemente — das Dokument ist unversehrt, die Kontrollkästchen waren in diesem Dialekt nur nie ein Merkmal.

Das ist die Gewohnheit, die sich lohnt: aus der Quelle zählen, nicht aus der Darstellung. Die Markdown-Datei ist der Datensatz; das HTML ist eine Deutung davon, und welche du bekommst, hängt von einer Bibliotheksversion und einem Schalter ab. Unser Checklisten-Generator schreibt den GFM-Marker genau so, wie Abschnitt 5.3 ihn festlegt — Aufzählungszeichen, Leerzeichen, Klammer, ein Zeichen, Klammer, Leerzeichen —, sodass die Datei dort funktioniert, wo die Erweiterung unterstützt wird, und dort, wo nicht, zu einer lesbaren Klammerliste zerfällt. Muss das Kontrollkästchen überall überleben, ist die ehrliche Alternative eine einfache Liste mit dem Wort „erledigt“ darin.

Was jede Spezifikation definiert und was vier Renderer tatsächlich erzeugt haben, ausgeführt in Node 26.3.0 mit marked 18.0.7, der commonmark-Referenzimplementierung 0.31.2 und markdown-it 15.0.0 in zwei Voreinstellungen. „GFM“ heißt hier marked mit aktiviertem gfm; „markdown-it Standard“ ist seine Auslieferungskonfiguration.
FunktionIn CommonMark 0.31.2?In der GFM-Spez. 0.29?Ausgabe der ReferenzimplementierungAusgabe von markdown-it Standard
Aufgabenliste - [ ] / - [x]Nein — 0 ErwähnungenJa — Abschnitt 5.3, ErweiterungWörtliches [ ] im ListeneintragWörtliches [ ] im Listeneintrag
Pipe-TabelleNeinJa — Abschnitt 4.10, ErweiterungEin Absatz mit wörtlichen StrichenEin echtes table-Element
Durchstreichung mit zwei TildenNeinJa — Abschnitt 6.5, ErweiterungWörtliche TildenEin durchgestrichenes Element, aber s statt del
Durchstreichung mit einer TildeNeinJa — eine oder zwei TildenWörtliche TildenWörtliche Tilden — es verlangt zwei
Nackte URL wird zum LinkNeinJa — Abschnitt 6.9, ErweiterungReiner TextReiner Text in den getesteten Konfigurationen
Autolink in spitzen KlammernJa — Teil der KernspezifikationJa, geerbtEin echter LinkEin echter Link
Fußnote [^1]NeinNein — auch nicht in der SpezifikationWörtliches [^1] und eine verwaiste DefinitionszeileWörtliches [^1] und eine verwaiste Definitionszeile
Rohes HTML durchgereichtJa, standardmäßigJa, abzüglich neun gefilterter TagsUnverändert durchgereichtAls Text maskiert
Markdown-Checklisten-GeneratorWandle eine Zeilenliste in eine Markdown-Aufgabenliste um (- [ ] Element).Tool ausprobieren

Häufige Fragen

Warum erscheinen meine Kontrollkästchen als [ ] statt als Kästchen?
Weil der Renderer CommonMark ohne die GitHub-Erweiterungen umsetzt. Aufgabenlisten sind Abschnitt 5.3 der GFM-Spezifikation und kommen in CommonMark 0.31.2 nirgends vor. Die zweite Möglichkeit ist ein fehlerhafter Marker: Das Leerzeichen nach der schließenden Klammer ist Pflicht, zwischen die Klammern gehört genau ein Zeichen, und der Marker muss den ersten Absatz des Eintrags eröffnen. All diese Fehler erscheinen ohne Warnung als wörtlicher Text.
Muss das x klein geschrieben sein?
Nein. Die GFM-Spezifikation sagt: der Buchstabe x in Klein- oder Großschreibung, und wir haben bestätigt, dass beide ein angehaktes Kästchen ergeben. Kein anderer Buchstabe tut das: ein o zwischen den Klammern erschien in jedem getesteten Renderer als wörtlicher Text. Auch zwei Zeichen sind nicht erlaubt — ein Marker mit zwei Leerzeichen zwischen den Klammern ist kein Marker.
Lassen sich Aufgabenlisten verschachteln oder in nummerierten Listen verwenden?
Beides. Die GFM-Spezifikation zeigt beliebig verschachtelte Aufgabenlisten in einem ausgeführten Beispiel, und unser Lauf erzeugte ein Kontrollkästchen beim Elternteil und bei jedem eingerückten Kind. Ein nummerierter Listeneintrag funktioniert genauso — der Marker steht nach Zahl und Punkt. Nicht funktioniert ein Eintrag, dessen erster Block kein Absatz ist: Ein Zitat vor dem Marker unterdrückte das Kästchen in jedem Renderer.
Sind Tabellen und Durchstreichung überall sicher?
Nein. Beide sind GFM-Erweiterungen, Abschnitte 4.10 und 6.5, und keine steht in CommonMark. Unsere Pipe-Tabelle kam in zwei Konfigurationen als echte Tabelle heraus und in einer anderen als Absatz mit wörtlichen Strichen. Die Durchstreichung ist schlimmer: Die GFM-Spezifikation streicht Text zwischen einer oder zwei Tilden durch, aber nicht bei dreien, während eine andere verbreitete Bibliothek zwei verlangte und ein anderes HTML-Element für das Ergebnis erzeugte.
Gehören Fußnoten zu GitHub Flavored Markdown?
Nach der veröffentlichten GFM-Spezifikation nicht. Das Wort footnote kommt darin genau einmal vor, in einem historischen Satz der Einleitung, und es gibt keinen Abschnitt über Fußnoten. GitHubs eigene Website stellt sie dennoch dar, was den Unterschied zwischen Spezifikation und Produkt gut zeigt. Alle vier getesteten Renderer ließen den Verweis als wörtlichen Text und die Definition als verwaisten Absatz stehen.

Artikel, die dich interessieren könnten

Alle Ratgeber
RatgeberZwischen Listenformaten konvertieren, ohne Daten zu verlieren: die Anführungsregeln, die niemand liestAus einer Liste mit Zeilenumbrüchen eine Kommaliste zu machen ist trivial, bis ein Eintrag ein Komma enthält. Die Anführungsregeln von RFC 4180, warum ein CSV-Feld einen Zeilenumbruch enthalten darf, warum europäische Tabellenkalkulationen das Semikolon verwenden und was ein leerer Eintrag mit dem Hin- und Rückweg macht — jeder Fall ausgeführt und abgedruckt.RatgeberMarkdown entfernen: was der Klartext verliert und was eine Regex falsch machtEin Link wird zu Text, dessen Ziel gelöscht ist, eine verschachtelte Liste verliert ihre Hierarchie, eine Tabelle wird zu einer Reihe von Wörtern. Dann die technische Hälfte: Markdown hat keine einzige Spezifikation, und ein Regex-Entferner verstümmelt einen Dateinamen, ein Malzeichen und das Innere eines Codeblocks - alles gegen einen echten Parser gehalten.ErklärungWo eine Zeile brechen darf: der Unicode-Algorithmus hinter jedem umbrochenen Absatz„An Leerzeichen umbrechen“ scheitert in den meisten Schriftsystemen der Welt. UAX #14 gibt jedem Zeichen eine Zeilenumbruchklasse; wir haben unsere in Unicode 17.0.0 nachgeschlagen und eine konforme Implementierung über geschützte Leerzeichen, weiche Trennstriche, Nullbreiten-Leerzeichen, URLs, Japanisch und Thai laufen lassen.AnleitungZeilen nach einem Muster filtern — ohne KommandozeileDas ist grep für Menschen, die grep nicht benutzen — mit einem wichtigen Unterschied: Gesucht wird eine reine Teilzeichenkette, ein echter regulärer Ausdruck liefert also ein leeres Feld und keine Fehlermeldung. Jede Aussage hier wurde durch Ausführen des Werkzeugs geprüft.ErklärungWörter zu zählen ist mehrdeutig, und jedes Werkzeug antwortet andersEine Wortzahl ist eine Definition, keine Messung. Wir haben denselben Absatz auf vier Arten gezählt und 25, 28, 33 und 38 erhalten — dann 50.000 Zeichen gewöhnlicher Prosa gezählt und Übereinstimmung auf 4,5 % genau. Der Abstand rührt ausschließlich von Komposita, Zahlen und URLs her.RatgeberZahlen für sechs Sprachen formatieren: Trennzeichen, Währung und der Weg zurück1.234,56 und 1,234.56 sind dieselbe Zahl, und sie zu verwechseln ändert den Wert, den eine Leserin herausliest. Wir haben Intl.NumberFormat für alle sechs Sprachen der Website laufen lassen und jedes Trennzeichen ausgegeben — auch das unsichtbare, das das Französische verwendet — und dann gemessen, warum parseFloat davon nichts rückgängig machen kann.

Ähnliche Tools

Quellen

Hast du einen Fehler in diesem Artikel entdeckt?