Ir al contenido
Código en GitHub (se abre en una pestaña nueva)

Documentación · Referencia · El formato de un proyecto

8. Markdown

El Markdown, las figuras, los enlaces externos, los encabezados y los atajos.

Versión 0.16

CommonMark, más tablas, notas al pie, tachado (~~texto~~) y enlaces automáticos: una dirección escrita tal cual (https://ejemplo.com, www.ejemplo.com) se convierte en enlace, y también una de correo (info@ejemplo.com pasa a mailto:) o una ftp://, que es un enlace externo. Se admite HTML escrito a mano. Las etiquetas vacías salen como en HTML5, sin barra final: <img …>, <br>.

También los bloques de código sangrados de CommonMark: un párrafo que empieza con cuatro espacios, o un tabulador, tras una línea en blanco, es código. Eleventy los desactiva, así que al migrar desde él se quita la sangría que no quería decir código.

Cerrado: el Markdown NO pasa por Twig. En su lugar hay atajos, con un vocabulario cerrado (§8.1).

Dos añadidos forman parte del contrato:

  • Figuras. Una imagen sola en su párrafo, enlazada o no, es una figura, y se construye igual que el atajo imagen (§8.2). Su texto alternativo da a la vez el alt de la imagen, en texto plano, y el pie, con su Markdown convertido: ![El **escaparate** en otoño](/img/escaparate.jpg) lleva alt="El escaparate en otoño" y el pie El <strong>escaparate</strong> en otoño. Los enlaces dentro del texto alternativo no llegan al pie (sale su texto), y el título entre comillas de la imagen en Markdown no se usa. Si la dirección empieza por / y es una imagen de publico/, lleva también su ancho y su alto, como el atajo, y, si el sitio declara imagenes (§2) y la imagen tiene variantes, su srcset (§8.2). Un sitio que quiera otro marcado para sus figuras (una clase, un loading="lazy") lo da con su propia plantilla imagen.
  • Enlaces externos. Un enlace escrito en Markdown a otro dominio recibe target="_blank" y rel="noopener". El dominio del sitio es el de url en sitio.yml, con y sin www.: un enlace es a otro dominio si su dirección empieza por http://, https:// u otro esquema con dominio, o por //, y ese dominio no es el del sitio, sin distinguir mayúsculas (un subdominio como blog.ejemplo.com sí es otro). Una ruta, un ancla, un mailto: o un tel: no lo son, y un enlace con un esquema inseguro (javascript:, vbscript:, file:, y data: salvo en las imágenes) sale sin dirección. El HTML escrito a mano y los enlaces de las plantillas no se tocan. La sección enlacesExternos de sitio.yml (§2) añade noreferrer y un aviso para los lectores de pantalla. Una imagen enlazada a otro dominio, sea una figura o el atajo imagen con enlace (§8.2), lleva también target y rel, aunque no el aviso, que un enlace que solo envuelve una imagen no tiene (decisión 131).

Y un añadido opcional, que solo existe si el sitio lo pide con la sección encabezados de sitio.yml (§2):

  • Encabezados con id. Los encabezados del Markdown de los niveles que se indiquen reciben un id: el slug (§5.2) de su texto plano, sin enlaces ni énfasis, y sin el texto alternativo de las imágenes ni los atajos en línea (## Con ![foto](/a.png) imagen da con-imagen); el código en línea sí cuenta. Si ya hay otro igual en la página, se le añade -2, -3… hasta el primer número libre; y si el slug sale vacío, el id es seccion. Con enlace, el encabezado termina en un enlace permanente, pegado a su texto: <h2 id="un-apartado">Un apartado<a class="ancla" href="#un-apartado" aria-label="Enlace a «Un apartado»">¶</a></h2>. Solo cuentan los encabezados que escribe el Markdown del cuerpo: no los del HTML escrito a mano, ni los de dentro de un atajo que envuelve contenido (§8.1), ni los que pone una plantilla, que decide los suyos. Las plantillas leen la lista en pagina.encabezados (§7.2).

8.1 Atajos

Un atajo es una llamada con nombre y atributos con nombre dentro del Markdown:

[imagen fichero="img/escaparate.jpg" alt="Escaparate de la librería" pie="La tienda en otoño"]

Y con contenido, cuando lo envuelve:

[aviso tipo="importante"]
Las plazas son limitadas.
[/aviso]

Reglas:

  • Solo se reconocen los nombres registrados: los que trae el motor y los que define el sitio. Un nombre que no está registrado se deja como texto; si lleva atributos, o es un cierre sin su apertura, además se avisa. Nunca se rompe el build. Un [algo] sin atributos que no está registrado es texto normal; uno registrado es siempre una llamada, lleve atributos o no (en «Adjunta el [archivo] firmado», [archivo] es el atajo y avisa de que falta fichero; para escribirlo como texto, \[archivo]). Un atajo registrado cuyos atributos no se pueden leer (sin comillas, por ejemplo) se deja como texto y se avisa (decisión 147). [imagen](…), [imagen][ref] y [imagen]: … siguen siendo enlaces de Markdown.
  • Los atributos son siempre cadenas con nombre, entre comillas dobles o simples: nombre="valor" o nombre='valor'. No hay expresiones, ni variables, ni condicionales, ni acceso a colecciones: eso es lógica y la lógica vive en las plantillas.
  • El nombre de un atajo empieza por una letra minúscula y lleva minúsculas, números y guiones; el de un atributo empieza por una letra y lleva letras y números, sin guiones ni guiones bajos (pie, siFalta). El valor termina en la misma comilla con que empieza y no admite escape: uno que lleve comillas dobles y simples a la vez no se puede escribir. Se admiten espacios alrededor del =; si un atributo se repite, gana el último; y la apertura de un atajo que envuelve puede llevar hasta tres espacios de sangría.
  • Un atajo simple puede ir en medio del texto. Si va solo en su párrafo y lo que produce es un bloque (una figura, un div, un vídeo…), sustituye al párrafo entero, porque un bloque no cabe dentro de un <p>. Si produce texto, como dato, se queda dentro del párrafo. Es un bloque lo que empieza por una de estas etiquetas: address, article, aside, audio, blockquote, details, div, dl, fieldset, figure, footer, form, h1 a h6, header, hr, iframe, nav, ol, p, picture, pre, section, table, ul y video. Con cualquier otra (main, dialog…) el atajo se queda dentro del <p>. Si el párrafo lleva más texto, o dos atajos en líneas seguidas, el bloque queda dentro del <p> y el HTML no es válido: conviene ponerlo en su propio párrafo. Un atajo que no inserta nada (porque avisa) deja un <p></p> vacío.
  • Un atajo que envuelve contenido lleva la apertura y el cierre cada uno en su línea. Lo que hay entre ellos es Markdown y llega convertido a HTML. Un atajo envuelve si en algún sitio de la página hay una línea [/nombre]; si no hay ninguna, [nombre] es un atajo simple, sin contenido. Si hay cierres pero a una apertura le falta el suyo, se avisa y el atajo se cierra al final del texto. El primer [/nombre] cierra la apertura más externa: dos atajos del mismo nombre no se anidan (con nombres distintos, sí).
  • Los atajos se reconocen al convertir el Markdown, así que uno escrito dentro de código, o empezado con \[, se queda como texto. Su HTML se inserta después, sin que el convertidor lo toque.
  • Los atajos son cosa de quien desarrolla el sitio. El esquema (§11) no los declara: un editor no sabe qué atajos hay ni qué atributos llevan.

Cada atajo es una plantilla de Twig. Los del sitio están en parciales/atajos/, con el nombre del atajo: parciales/atajos/aviso.twig. El nombre solo lleva minúsculas sin tildes, números y guiones. La plantilla recibe:

  • sus atributos, como variables: {{ tipo }};
  • contenido, el HTML de lo que envuelve, si envuelve algo;
  • sitio, datos y pagina, igual que cualquier plantilla (§7.2).

Por eso sitio, datos, pagina y contenido no se pueden usar como nombres de atributo: se avisa y se ignoran. Y un atributo con el nombre de una variable que el motor calcula para ese atajo (§8.2: src, alt, ancho, href, peso, valor…) no llega a la plantilla: gana la del motor.

8.2 Atajos incluidos en el motor

Un sitio puede sustituir cualquiera de ellos con su propia plantilla del mismo nombre en parciales/atajos/, que recibe las mismas variables. Un fichero que sale de publico/ (con .. o barras invertidas) se avisa y no inserta nada.

  • imagen: [imagen fichero="img/escaparate.jpg" alt="…" pie="…"], con fichero dentro de publico/. Da <figure> con la imagen y, si hay pie, <figcaption>. Sin pie, el pie es el alt; con pie="", no hay pie. El pie del atajo es texto, que se escapa; el de una figura del Markdown (§8) se convierte como Markdown. enlace hace la imagen enlazada. La imagen lleva width y height cuando se pueden leer del fichero (§7.3, dimensiones()), para que el navegador le reserve el sitio antes de cargarla. La plantilla recibe src, alt, pie (ya en HTML), enlace, externo (si el enlace lleva a otro dominio, como los enlaces del Markdown de §8; sin enlace, falso), rel (el de ese enlace, noopener y, si el sitio lo pide, noreferrer; solo si externo), ancho y alto (null si no se pueden leer). La plantilla de Ehundu pone target="_blank" y rel en un enlace externo; la del sitio que no use externo ni rel queda como la escribió. Se avisa si falta alt o si el fichero no existe; sin fichero no se inserta nada.

    Si el sitio declara imagenes (§2), el atajo busca las variantes de la imagen (variantes(), §7.3) y, si las hay, la plantilla recibe además srcset, sizes y fuentes, y src, ancho y alto son los de la variante más ancha del último formato; la plantilla del motor escribe <picture> si hay fuentes y un <img srcset> si no. sizes es el del atributo sizes="…" del atajo o, si no lo lleva, el de la sección. Una imagen sin ninguna variante sale como sin imagenes y sin aviso: no todas se procesan (un logotipo, una imagen pequeña). Si tiene unas variantes y le faltan otras, se avisa de cada fichero que falta. Una imagen con variantes no necesita el fichero que da nombre a su tronco: puede que en publico/ solo estén ellas, y entonces no se avisa de que no existe. Las imágenes que no son de publico/, o que son SVG, no se tocan.

  • video: [video url="https://www.youtube.com/watch?v=…" titulo="…"] admite YouTube, que se inserta desde youtube-nocookie.com para no poner cookies hasta que se reproduce, y Vimeo, con dnt=1. Con fichero="video/visita.mp4" inserta un vídeo de publico/. La proporción es 16:9 salvo que se indique otra con proporcion="4:3", y se aplica con aspect-ratio (a la plantilla llega como 16 / 9). Para YouTube valen las formas watch?v=, embed/, shorts/ y youtu.be/; si hay url y fichero, gana url. titulo es el nombre accesible del vídeo (Vídeo si falta). Con otra dirección, se avisa y no se inserta nada. La plantilla recibe insercion (la dirección para el iframe) o src (la del fichero), proporcion y titulo.

  • archivo: [archivo fichero="docs/tarifas.pdf" texto="Tarifas 2025"] da <a href="/docs/tarifas.pdf">Tarifas 2025</a> (PDF, 1,2 MB), con el tipo y el peso leídos del fichero de publico/ al compilar (en KB por debajo de 1 MB; los múltiplos son de 1024 y se redondea: 1.048.000 bytes salen como «1023 KB»). tipo es la extensión en mayúsculas. Sin texto, el nombre del fichero. Si el fichero no existe, se avisa y el enlace sale sin peso. La plantilla recibe href, texto, tipo y peso.

  • dato: [dato clave="cliente.nombre"] escribe un valor de datos/, escapado. Es lo que necesitan los textos legales, que repiten el nombre, la razón social o la dirección del titular. Con siFalta="…" se escribe ese texto si el dato no existe o está vacío; sin él, se avisa y no se escribe nada. Una lista, un mapa o una fecha avisan y no se escriben; 0 y false no cuentan como vacío (se escriben 0 y no). La plantilla recibe valor.

Lo que escriben las plantillas de Ehundu va en castellano: el título Vídeo de video, el peso de archivo con coma decimal (1,2 MB), y sí o no en dato si el valor es un booleano. peso llega a la plantilla ya como texto, así que una plantilla propia no puede darle otro formato; un sitio en otro idioma sustituye el atajo con la suya (decisión 136).

Ver este documento en GitHub (se abre en una pestaña nueva)