Aller au contenu
OneKitly

Les listes de tâches Markdown et ce qui s'affiche vraiment où

Publié le 02/07/2025 · 12 min de lecture · Outils texte & langage

Daniel Okonkwo

Daniel OkonkwoDéveloppeur front-end et rédacteur Tech chez OneKitly

Performance web · Formats de fichiers

Vérifié à partir de 4 sources

Voir le profil
En bref

Une liste de tâches Markdown — un élément de liste commençant par [ ] ou [x] — ne fait pas partie de CommonMark. Nous avons cherché dans la spécification CommonMark 0.31.2 du 28 janvier 2024 : les expressions « task list » et « checkbox » y apparaissent zéro fois. Les listes de tâches sont définies dans la spécification GitHub Flavored Markdown 0.29-gfm du 6 avril 2019, section 5.3, comme une extension. Voilà toute l'explication du fait que le même fichier affiche des cases à cocher sur GitHub et des crochets littéraux ailleurs. Nous avons passé une ligne, « - [ ] acheter du lait », dans quatre moteurs : seul celui à saveur GitHub a produit un élément input de type checkbox ; l'implémentation de référence CommonMark et deux autres configurations ont produit un élément de liste contenant le texte littéral [ ] acheter du lait. La règle du marqueur est exacte et sans indulgence : une suite facultative d'espaces, un crochet ouvrant, soit une espace soit la lettre x dans l'une ou l'autre casse, un crochet fermant, puis au moins une espace avant le contenu. « - [x]acheter » sans espace s'affiche en texte partout, « - [] » s'affiche en texte, et « - [ ] » avec deux espaces aussi. La même prudence vaut pour les tableaux, le texte barré et les liens automatiques, également des extensions, et pour les notes de bas de page, absentes des deux spécifications.

Les listes de tâches ne sont pas dans CommonMark. C'est une extension de GitHub Flavored Markdown, d'où des cases à cocher ici et des crochets littéraux là. La règle exacte du marqueur, l'effet de l'imbrication, et un tableau de ce qui est CommonMark, GFM ou ni l'un ni l'autre — vérifié sur les deux spécifications et quatre moteurs de rendu.

Une ligne, quatre moteurs, deux documents différents

Nous avons pris une seule ligne — un tiret, une espace, une paire de crochets vides, une espace, du texte — et l'avons rendue de quatre façons. Avec l'analyse à saveur GitHub activée, elle devient un élément de liste contenant un élément input de type checkbox, désactivé. Avec la même bibliothèque, saveur désactivée, avec l'implémentation de référence CommonMark, et avec une troisième bibliothèque dans sa configuration par défaut, elle devient un élément de liste contenant les caractères [ ] suivis du texte. Rien n'a échoué. Quatre moteurs corrects ont produit deux documents différents à partir d'un même fichier.

La raison est documentée, pas mystérieuse. Nous avons cherché « task list » et « checkbox » dans la spécification CommonMark 0.31.2 du 28 janvier 2024 : les deux apparaissent zéro fois. La spécification GitHub Flavored Markdown 0.29-gfm du 6 avril 2019 comporte une section 5.3 intitulée « Task list items (extension) ». Le mot extension fait tout le travail dans ce titre : GFM est CommonMark plus cinq ajouts nommés, et un moteur qui n'implémente que CommonMark n'est pas cassé quand il affiche tes cases à cocher en crochets. Il est correct.

La règle du marqueur, caractère par caractère

La section 5.3 de GFM définit un élément de liste de tâches comme un élément dont le premier bloc est un paragraphe commençant par un marqueur de liste de tâches suivi d'au moins une espace avant tout autre contenu. Le marqueur est un nombre facultatif d'espaces, un crochet ouvrant, soit une espace soit la lettre x en minuscule ou en majuscule, et un crochet fermant. Chaque mot de cette phrase porte, et les échecs sont silencieux : nous avons exécuté chaque violation, et toutes se sont affichées en texte ordinaire dans les quatre moteurs.

Omets l'espace après le crochet fermant et rien ne se passe : « - [x]acheter du lait » est du texte. Mets deux espaces entre les crochets et rien ne se passe : le marqueur tient exactement un caractère. Laisse les crochets vides et rien ne se passe. Utilise une lettre autre que x et rien ne se passe. Déplace le marqueur hors du début du paragraphe — « - acheter [ ] du lait » — et rien ne se passe. Supprime la liste et « [ ] acheter du lait » est un paragraphe. Ce qui marche est plus généreux qu'on ne croit : la lettre peut être un X majuscule, la puce peut être un tiret, un astérisque ou un plus, la liste peut être numérotée, et une tabulation compte comme l'espace après le crochet.

L'imbrication, et la règle du premier bloc

Les listes de tâches s'imbriquent librement, et la spécification le montre par un exemple traité. Nous avons exécuté un élément parent avec deux enfants indentés : case à cocher sur le parent, liste imbriquée à l'intérieur du même élément, case à cocher sur chaque enfant. Mélanger des éléments cochés, non cochés et de simples puces dans la même liste fonctionne aussi : l'élément simple reste simple et les éléments marqués deviennent des cases, ce qui rend une checklist lisible comme un ordre du jour mixte.

L'exigence que le premier bloc soit un paragraphe se rate facilement. Mets une citation en tête — « - > [ ] cité » — et aucun moteur ne produit de case à cocher, y compris celui à saveur GitHub, parce que le premier bloc est une citation et que le marqueur n'a jamais sa chance. Ajoute un second paragraphe après celui marqué et la case survit, placée avant le premier paragraphe, le second suivant dans le même élément. Ce ne sont pas des bizarreries de moteur : cela découle directement de la phrase de la section 5.3.

Les extensions voisines, et une qui n'est dans aucune spécification

Les tableaux sont la section 4.10 de la spécification GFM, également une extension. Notre tableau à barres verticales de trois lignes est sorti en vrai élément table sous l'analyse à saveur GitHub et sous les réglages par défaut d'une autre bibliothèque, et en un seul paragraphe de barres littérales sous l'implémentation de référence CommonMark. Le barré est la section 6.5, et il est plus étrange qu'il n'y paraît : la spécification autorise un ou deux tildes, donc ~ici~ est barré, mais trois ou plus ne le sont pas, donc ~~~non~~~ reste littéral. Une bibliothèque testée barrait avec deux tildes mais pas un seul, et émettait un élément de texte barré plutôt que l'élément de texte supprimé montré par la spéc. GFM. Deux moteurs peuvent tous deux prendre en charge le barré et diverger sur l'entrée comme sur la sortie.

Les liens automatiques existent en deux formes et une seule est native. Une adresse entre chevrons est du CommonMark et est devenue un lien dans tous les moteurs testés. Une adresse nue relève de la section 6.9 de GFM, une extension avec ses propres règles : le schéma http est inséré devant une adresse en www, et la ponctuation finale est exclue du lien, si bien que « Visite www.commonmark.org. » lie l'adresse et laisse le point dehors. Seule la configuration à saveur GitHub a produit ces liens ; les autres ont laissé le texte tel quel.

Les notes de bas de page sont le cas instructif, parce qu'elles ne sont dans aucune des deux spécifications. Le mot footnote apparaît exactement une fois dans la spéc. CommonMark et exactement une fois dans la spéc. GFM, dans la même phrase historique de l'introduction, à propos des implémentations qui ont ajouté des conventions pour les notes et les tableaux. Nos quatre moteurs ont laissé [^1] en texte littéral et transformé la ligne de définition en paragraphe orphelin. GitHub, lui, affiche les notes, et c'est exactement le piège : une fonction que tu as vue marcher n'est pas pour autant dans le format.

« Markdown » n'est pas un format

Deux spécifications avec des numéros de version différents et cinq ans d'écart, plus un jeu d'options par bibliothèque, cela ne fait pas un format unique. Les désaccords descendent bien en dessous du niveau des fonctions. Sur une coupure de ligne dure, deux de nos moteurs ont émis une balise de saut auto-fermante et deux la forme HTML5. Sur un élément script dans la source, deux l'ont laissé passer tel quel dans la sortie et un l'a échappé en texte ; la spéc. GFM consacre une extension entière, la section 6.11, au filtrage de neuf balises précises en remplaçant leur chevron ouvrant par une entité. Rien de tout cela n'est un bug.

La règle pratique qui s'ensuit est courte : écris pour le moteur que tu vises réellement, et sache lequel c'est. Un README sur une forge, un générateur de site de documentation, un client de discussion et un constructeur de site statique sont quatre cibles avec quatre jeux de fonctions, et un document qui s'affiche correctement chez l'une n'offre aucune garantie chez les autres. Si la destination est inconnue, reste dans CommonMark : titres, emphase, listes, code, liens, liens automatiques entre chevrons et citations sont identiques partout.

Teste l'aller-retour, et compte depuis la source

Le test le moins cher pour une checklist est arithmétique. Nous avons écrit une liste de sprint imbriquée de cinq éléments, dont deux marqués faits, comptés depuis la source markdown avec un motif reconnaissant le marqueur en tête d'élément : 2 sur 5. Le rendu du même document en analyse à saveur GitHub, avec comptage des éléments input cochés dans le HTML, donne aussi 2 sur 5. Le rendu par l'implémentation de référence CommonMark donne zéro élément input — le document est intact, les cases n'ont simplement jamais fait partie de ce dialecte.

Voilà l'habitude à garder : compte depuis la source, pas depuis le rendu. Le fichier markdown est l'enregistrement ; le HTML n'en est qu'une interprétation, et laquelle tu obtiens dépend d'une version de bibliothèque et d'un drapeau. Notre générateur de checklist écrit le marqueur GFM exactement comme la section 5.3 le spécifie — puce, espace, crochet, un caractère, crochet, espace — de sorte que le fichier fonctionne là où l'extension est prise en charge et se dégrade en liste à crochets lisible là où elle ne l'est pas. S'il faut que la case survive partout, l'alternative honnête est une liste simple contenant le mot « fait ».

Ce que définit chaque spécification et ce que quatre moteurs ont réellement produit, exécutés dans Node 26.3.0 avec marked 18.0.7, l'implémentation de référence commonmark 0.31.2 et markdown-it 15.0.0 en deux préréglages. « GFM » signifie ici marked avec gfm activé ; « markdown-it par défaut » est sa configuration d'origine.
FonctionDans CommonMark 0.31.2 ?Dans la spéc. GFM 0.29 ?Sortie de l'implémentation de référenceSortie de markdown-it par défaut
Liste de tâches - [ ] / - [x]Non — 0 mentionOui — section 5.3, extension[ ] littéral dans l'élément de liste[ ] littéral dans l'élément de liste
Tableau à barres verticalesNonOui — section 4.10, extensionUn paragraphe de barres littéralesUn vrai élément table
Barré à deux tildesNonOui — section 6.5, extensionTildes littérauxUn élément barré, mais s au lieu de del
Barré à un seul tildeNonOui — un ou deux tildesTildes littérauxTildes littéraux — il en exige deux
URL nue transformée en lienNonOui — section 6.9, extensionTexte brutTexte brut dans les configurations testées
Lien automatique entre chevronsOui — dans la spéc. de baseOui, héritéUn vrai lienUn vrai lien
Note de bas de page [^1]NonNon — absente de la spéc. aussi[^1] littéral et une ligne de définition orpheline[^1] littéral et une ligne de définition orpheline
HTML brut laissé passerOui, par défautOui, moins neuf balises filtréesLaissé passer tel quelÉchappé en texte
Générateur de checklist MarkdownTransforme une liste de lignes en checklist Markdown (- [ ] élément).Essayer l'outil

Questions fréquentes

Pourquoi mes cases s'affichent-elles en [ ] au lieu de cases ?
Parce que le moteur implémente CommonMark sans les extensions GitHub. Les listes de tâches sont la section 5.3 de la spéc. GFM et n'apparaissent nulle part dans CommonMark 0.31.2. La seconde possibilité est un marqueur mal formé : l'espace après le crochet fermant est obligatoire, il faut exactement un caractère entre les crochets, et le marqueur doit ouvrir le premier paragraphe de l'élément. Tous ces échecs s'affichent en texte littéral sans aucun avertissement.
Le x doit-il être en minuscule ?
Non. La spéc. GFM dit la lettre x en minuscule ou en majuscule, et nous avons confirmé que les deux donnent une case cochée. Aucune autre lettre ne fonctionne : un o entre les crochets s'affiche en texte littéral dans tous les moteurs essayés. Il ne peut pas non plus y avoir deux caractères — un marqueur avec deux espaces entre les crochets n'est pas un marqueur.
Les listes de tâches peuvent-elles être imbriquées ou numérotées ?
Les deux. La spéc. GFM montre des listes de tâches imbriquées librement dans un exemple traité, et notre exécution a produit une case sur le parent et sur chaque enfant indenté. Un élément de liste numérotée fonctionne pareil — le marqueur se place après le nombre et le point. Ce qui ne fonctionne pas, c'est un élément dont le premier bloc n'est pas un paragraphe : une citation avant le marqueur a supprimé la case dans tous les moteurs.
Les tableaux et le barré sont-ils utilisables partout ?
Non. Les deux sont des extensions GFM, sections 4.10 et 6.5, et aucune n'est dans CommonMark. Notre tableau est sorti en vrai tableau dans deux configurations et en paragraphe de barres littérales dans une autre. Le barré est pire : la spéc. GFM barre le texte entouré d'un ou deux tildes mais pas de trois, tandis qu'une autre bibliothèque très répandue en exigeait deux et produisait un autre élément HTML pour le résultat.
Les notes de bas de page font-elles partie de GitHub Flavored Markdown ?
Pas selon la spécification GFM publiée. Le mot footnote y apparaît exactement une fois, dans une phrase historique de l'introduction, et il n'existe aucune section sur les notes. Le site de GitHub les affiche pourtant, bonne illustration de l'écart entre une spécification et un produit. Les quatre moteurs testés ont laissé l'appel en texte littéral et la définition en paragraphe orphelin.

Articles qui pourraient t'intéresser

Tous les guides
GuideConvertir entre formats de liste sans perdre de données : les règles de guillemets que personne ne litPasser d'une liste à retours à la ligne à une liste à virgules est trivial jusqu'à ce qu'un élément contienne une virgule. Les règles de guillemets de la RFC 4180, pourquoi un champ CSV peut contenir un saut de ligne, pourquoi les tableurs européens utilisent le point-virgule, et ce qu'un élément vide fait à l'aller-retour — chaque cas exécuté et imprimé.GuideRetirer le Markdown : ce que le texte brut perd, et ce qu'une regex se trompe à faireUn lien devient un texte dont la destination a disparu, une liste imbriquée perd sa hiérarchie, un tableau devient une file de mots. Puis la moitié technique : le markdown n'a pas de spécification unique, et un nettoyeur à base de regex abîme un nom de fichier, un signe de multiplication et l'intérieur d'un bloc de code — le tout confronté à un vrai analyseur.ExplicationOù une ligne peut se couper : l'algorithme Unicode derrière chaque paragraphe justifié« Couper aux espaces » échoue dans la plupart des systèmes d'écriture. UAX #14 donne à chaque caractère une classe de coupure ; nous avons cherché les nôtres dans Unicode 17.0.0 et exécuté une implémentation conforme sur espaces insécables, traits d'union conditionnels, espaces de largeur nulle, URL, japonais et thaï.TutorielFiltrer des lignes selon un motif, sans ligne de commandeC'est grep pour ceux qui n'utilisent pas grep, avec une différence de taille : la recherche est une sous-chaîne littérale, si bien qu'une vraie expression régulière renvoie une zone vide sans message d'erreur. Chaque affirmation a été vérifiée en exécutant l'outil.ExplicationCompter les mots est ambigu, et chaque outil répond autrementUn compte de mots est une définition, pas une mesure. Nous avons compté le même paragraphe de quatre façons et obtenu 25, 28, 33 et 38 — puis compté 50 000 caractères de prose ordinaire et obtenu un accord à 4,5 % près. L'écart tient entièrement aux composés, aux chiffres et aux URL.GuideFormater les nombres pour six langues : séparateurs, monnaie et le retour à la valeur1 234,56 et 1,234.56 sont le même nombre, et les confondre change la valeur que lit un lecteur. Nous avons lancé Intl.NumberFormat pour les six locales du site et imprimé chaque séparateur — dont l'invisible qu'emploie le français — puis mesuré pourquoi parseFloat ne peut rien défaire.

Outils similaires

Sources

Tu as repéré une erreur dans cet article ?