Cómo mantengo una cabecera y un pie únicos en una web estática sin generador de sitios: marcadores HTML, un script idempotente y un modo comprobación.
El problema: la misma cabecera copiada en decenas de páginas
Esta web es HTML estático servido por GitHub Pages: páginas de servicios, legales, utilidades y el blog. Cada página llevaba su propia copia de la cabecera y del pie. Mientras eran pocas no importaba. Cuando pasaron de cincuenta, cada cambio de menú era una operación de riesgo.
El historial lo deja claro. Un cambio de navegación en julio tocó 21 páginas; el banner de cookies, a finales de julio, 26; el pie legal, en agosto, 14. Cada uno a base de edición en bloque, con su copia de seguridad por fichero, y con el riesgo de que una página con un espacio distinto o un enlace ya cambiado a mano se quedara con la versión vieja sin que nadie lo viera.
La opción evidente, y por qué no encaja aquí
La respuesta de manual es un generador de sitios estáticos: plantillas, includes y una fase de build. GitHub Pages incluso trae uno integrado. No encaja aquí por tres motivos concretos:
- Lo que hay en el repositorio es lo que se sirve. Sin build, el HTML que ves en el repo es el que ve Google. No hay un paso intermedio que pueda fallar ni una salida que revisar aparte.
- Un bot escribe en este repositorio. El blog lo publica un sistema automático que sube HTML ya terminado por la API de GitHub. Con un generador de por medio, ese flujo tendría que pasar por el build o aprender su formato.
- Migrar decenas de páginas a layouts era reescribirlas todas para resolver un problema que solo afecta a dos o tres bloques de cada una.
Lo que hice: huecos marcados y un script que los rellena
Cada página declara dónde va un bloque compartido con dos comentarios HTML, uno de apertura y otro de cierre, con el nombre del bloque. El bloque canónico vive una sola vez en una carpeta de parciales. Un script recorre las páginas y sustituye lo que hay entre los marcadores por la versión canónica. Lo que hay entre medias es una copia generada, y así lo trata todo el mundo.
El script está pensado para no poder romper una página:
- Solo toca lo que hay entre marcadores. Antes de escribir, comprueba que el resto del fichero queda byte a byte igual. Si no, no escribe.
- Es idempotente. Una segunda pasada no cambia nada.
- Los errores no escriben a medias. Un marcador sin cerrar, un bloque anidado o un parcial que no existe dejan esa página intacta y hacen que el script termine con error.
- Tiene modo comprobación. Con una opción no escribe nada: sale con error si alguna página está desincronizada. Es el paso previo a cada commit que toca páginas.
- Admite extras. La portada lleva un pie con un bloque adicional; se declara en el propio marcador y el parcial tiene un hueco para él. Sin plantillas condicionales.
Lo que ha dado de sí
La primera pasada convirtió 57 páginas. Hoy son 87, porque todo lo nuevo nace con los marcadores puestos. Añadir la insignia de la app de Google Play a las 29 utilidades fue crear un parcial y poner su marcador: un cambio revisable en lugar de 29 ediciones sueltas.
El caso que más me gusta es el del blog. La plantilla con la que el bot crea cada página de post también lleva los marcadores, así que el script la mantiene al día como a cualquier otra página. Si mañana cambio el menú, los posts que publique el bot a partir de ese momento salen con el menú nuevo sin tocar el bot.
Y un detalle de GitHub Pages que acabó jugando a favor: aunque no uso su generador, Pages pasa Jekyll por encima del repositorio, y Jekyll no publica los ficheros cuyo nombre empieza por guion bajo. La plantilla de posts se llama así a propósito: está en el repositorio, el script la sincroniza, el bot la lee por la API y, si alguien pide su URL, recibe un 404. Lo comprobé en producción, no lo di por supuesto.
Lo que cuesta
- Bytes repetidos. Cada página sigue llevando su copia de la cabecera. En páginas de este tamaño no se nota, pero existe.
- Disciplina en lugar de build. Si cambias un parcial y no ejecutas el script, las páginas se quedan con la versión anterior. No hay integración continua que lo impida; lo que hay es el modo comprobación, que lo detecta antes del commit si lo usas. Es una red que depende de un hábito.
- No es un sistema de plantillas. Sirve para bloques idénticos en muchas páginas. Si algún día necesito lógica (menús que cambian por sección, variables), este enfoque se queda corto y tocará replantearlo.
Si tu web tiene este mismo problema, o quieres una que no lo tenga desde el principio, es lo que hago en webs personalizadas.
Daniel · Arquitecto Digital. Los sistemas los diseño y dirijo yo; la implementación la hago con asistentes de IA de programación.