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

Documentación · Empezar

Hoja de referencia rápida

Las órdenes, la estructura de un proyecto, los campos, las funciones de las plantillas y los atajos, en una sola página.

Versión 0.16

Lo que vas a necesitar cada día, sin explicaciones largas. Cada apartado termina con el enlace al detalle en el formato.

Las órdenes

Orden Qué hace
php ehundu.phar compilar [carpeta] Compila el proyecto en su carpeta salida/. Sin carpeta, la actual.
php ehundu.phar servir [carpeta] Lo previsualiza en http://127.0.0.1:8000/ y rehace solo lo que cambia.
php ehundu.phar desplegar [carpeta] Lo compila y lo sube al destino de sitio.yml.
--puerto=8000, --borradores, --completo Opciones de servir: el puerto, enseñar también los borradores, rehacerlo todo en cada cambio.
--simular, --todo Opciones de desplegar: decir qué haría sin hacerlo, o subirlo todo aunque no haya cambiado.
--ayuda, --version La ayuda y la versión.

Con Composer, vendor/bin/ehundu en lugar de php ehundu.phar.

Un proyecto

mi-sitio/
  sitio.yml          configuración: nombre y url son obligatorios
  contenido/         páginas (.md y .twig) y ficheros que se copian tal cual
    _datos.yml       datos que heredan las páginas de la carpeta
  datos/             datos globales: datos.<nombre> en las plantillas
  plantillas/        layouts; pagina.twig es el de por defecto
  parciales/         fragmentos con {% include %}; parciales/atajos/ para los atajos
  publico/           se copia a la raíz del sitio (CSS, imágenes, favicon)
  .secretos.yml      contraseñas del despliegue; no se versiona
  salida/  .ehundu/  los genera el motor; no se versionan

Detalle: estructura del proyecto.

sitio.yml

Clave Para qué
nombre, url Obligatorios. La url es el dominio, sin ruta: https://www.ejemplo.com.
idioma, idiomas El idioma del sitio, o la lista si son varios (es por defecto).
zonaHoraria Europe/Madrid. Con ella se leen y se escriben las fechas.
feed Pide un feed.xml: feed: { coleccion: blog }.
encabezados Da un id, y si quieres un enlace permanente, a los encabezados del Markdown.
imagenes Los anchos y formatos de las variantes de imagen, para el srcset.
enlacesExternos Cómo salen los enlaces a otros dominios que escribes en Markdown.
generador generador: no quita la etiqueta generator de las páginas.
despliegue Adónde se publica (más abajo).

Detalle: sitio.yml.

El front matter de una página

Lo que escribes entre las dos líneas de guiones al principio de un .md. Estos campos tienen un significado especial; cualquier otro llega a la plantilla tal cual.

Campo Para qué
titulo, subtitulo, descripcion El título, la entradilla y la descripción para buscadores.
fecha Un día, 2026-10-15. Ordena las colecciones y sale en el feed.
url La dirección de la página, o un patrón. Si falta, sale de la ruta del fichero. false: no genera fichero.
plantilla El layout, sin carpeta ni extensión. false: sin layout.
etiquetas Las colecciones a las que pertenece: [blog, novedades].
listada no: se publica, pero no sale en colecciones ni en el sitemap.
css, js Ficheros de publico/ que necesita esta página.
imagen, imagenAlt, icono Imagen principal, su texto alternativo e icono SVG.
orden Un número, para ordenar a mano dentro de una colección.
borrador sí: no se publica.
publicar No se publica antes de esa fecha.
publicarEnSuFecha sí: no se publica antes de su fecha. Va en el _datos.yml de un blog.
pdf sí: la página sale también en PDF.
agrupar, paginar Una página por cada valor de un campo, o una lista partida en páginas (más abajo).

Los campos de sí o no admiten sí, no, true y false, pero solo los de esta tabla: en un campo tuyo, sí y no son texto, y para un verdadero o un falso se escribe true o false (más en Plantillas y atajos). Los nombres ingleses de Eleventy y Lume (title, date, tags, layout, permalink) valen como alias. Detalle: campos reservados.

Direcciones

contenido/servicios/diseno.md   →  /servicios/diseno/
contenido/servicios/index.md    →  /servicios/
contenido/404.md con url: /404.html   →  /404.html

Un patrón usa campos de la propia página y el filtro slug:

url: "/blog/{{ titulo|slug }}/"
url: "/{{ nombreDeFichero }}/"

Detalle: URL.

Datos y herencia

Un fichero de datos/ está en las plantillas con su nombre: datos/cliente.yml es datos.cliente. Un _datos.yml dentro de una carpeta de contenido/ lo heredan todas sus páginas; gana el valor más cercano al fichero, salvo etiquetas, css y js, que se suman.

# contenido/blog/_datos.yml
plantilla: articulo
etiquetas: [blog]
url: "/blog/{{ titulo|slug }}/"

Detalle: datos.

Plantillas

Las plantillas son Twig. Se nombran con su ruta desde la raíz del proyecto.

{% extends 'plantillas/base.twig' %}
{% include 'parciales/cabecera.twig' %}
{% block cuerpo %}{{ pagina.contenido }}{% endblock %}

Tres variables: sitio (lo de sitio.yml), datos (los ficheros de datos/) y pagina (los campos de la página, más pagina.url, pagina.contenido, pagina.idioma, pagina.traducciones y pagina.encabezados).

{{ coleccion('blog') }}              {# las páginas con esa etiqueta, de la más antigua a la más reciente #}
{{ lista|orden('fecha desc') }}      {# ordena por uno o varios campos: 'orden, titulo' #}
{{ lista|limite(6) }}                {# las seis primeras #}
{{ lista|invertir }}                 {# da la vuelta a la lista #}
{{ lista|sin(pagina) }}              {# quita una página, normalmente la actual #}
{{ lista|donde('etiquetas', 'x') }}  {# las que tienen ese valor en ese campo #}
{{ lista|anterior(pagina) }}         {# la página anterior, o siguiente(pagina) #}
{{ titulo|slug }}                    {# un texto convertido en un tramo de dirección #}
{{ pagina.fecha|fecha('j F Y') }}    {# una fecha con los meses en el idioma de la página #}
{{ campo|markdown }}                 {# un texto con formato, convertido a HTML #}
{{ svg('img/logo.svg') }}            {# incrusta un SVG de publico/ #}
{{ dimensiones('img/foto.jpg') }}    {# da el ancho y el alto de una imagen #}
{{ activo('/blog/') }}               {# ¿estoy en esa página o dentro de ella? #}
{{ version('css/estilo.css') }}      {# la dirección con una huella, para que se guarde en la caché #}
{{ css('css/base.css') }}            {# declara CSS; {{ css() }} lo escribe todo donde lo pongas #}
{{ js('js/app.js') }}                {# lo mismo con JavaScript; también css_fichero() y js_fichero() #}

Detalle: plantillas y colecciones.

Markdown y atajos

Markdown corriente, más tablas, notas al pie, tachado y enlaces automáticos. Un enlace a otro dominio se abre en otra pestaña. Los atajos son lo que el Markdown no tiene, sin lógica en el texto:

Atajo Qué da
[imagen fichero="img/foto.jpg" alt="…" pie="…"] Una figura con su pie y sus medidas.
[video url="https://www.youtube.com/watch?v=…" titulo="…"] Un vídeo de YouTube o Vimeo, o uno de publico/ con fichero.
[archivo fichero="docs/tarifas.pdf" texto="Tarifas"] Un enlace con el tipo y el peso del fichero.
[dato clave="cliente.nombre"] Un valor de datos/.

Los atajos que defines tú pueden envolver texto, con la apertura ([aviso tipo="importante"]) y el cierre ([/aviso]) cada uno en su línea. Para cambiar el HTML de un atajo, o crear uno, escribe parciales/atajos/<nombre>.twig. Detalle: Markdown y atajos.

Una página por cada valor, y listas partidas

# contenido/etiqueta.md: una página por cada etiqueta de los artículos
agrupar:
  coleccion: blog
  campo: etiquetas
  excluir: [blog]
url: "/etiqueta/{{ grupo|slug }}/"
# contenido/blog/index.md: el listado, de diez en diez
paginar:
  coleccion: blog
  porPagina: 10
  orden: fecha desc

En la plantilla, pagina.paginas son las páginas del grupo o del trozo, y pagina.paginacion trae actual, total, entradas, anterior, siguiente y enlaces. Detalle: agrupar y paginar.

Varios idiomas

# sitio.yml: el primero es el predeterminado
idiomas:
  - codigo: es
    nombre: Castellano
  - codigo: eu
    nombre: Euskara

El idioma va en el nombre del fichero: contacto.md está en castellano y contacto.eu.md, en euskera. Los datos siguen la misma regla (menus.eu.yml). Las plantillas tienen pagina.traducciones y sitio.idiomas para escribir el selector de idioma. Detalle: multiidioma.

Borradores y publicación programada

borrador: sí no publica la página. publicar: 2026-12-01 la publica ese día, y publicarEnSuFecha: sí hace que ninguna página de la carpeta salga antes de su fecha. Es un sitio estático: sale cuando se vuelve a compilar después de esa fecha. Con servir --borradores lo ves todo.

Despliegue

Destino Campos de despliegue
carpeta ruta: una carpeta de tu máquina, fuera del proyecto.
ftp servidor, usuario, ruta, y puerto o cifrado: no si hace falta.
sftp servidor, usuario, ruta y huella (la de la clave del servidor).
s3 servidor, region, cubo, usuario y ruta.

Los secretos van en .secretos.yml, nunca en sitio.yml: clave (o clavePrivada, la ruta de un fichero de clave) y frase. Ehundu apunta lo que sube en un fichero .ehundu.json del destino, y solo borra lo que subió él. Detalle: el despliegue.