Hecho con Chinaski

Referencia de Templates

Un theme de Chinaski es un directorio plano de templates de Template Toolkit 2 (.tt). El proceso de build renderiza tus templates a HTML estático para cada página y post, una vez por idioma configurado. Esta página cubre los archivos del theme, las variables que reciben, y los helpers y reglas que vienen con ellos.

Dónde viven los themes

  • El theme default integrado viene con la aplicación y sirve como fallback.
  • Los themes custom viven en builder/themes/<your-theme-slug>/. Cuando un theme con el slug del theme activo existe allí, el builder lo usa; si no, cae a los templates integrados.
  • Cuál theme está activo se elige en Settings.

Archivos de template requeridos y opcionales

Archivo Propósito ¿Requerido?
page.tt Template por defecto para cada página publicada Yes
post.tt Template para cada post de blog publicado Yes
blog_index.tt Listado de blog paginado y páginas de categoría Yes
404.tt Página 404 por idioma Yes
_head.tt Partial de <head>, CSS, meta tags, imports de fuentes Convention
_header.tt Header del site, logo, nav, tagline Convention
_footer.tt Footer del site, copyright, nav secundaria Convention
_cookie_notice.tt Banner GDPR opcional No
_sidebar.tt Partial de sidebar opcional No
search.tt Página de resultados de búsqueda opcional No
_password_gate.tt UI de password-gate del lado cliente para páginas protegidas Requerido si page.tt/post.tt soporta password_hash
<custom>.tt Cualquier página que lo seleccione vía la setting template No

Los archivos que empiezan con _ son partials y nunca se renderizan como páginas independientes.

Variables de template

Los templates reciben un conjunto plano de variables, no hay objetos page. ni site.. Usa site_name, title, lang, etc. directamente.

page.tt y 404.tt

Variable Tipo Notas
title string Título de la página
body HTML string Pre-renderizado, no lo escapes con | html
slug string URL slug
lang / language string Código de 2 letras, p. ej. en
meta_title string? Sobrescribe <title>
meta_description string? Descripción SEO
og_title string? Título OpenGraph
og_description string? Descripción OG
og_image_url string? Imagen OG
canonical_url string Auto-generada
featured_image_url string? Imagen hero
featured_image_alt string? Texto alt
updated_at string datetime de SQLite
site_name string De las settings
site_tagline string De las settings
footer_copyright string De las settings
year integer Año del build
url_base string Prefijo de URL, puede estar vacío; antepón a TODOS los links internos
cookie_notice_active 0/1 Si renderizar _cookie_notice.tt
noindex 0/1 Emitir <meta name="robots" content="noindex">
nav_items array {label, url, slot, sort_order}, slot es header o footer
hreflang_links array {hreflang, href} para traducciones
breadcrumbs array {name, url, is_current}
json_ld HTML string JSON-LD pre-serializado, emítelo raw
ga4_id string? ID de Google Analytics
gtm_id string? ID de contenedor GTM
twitter_site string? Handle de Twitter con @
password_hash string? Si está establecido, DEBES envolver el contenido en _password_gate.tt (ver abajo)

post.tt, todo lo de page.tt, más

Variable Tipo Notas
excerpt string Excerpt suministrado por el author; puede estar vacío
category string? Nombre de una sola categoría
tags string Separadas por comas; dividir con tags.split(',')
author string Nombre para mostrar
is_sticky 0/1 Fijado a la parte superior del índice del blog
published_at string? datetime de SQLite; momento del primer publish
date string Porción YYYY-MM-DD de published_at
feed_url string URL absoluta del feed RSS
og_type string Siempre 'article'

blog_index.tt

Variable Tipo Notas
posts array Hashes de post ordenados con keys p.title, p.slug, p.date, p.body, p.category
pagination hash? {current_page, total_pages, prev_url, next_url, abs_prev_url, abs_next_url}, undef cuando solo hay una página
title string "Blog" localizado o nombre de la categoría
category_name string? Establecido al filtrar por categoría

search.tt

Variable Tipo Notas
query string Término de búsqueda
results array {title, url, excerpt}
result_count integer Total de coincidencias

Blocks

Los authors colocan blocks escribiendo {{block:my-cta}} en un body de página o post. Al momento del build, el builder encuentra el shortcode, busca el block publicado con ese nombre e idioma, lo renderiza con su template y sustituye el HTML renderizado en el body.

Los block templates se comparten entre themes y viven fuera de tu directorio de theme; tu theme es responsable del CSS que estiliza su output.

Clases CSS que tu theme debe estilar

Cada tipo de block emite una clase wrapper predecible y clases internas. Tu _head.tt debe estilizar estas:

Tipo Wrapper Clases internas
cta .cta-block .cta-inner .cta-heading .cta-subheading .cta-button
image .image-block .image-caption
quote .quote-block .quote-text .quote-attribution
hero .hero-block .hero-media .hero-inner .hero-heading .hero-subheading .hero-button
testimonial .testimonial-block .testimonial-label .testimonial-body .testimonial-avatar .testimonial-text .testimonial-attribution
stat .stat-block .stat-number .stat-label
promo .promo-block .promo-media .promo-content .promo-heading .promo-body .promo-button
embed .embed-block .embed-caption .embed-content
video .video-block .video-heading .video-wrapper .video-caption
form .form-block .form-heading .form-subheading .block-form .block-form__field
newsletter .newsletter-block .newsletter-inner .newsletter-heading .newsletter-subheading .newsletter-form .newsletter-form__row
accordion .accordion-block .accordion-heading .accordion .accordion__item .accordion__summary .accordion__body
table .table-block .table-heading .table-wrapper .block-table
columns .columns-block .columns-heading .columns .columns--2 .columns--3 .columns--4 .column .column__image .column__heading .column__body
gallery .gallery-block .gallery-heading .gallery .gallery__item

Estilización mínima requerida para las clases de utilidad:

  • .video-wrapper: position: relative; padding-bottom: 56.25%; con el iframe position: absolute; inset: 0; width: 100%; height: 100%; (video responsivo 16:9)
  • .columns--2 / .columns--3 / .columns--4: display: grid con el grid-template-columns apropiado
  • .table-wrapper: overflow-x: auto para que las tablas hagan scroll en celular
  • .gallery: grid responsivo con gap
  • .accordion__item / .accordion__summary: reset básico de details/summary

Páginas multi-idioma

  • Cada página se renderiza una vez por idioma configurado.
  • Los links internos deben incluir el prefijo de idioma: usa [% url_base %]/[% lang %]/contact/ o similar. La única excepción es nav_items[].url, que ya contiene la URL completa correcta, úsala tal cual.
  • El elemento <html> debe cargar el idioma: <html lang="[% lang | html %]">.

Reglas de seguridad

Las siguientes directivas están prohibidas en el contenido de themes y blocks y son rechazadas:

  • [% PERL %] / [% RAWPERL %]
  • [% INSERT '/absolute/path' %]
  • [% USE ... %] de módulos como File/IO/Socket/Net/LWP/WWW/HTTP/System/Cmd/Process/Pipe/Expect

Páginas protegidas por contraseña

La protección por contraseña es una puerta orientada al lector: el contenido de la página sigue presente en el HTML publicado, y _password_gate.tt lo esconde detrás de una verificación de JavaScript hasta que el visitante introduce la contraseña correcta. page.tt y post.tt deben implementarlo así:

[% IF password_hash %]
[% WRAPPER '_password_gate.tt' %][% PROCESS page_content %][% END %]
[% ELSE %]
[% PROCESS page_content %]
[% END %]

(Envuelve el block que contenga tu contenido principal. El nombre exacto del block no importa, solo que esté definido y se procese dentro del wrapper.) Si tu template omite por completo el branch de password_hash, un administrador puede aún establecer una contraseña en una página usando tu theme, y la página renderizará públicamente sin ninguna advertencia de que la puerta falta.

Hoja de referencia rápida

  • [% variable %] : output raw
  • [% variable | html %] : escapado en HTML (usa para TODO el texto controlado por el usuario)
  • [% variable | uri %] : codificación URI (para segmentos de path de URL)
  • [% IF / ELSE / END %], [% UNLESS ... %] : condicionales
  • [% FOREACH item IN items %] : loops; loop.index / loop.count disponibles
  • [% INCLUDE '_partial.tt' %] : incluye un partial, compartiendo las variables actuales; añade x = 42 para pasar o ensombrecer valores
  • [% PROCESS '_macros.tt' %] : como INCLUDE pero sin introducir un nuevo scope
  • [%- ... -%] : recorta el whitespace circundante

Gotchas clave

  1. Stash plano: sin objetos anidados. site.title, page.title, post.title están todos mal; usa site_name y title.
  2. Los campos de nav item son label, url, slot : no name, href, active.
  3. Fechas: nunca llames a formateo de fechas sobre published_at; usa la variable pre-calculada date, o toma los primeros 10 caracteres de published_at.
  4. El body es HTML pre-renderizado: emite [% body %] sin escapar.
  5. Los partials tienen prefijo _.
  6. Todos los links internos necesitan url_base y lang.
Lee este sitio en