- Inicio
- Rutas de aprendizaje
- Ruta Experto
- Personalizar las plantillas del tema
Personalizar las plantillas del tema
- 1. La arquitectura de Éclair
- 2. Desarrollo local y pruebas
- 3. Personalizar las plantillas del tema
- 4. Nginx, VPS, proxy y CDN: desplegar Éclair
- 5. Rendimiento de Éclair: mantener 100 sobre 100
- 6. Migrar desde WordPress o Wix
- 7. Seguridad avanzada y mantenimiento
- 8. Industrializar Éclair en una agencia
Índice
Índice
Las plantillas del tema deciden el HTML de cada página pública: cabecera, menús, artículo, listas, ficha de producto. Modificarlas te abre todo lo que el CSS personalizado no permite, a cambio de un mantenimiento en cada actualización. Esta lección detalla los archivos de app/theme, las variables que reciben, las restricciones de seguridad y el ejemplo de esta academia.
Lo que sabrás hacer
Elegir el nivel adecuado
CSS personalizado, hoja del tema o plantilla PHP.
Modificar una plantilla
Con las variables de $v y las funciones de Éclair.
Seguir siendo compatible
CSP estricta, actualizaciones y control de integridad.
¿CSS personalizado o plantillas del tema?
Empieza siempre por la solución más ligera. El CSS personalizado cubre la gran mayoría de los retoques, y el <body> lleva clases que permiten apuntar a un contexto sin tocar el PHP: theme-<nom>, hdr-center, cards-list y, según la página, is-post, is-page, is-wide, is-list, is-shop, is-product, is-search o is-404.
| Criterio | CSS personalizado | Plantillas del tema |
|---|---|---|
| Dónde | Ajustes › Apariencia › CSS personalizado | archivos de app/theme/ |
| Se guarda en | data/custom-css.php | app/, sustituido en cada actualización |
| Tras una actualización | se conserva | hay que volver a aplicarlas |
| Control de integridad | nada que señalar | archivos «modificados» o «desconocidos» |
| Límites | 64 KB, sin < ni @import | HTML libre, siempre que respete la CSP |
| Ideal para | colores, espaciados, estilo de un bloque | estructura del HTML, elementos nuevos |
Los archivos de app/theme
| Archivo | Función | Variables principales de $v |
|---|---|---|
layout.php | esqueleto común: <head>, cabecera, menús, pie de página | lang, head, css, content, current, id, class |
single.php | página o artículo | item, html, toc, author, category, crumbs, related, translations, featured, words, h1_in_content |
list.php | blog, categoría, autor, búsqueda, catálogo | title, description, entries, crumbs, page, pages, prev, next, query, empty, chips |
cards.php, products.php | cuadrículas de tarjetas de artículos y de productos | entries, lang, level |
product.php | ficha de producto | item, entry, html, images, offers, crumbs, related |
404.php | página no encontrada | lang |
theme.css | estilos de base, insertados en cada página | marcadores /*dark*/ y /*shop*/ |
themes/*.css | los 5 temas opcionales | se añaden después de theme.css |
directions.css | fragmentos de las direcciones de arte | solo los de la dirección elegida |
Las variables CSS de theme.css (--bg, --bg-soft, --card, --text, --muted, --line, --radius, --shadow, --text-w, --wide-w) y las que se calculan a partir de los ajustes (--accent, --on-accent, --link, --font, --font-head) siguen siendo tu mejor punto de apoyo: úsalas en tus plantillas para seguir el tema, el modo oscuro y el color elegido.
¿Cómo recibe una plantilla sus datos?
Theme::render($vue, $v) ejecuta primero la plantilla de la vista (single, list…), coloca su HTML en $v['content'] y después ejecuta layout.php con el mismo array. Así, el esqueleto ve todas las variables de la página: $v['item'] o $v['translations'] están disponibles en la cabecera de un artículo. En una página en francés, Theme::typography() añade al final los espacios de no separación.
Theme::partial($nom, $v) incluye app/theme/<nom>.php dentro de una función aislada: solo $v es visible ahí. Así puedes dividir tu tema en archivos pequeños (Theme::partial('lang-switch', $v)), sabiendo que cada archivo nuevo aparecerá entre los «Archivos desconocidos» del control de integridad.
| Herramienta | Uso en una plantilla |
|---|---|
e($valeur) | escapar cualquier texto o atributo, sin excepción |
path_to('/chemin') | dirección interna, compatible con una subcarpeta |
asset('site/site.js') | dirección de un archivo estático con su huella de versión |
Theme::t('clé', $lang) | textos del tema en seis idiomas (read_in, toc, menu…) |
Menus::items('main') | enlaces del menú: label, href, path y external |
Media::get(), Media::picture() | imagen de la biblioteca de medios, en WebP adaptable |
Content::path($entree) | dirección pública de un contenido |
Ejemplo: mostrar las traducciones de un artículo en la cabecera, a partir de los enlaces creados en el editor.
<?php // layout.php, dentro de <header> (añade «use Eclair\Content;» al principio del archivo) ?>
<?php if (!empty($v['translations'])): ?>
<nav class="lang-switch" aria-label="<?= e(Theme::t('read_in', $lang)) ?>">
<?php foreach ($v['translations'] as $code => $t): ?>
<a href="<?= e(path_to(Content::path($t))) ?>" hreflang="<?= e($code) ?>" lang="<?= e($code) ?>"<?= $t['id'] === ($v['id'] ?? '') ? ' aria-current="true"' : '' ?>><?= e(strtoupper($code)) ?></a>
<?php endforeach ?>
</nav>
<?php endif ?>
El estilo de .lang-switch va en el CSS personalizado o en theme.css, nunca en un atributo style.
Las restricciones de la CSP
Las páginas públicas se sirven con una Content-Security-Policy estricta, calculada por Security::headers(). Se aplica a tus plantillas igual que al resto:
- Scripts:
script-src 'self'. Ningún<script>en línea, ningún atributoonclick, ningún enlacejavascript:. Un archivo.jsservido por tu dominio sigue estando permitido. - Estilos:
style-src 'self'más la huellasha256del CSS de Éclair. La única etiqueta<style>aceptada es la delayout.php, que rellenaTheme::css(). Un segundo bloque<style>o un atributostyle=""quedan bloqueados. - Tipografías y recursos:
default-src 'self'. Una tipografía cargada desde un servicio de terceros se rechaza: alójala en tu dominio, como hacen las direcciones de arte enmedia/fonts/. - Marcos: solo se permiten los reproductores
youtube-nocookie.comyplayer.vimeo.com.
Rendimiento de las plantillas
En una página guardada en caché, una plantilla solo se ejecuta en la primera visualización: lo demás lo sirve la caché estática. Eso no es motivo para hacerla más pesada. Lee las listas en el índice (Content::index(), Content::published()) en lugar de abrir cada contenido, usa Media::picture() para las imágenes (dimensiones, srcset WebP, carga diferida) y prefiere los elementos HTML nativos, <details> o el atributo popover, al JavaScript. Cada KB que añades a theme.css se envía con cada página, ya que el CSS se inserta en el HTML.
Conserva también las llamadas a Blocks::ea() de single.php (atributos data-ef): solo aparecen en la vista previa de la administración y permiten que el modo Visual edite ahí mismo el título, la imagen destacada y «Lo esencial».
Actualizaciones, integridad y manifest.php
El procedimiento de actualización borra app/ y lo sustituye: tus plantillas desaparecen con él. Organízate desde el primer día.
- Guarda tus plantillas modificadas en un repositorio Git, fuera del sitio en línea.
- Después de cada actualización, compara las plantillas originales de la versión antigua y de la nueva para detectar una variable añadida o una corrección.
- Lleva esos cambios a tus archivos, prueba en local y vuelve a subirlos.
- Lanza Seguridad › Integridad de los archivos › Analizar ahora: solo tus archivos deben aparecer entre los modificados o los desconocidos.
# Lo que ha cambiado en las plantillas originales entre dos versiones
diff -ru eclair-1.7.0/eclair/app/theme eclair-1.8.0/eclair/app/theme
El control de integridad compara cada archivo con su huella en app/manifest.php. Una lista que coincide con tus cambios es normal; un archivo inesperado merece una investigación. Para un parque de sitios, una variante más robusta consiste en mantener tu propia copia del repositorio y generar tu archivo de distribución con php tools/build.php: su manifiesto lleva las huellas de tus plantillas y el control queda limpio. Las pruebas de renderizado público de tests/run.php señalarán entonces tus cambios: es lo esperado, porque comparan con el renderizado original.
El ejemplo de esta academia
Éclair Academy está construida con Éclair 1.8 y plantillas del tema personalizadas. El tema estándar comparte los mismos menús entre todos los idiomas; nuestras plantillas añaden cuatro comportamientos, respetando las restricciones descritas más arriba (ningún script en línea, ningún atributo style, renderizado guardado en caché como siempre).
| Archivo | Función |
|---|---|
app/theme/academy.php | Clase Academy: idioma de una dirección, filtrado de los menús, iconos, rutas y posición de una lección |
app/theme/layout.php | Cabecera con selector de idioma, menú móvil a pantalla completa, pie de página con las cuatro rutas |
app/theme/single.php | Cabecera de lección (distintivo, «Lección 3 de 9», esquema de la línea), índice lateral, navegación anterior y siguiente |
app/theme/list.php y 404.php | Búsqueda y página no encontrada en el idioma solicitado |
assets/site/academy.js | Botón «Copiar» de los bloques de código e indicador de lectura del índice (carga diferida) |
- Menús filtrados por idioma: un único menú principal contiene los enlaces de los tres idiomas;
Academy::menu()solo conserva aquellos cuya dirección pertenece al idioma de la página (el francés en la raíz,/en/…y/es/…). Un emoji al principio de la etiqueta, como «🧭 Rutas», se convierte en un icono dibujado en el menú móvil. - Selector de idioma: se apoya en las traducciones enlazadas en el editor (
$v['translations']) y lleva a la misma página en el otro idioma, o a la página de inicio de ese idioma si la página no está traducida. - Menú móvil a pantalla completa sin JavaScript: un botón
popovertarget="site-menu"abre<nav id="site-menu" popover>, que el navegador gestiona por sí solo (tecla Esc, clic fuera). En pantallas grandes, el CSS vuelve a colocar el menú en la cabecera. - Navegación de lección automática:
Academy::stations()lee la primera lista numerada de la página de la ruta; el orden de sus enlaces da el número de cada lección, el esquema de la línea y los enlaces «Lección anterior» y «Lección siguiente». Para reordenar una ruta, basta con modificar esa lista en el editor.
El núcleo del filtrado cabe en unas pocas líneas:
<?php
// app/theme/academy.php (extracto)
public static function langOf(string $path): string
{
foreach (['en', 'es'] as $code) {
if ($path === '/' . $code || str_starts_with($path, '/' . $code . '/')) {
return $code;
}
}
return 'fr';
}
/** Elementos de un menú en el idioma de la página; los enlaces externos se muestran en todas partes. */
public static function menu(array $items, string $lang): array
{
$out = [];
foreach ($items as $item) {
$path = (string) ($item['path'] ?? '');
if ($path !== '' && self::langOf($path) !== $lang) {
continue;
}
[$icon, $label] = self::splitIcon((string) $item['label']);
$out[] = $item + ['icon' => $icon, 'text' => $label];
}
return $out;
}
Para que la página Seguridad siga en verde, el script de construcción de la academia vuelve a calcular la huella de estos seis archivos en app/manifest.php (los 32 primeros caracteres de su SHA-256, como el manifiesto original). Después de una actualización de Éclair, basta con volver a copiar estos archivos desde el ZIP de la academia y vaciar la caché.
Para recordar
- Prueba primero el CSS personalizado: se guarda en
data/y sobrevive a las actualizaciones. - Las plantillas del tema lo reciben todo en
$v, ylayout.phpve las mismas variables que la vista. - Escapa cada valor con
e()y construye las direcciones conpath_to(). - La CSP prohíbe los scripts y estilos en línea: el CSS va en las hojas del tema; el comportamiento, en el HTML nativo.
- Una plantilla modificada se sustituye en cada actualización: ponla bajo control de versiones y compárala con las versiones nuevas.
Para saber más
- El CSS personalizado: todo lo que se puede hacer sin tocar el PHP.
- Crear un sitio multilingüe: las traducciones enlazadas en las que se apoya el selector de idioma.
- Rendimiento: mantener 100 sobre 100: medir el efecto de tus plantillas.
- Seguridad avanzada y mantenimiento: el procedimiento de actualización y el control de integridad.
- MDN: la API Popover, para un menú sin JavaScript (en inglés).
- OWASP: hoja de referencia sobre la Content Security Policy, en inglés.
Preguntas frecuentes
¿Mis plantillas sobreviven a una actualización de Éclair?
No: la actualización sustituye toda la carpeta app/, plantillas incluidas. Guarda tus archivos en un repositorio Git y vuelve a aplicarlos después de cada versión, revisando los cambios de origen.
¿Puedo añadir mi propio archivo JavaScript?
La CSP acepta un archivo servido por tu dominio, nunca un script en línea. Elige bien su ubicación: assets/ se sustituye en cada actualización, y el control de integridad señala en media/ todo lo que no sea una imagen o una tipografía. Prueba primero una solución en HTML nativo.
¿Cómo añado un texto traducido que no existe en Theme::t?
Theme::t() devuelve la propia clave cuando no existe. Declara en tu plantilla un pequeño array de textos por idioma, indexado por $lang.
¿El modo Visual funciona con mis plantillas?
Sí, si conservas la salida de los bloques ($v['html']) y los atributos Blocks::ea() de single.php. Siguen siendo invisibles en el sitio público.