4 Ruta ExpertoLección 3 de 8 8 min de lectura

Personalizar las plantillas del tema

  1. 1. La arquitectura de Éclair
  2. 2. Desarrollo local y pruebas
  3. 3. Personalizar las plantillas del tema
  4. 4. Nginx, VPS, proxy y CDN: desplegar Éclair
  5. 5. Rendimiento de Éclair: mantener 100 sobre 100
  6. 6. Migrar desde WordPress o Wix
  7. 7. Seguridad avanzada y mantenimiento
  8. 8. Industrializar Éclair en una agencia
Índice
  1. ¿CSS personalizado o plantillas del tema?
  2. Los archivos de app/theme
  3. ¿Cómo recibe una plantilla sus datos?
  4. Las restricciones de la CSP
  5. Rendimiento de las plantillas
  6. Actualizaciones, integridad y manifest.php
  7. El ejemplo de esta academia
  8. Para recordar
  9. Para saber más
  10. Preguntas frecuentes

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.

CriterioCSS personalizadoPlantillas del tema
DóndeAjustes › Apariencia › CSS personalizadoarchivos de app/theme/
Se guarda endata/custom-css.phpapp/, sustituido en cada actualización
Tras una actualizaciónse conservahay que volver a aplicarlas
Control de integridadnada que señalararchivos «modificados» o «desconocidos»
Límites64 KB, sin < ni @importHTML libre, siempre que respete la CSP
Ideal paracolores, espaciados, estilo de un bloqueestructura del HTML, elementos nuevos

Los archivos de app/theme

ArchivoFunciónVariables principales de $v
layout.phpesqueleto común: <head>, cabecera, menús, pie de páginalang, head, css, content, current, id, class
single.phppágina o artículoitem, html, toc, author, category, crumbs, related, translations, featured, words, h1_in_content
list.phpblog, categoría, autor, búsqueda, catálogotitle, description, entries, crumbs, page, pages, prev, next, query, empty, chips
cards.php, products.phpcuadrículas de tarjetas de artículos y de productosentries, lang, level
product.phpficha de productoitem, entry, html, images, offers, crumbs, related
404.phppágina no encontradalang
theme.cssestilos de base, insertados en cada páginamarcadores /*dark*/ y /*shop*/
themes/*.csslos 5 temas opcionalesse añaden después de theme.css
directions.cssfragmentos de las direcciones de artesolo 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.

HerramientaUso 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:

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.

  1. Guarda tus plantillas modificadas en un repositorio Git, fuera del sitio en línea.
  2. 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.
  3. Lleva esos cambios a tus archivos, prueba en local y vuelve a subirlos.
  4. 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).

ArchivoFunción
app/theme/academy.phpClase Academy: idioma de una dirección, filtrado de los menús, iconos, rutas y posición de una lección
app/theme/layout.phpCabecera con selector de idioma, menú móvil a pantalla completa, pie de página con las cuatro rutas
app/theme/single.phpCabecera 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.phpBúsqueda y página no encontrada en el idioma solicitado
assets/site/academy.jsBotón «Copiar» de los bloques de código e indicador de lectura del índice (carga diferida)

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

Para saber má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.

Todas las lecciones de la ruta