06: Frontmatter e Markdown per il web

← 05 - Obsidian Flavored Markdown e portabilità · Indice · successivo → 07 - Strumenti, stile e trappole

6.1 Che cos’è il frontmatter

Un blocco di metadati in cima al file, delimitato da ---. Non è Markdown: è YAML (per approfondire, il corso dedicato).

---
title: Configurare il server
description: Come preparare l'ambiente di sviluppo
pubDate: 2026-08-21
tags: [setup, server]
draft: false
---
 
## Prerequisiti

Chi lo supporta:

SistemaYAML ---TOML +++Note
Obsidian✓ (unico)✗è il motore delle “Properties”
Astro✓✓validato con Zod nelle content collections
Jekyll✓✗obbligatorio: senza, il file non viene processato
Hugo✓✓anche JSON
Pandoc✓✗i valori stringa vengono parsati come Markdown

Deve essere la primissima cosa del file: nemmeno una riga vuota prima.

6.2 Properties di Obsidian

Da Obsidian 1.4 il frontmatter ha un editor grafico. Il formato sul disco non è cambiato (è sempre YAML), ma ci sono regole da conoscere:

  • Sette tipi: text, list, number, checkbox, date (YYYY-MM-DD), date & time, tags. Il tipo è associato al nome della proprietà a livello di vault, non al singolo file.
  • Niente proprietà annidate. Questo è il limite che conta: uno schema con oggetti dentro oggetti non è editabile dall’interfaccia.
  • Niente Markdown nei valori.
  • I wikilink dentro le proprietà vanno fra virgolette: nota: "[[Altra nota]]".
  • Proprietà speciali: tags, aliases, cssclasses.
  • L’editor riscrive il file: normalizza il quoting, converte le liste in stile a blocchi, e può spostare o perdere i commenti. Se un file è condiviso con un altro sistema, aspettati diff spuri quando tocchi una proprietà dall’interfaccia.

6.3 Le tre trappole del frontmatter (che sono trappole YAML)

1. I valori ambigui vanno virgolettati.

paese: NO          # ← in molti parser diventa false (Norvegia → falso)
versione: 1.10     # ← diventa il numero 1.1: hai perso la patch
cap: 01234         # ← diventa 1234, oppure 668 in ottale
orario: 12:30      # ← in YAML 1.1 diventa 750

✅ verificato con PyYAML 6.0.3 e con lo schema core di YAML 1.2: sono tutti reali, e cambiano da parser a parser. Regola: qualsiasi codice, versione, identificatore o sigla va fra apici singoli.

2. Le date. pubDate: 2026-08-21 senza virgolette diventa un oggetto data in molti parser (fra cui quello che usa Astro) e una stringa in altri. In Astro con Zod, usa z.coerce.date(): funziona in entrambi i casi.

3. Il cancelletto. colore: #FF0000 produce un valore vuoto: il # preceduto da spazio apre un commento. ✅ verificato. Va scritto colore: '#FF0000'.

6.4 Astro: dalle note al sito

Il modo corretto di gestire contenuto Markdown in Astro sono le content collections: dichiari uno schema, Astro valida ogni file e ti dà tipi TypeScript.

// src/content.config.ts
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
 
const guide = defineCollection({
  loader: glob({ base: './src/content/guide', pattern: '**/[^_]*.{md,mdx}' }),
  schema: z.object({
    title: z.string(),
    description: z.string().optional(),
    pubDate: z.coerce.date(),
    draft: z.boolean().default(false),
    tags: z.array(z.string()).default([]),
  }),
});
 
export const collections = { guide };

Il valore vero: se sbagli un campo del frontmatter, la build si ferma e ti dice dove. Su venti note scritte a mano in mesi diversi, questo è ciò che tiene insieme la baracca.

Lo schema come contratto fra Obsidian e il sito

Se le stesse note vivono in Obsidian e in Astro, tieni lo schema piatto (Obsidian non gestisce proprietà annidate) e usa gli stessi nomi di campo in entrambi. Ne guadagni anche in Obsidian: le viste “Bases” lavorano proprio sulle properties.

Da sapere: layout: nel frontmatter funziona per i .md dentro src/pages/, non per le content collections (lì il layout lo decidi nella rotta).

6.5 MDX: quando serve e cosa ti costa

MDX è Markdown + JSX: puoi importare componenti e usare espressioni.

---
title: Setup
---
import Callout from '../../components/Callout.astro';
 
## Prerequisiti
 
<Callout tipo="attenzione">Serve Node 22.</Callout>

Ma MDX toglie delle cose rispetto al Markdown normale:

  • niente blocchi di codice indentati (l’indentazione serve al JSX) → solo fence;
  • niente autolink <https://esempio.it> (indistinguibili dal JSX);
  • niente commenti HTML <!-- --> → si usa {/* */};
  • < e { letterali vanno escapati (\<, \{).

Quest’ultimo punto è quello che rompe le build davvero: scrivere in prosa { "chiave": 1 } o Set<string> o ${variabile} fa fallire la compilazione. La regola pratica: se una frase contiene { o <, mettila fra backtick, dentro il codice non viene interpretato.

Il costo secondario, ma non trascurabile: un .mdx non è più leggibile in Obsidian come nota normale.

Regola d’ingaggio: .md di default, .mdx solo quando quella pagina specifica ha bisogno di un componente interattivo.

6.6 Markdoc, l’alternativa

{% callout type="warning" %}
Attenzione a questo passaggio.
{% /callout %}

Markdoc usa tag dichiarativi invece di JSX: niente import, niente esecuzione di JavaScript arbitrario nel contenuto, e i tag sono validabili con uno schema. Ha senso quando il contenuto lo scrive qualcun altro (o arriva da un CMS) e vuoi una sandbox vera. Se scrivi da solo, MDX è più diretto.

6.7 Scrivere una volta, pubblicare ovunque

La strategia sostenibile, in tre regole:

  1. Il contenuto è .md standard. Niente sintassi proprietaria nel testo.
  2. I metadati stanno nel frontmatter, con nomi coerenti e valori sempre virgolettati quando ambigui.
  3. Le trasformazioni stanno nel generatore, non nel contenuto. Wikilink, callout, componenti: li converte la pipeline, non li scrivi due volte a mano.

Il testo così resta leggibile fra dieci anni, con qualsiasi editor. Che è poi il motivo per cui hai scelto Markdown invece di un formato proprietario.

Esercizi

  1. Aggiungi un frontmatter coerente alle ultime cinque note che hai scritto, usando gli stessi nomi di campo. Poi controlla in Obsidian che le properties siano tipizzate bene.
  2. Scrivi uno schema Zod per quelle cinque note e fai fallire la build di proposito con un campo sbagliato: leggi il messaggio d’errore.
  3. Metti versione: 1.10 e codice: 007 in un frontmatter e leggili da un parser (python3 -c "import yaml;print(yaml.safe_load(open('nota.md').read().split('---')[1]))"). Guarda cosa ti torna.
  4. Prendi una nota .md e convertila in .mdx con un componente dentro. Nota cosa devi cambiare per farla compilare.