02 - Blocchi: paragrafi, liste, codice

← 01 - Markdown e i suoi dialetti · Indice · successivo → 03 - Inline - enfasi, link, escaping

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:

Foo
bar
---
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 riga
seconda 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. uno
1. due
1. 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):

```python
def 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:

> foo
bar

→ <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>.

Attenzione: Obsidian non renderizza Markdown dentro elementi HTML, mai. È una delle differenze da tenere a mente (05 - Obsidian Flavored Markdown e portabilità).

2.8 Tabulazioni

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

  1. 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.
  2. 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.
  3. Prendi una tua nota lunga e cerca liste diventate loose per una riga vuota di troppo. Sono quasi sempre involontarie.
  4. Scrivi un <details> con dentro una lista formattata: fallo funzionare.