Markdown si parsa in due passate: prima la struttura a blocchi, poi il contenuto
inline dentro i blocchi. Questo modulo copre la prima passata. Capirla spiega il
90% dei “perché non funziona”.
2.1 Heading
Due forme. ATX (quella che userai sempre):
# Titolo 1## Titolo 2###### Titolo 6
Regole esatte: da 1 a 6 #, seguiti da uno spazio (#5 bolt non è un heading),
al massimo 3 spazi di indentazione (con 4 diventa un blocco di codice). La sequenza di
chiusura è opzionale: ## Titolo ## è valido.
Setext (quella che ti si presenta per sbaglio):
Titolo 1========Titolo 2--------
La trappola del ---
✅ verificato:
Foobar---baz
produce <h2>Foo bar</h2> seguito da <p>baz</p>. Non un separatore orizzontale, e
si mangia entrambe le righe del paragrafo precedente.
Se vuoi una linea di separazione dopo un paragrafo: lascia una riga vuota prima,
oppure usa ***.
2.2 Paragrafi e a capo
Un paragrafo è una sequenza di righe non vuote. Le righe consecutive vengono unite:
Prima rigaseconda riga
→ <p>Prima riga\nseconda riga</p>: che il browser mostra su una riga sola,
perché in HTML un a capo è uno spazio.
Per andare davvero a capo dentro un paragrafo servono due spazi a fine riga, oppure un
backslash. Ne parliamo in 03 - Inline - enfasi, link, escaping, perché è una regola inline.
2.3 Liste: la regola vera
Qui c’è la cosa più mal insegnata di tutto Markdown. Non è “2 spazi”, non è “4 spazi”.
La regola
Il contenuto di un item inizia alla colonna dove inizia il testo dell’item.
Tutto ciò che appartiene a quell’item, righe successive, sottoliste, paragrafi, > va allineato a quella colonna.
Con - foo il testo inizia a colonna 2, quindi 2 spazi bastano. ✅ verificato:
- foo - bar
→ lista annidata.
Con 10) foo il testo inizia a colonna 4, quindi 3 spazi non bastano. ✅ verificato:
10) foo - bar
→ <ol start="10"><li>foo</li></ol> + <ul><li>bar</li></ul>, due liste separate.
10) foo - bar
→ annidata correttamente.
E il caso che sorprende di più, ✅ verificato:
1. foo - bar
→ due liste. Con 1. il testo parte a colonna 3, e 2 spazi non arrivano.
Numerazione
Conta solo il primo numero; gli altri vengono ignorati e rinumerati.
1. uno1. due1. tre
→ 1, 2, 3. Ed è il modo consigliato di scrivere le liste ordinate: sposti, aggiungi,
elimini righe senza rinumerare niente. 3. come primo item produce <ol start="3">.
Cosa spezza una lista in due
Cambiare tipo di marker. ✅ verificato: - a seguito da * b produce due <ul>
distinti. Lo stesso vale fra 1. e 1). Nel rendering spesso non si vede, ma la
spaziatura cambia e i CSS impazziscono. Scegli un marker e restaci.
Liste “tight” e “loose”
Se una qualsiasi riga vuota separa due item, l’intera lista diventa loose e ogni
item viene avvolto in <p>. ✅ verificato:
- a- b- c
→ tutti e tre gli item in <p>, con spaziatura verticale maggiore. La causa non è
“l’item c”: è la lista intera. È il motivo per cui a volte una lista “si allarga” e non
capisci perché.
Blocchi di codice dentro una lista
Servono 4 spazi oltre la colonna di contenuto. ✅ verificato, con - foo (colonna 2):
- foo codice
→ è un paragrafo, non codice (4 spazi = colonna 2 + 2).
- foo codice
→ questo è codice (6 spazi). In pratica: usa i fence (```), che hanno bisogno
solo dell’allineamento normale e non ti fanno contare gli spazi.
Una lista può interrompere un paragrafo
Contrariamente a quel che si legge in giro, ✅ verificato:
Testo- uno- due
→ <p>Testo</p> + la lista. In CommonMark funziona (nel Markdown.pl originale no).
L’eccezione: una lista ordinata può interrompere un paragrafo solo se parte da 1.
Ecco perché una frase che finisce con “…nel 2026.\n14. Il resto” resta un paragrafo,
mentre “…\n1. Il resto” ti crea una lista che non volevi.
2.4 Blocchi di codice
Fenced (usa questi):
```pythondef ciao(): print("ciao")```
Almeno 3 backtick (o 3 tilde), e una info string dopo l’apertura. La chiusura deve
avere almeno tanti backtick quanti l’apertura: per mostrare un blocco che contiene
backtick, apri con quattro.
Nota che la specifica non impone alcun significato all’info string: la classe
language-python è una convenzione dei renderer, non una regola. In pratica tutti la
rispettano, e Shiki/Prism/Pygments la usano per la colorazione.
Indentati (4 spazi): esistono, funzionano, ma non hanno info string, non possono
interrompere un paragrafo e dentro le liste diventano un incubo di conteggio. Non usarli.
MDX li ha addirittura rimossi.
2.5 Blockquote
> Citazione.> Continua.
La cosa da sapere è la lazy continuation. ✅ verificato:
> foobar
→ <blockquote><p>foo\nbar</p></blockquote>: la seconda riga entra nella citazione
anche senza >, perché è “testo di continuazione del paragrafo”. È comodo e insidioso
allo stesso tempo: per uscire davvero dalla citazione serve una riga vuota.
Lo stesso meccanismo vale nelle liste: una riga non indentata subito dopo un item ne fa
comunque parte, ma una riga vuota + testo a colonna 0 chiude la lista.
2.6 Separatore orizzontale
Tre o più -, * o _uguali fra loro, con al massimo 3 spazi di indentazione.
*** è la scelta più sicura, perché --- dopo un paragrafo diventa un heading (§2.1) e
--- a inizio file è un delimitatore di frontmatter.
2.7 HTML dentro Markdown
CommonMark permette blocchi HTML grezzi. La regola pratica da ricordare è una sola:
dentro un blocco HTML, il Markdown non viene più interpretato, e il blocco finisce
alla prima riga vuota (per i tag comuni).
<div>**questo non diventa grassetto**</div><div>**questo sì**, perché la riga vuota ha chiuso il blocco HTML</div>
È il trucco per usare <details> su GitHub con contenuto formattato dentro: metti una
riga vuota dopo il <summary>.
Non vengono convertite in spazi, ma “contano” come se lo fossero con tab stop di 4.
Tradotto: non usare i tab per indentare Markdown. Il tuo editor e il parser possono
avere idee diverse su quanto valga un tab, e il risultato è indentazione che sembra
giusta e non lo è.
Esercizi
Riproduci nel Dingus i tre casi di annidamento di §2.3 (- foo con 2 spazi,
10) foo con 3 e con 4). Guarda l’albero sintattico, non solo l’HTML.
Scrivi una lista con un blocco di codice dentro un item, prima con i fence e poi
con l’indentazione. Cronometra quale delle due ti fa perdere più tempo.
Prendi una tua nota lunga e cerca liste diventate loose per una riga vuota di
troppo. Sono quasi sempre involontarie.
Scrivi un <details> con dentro una lista formattata: fallo funzionare.