Plantillas por dentro

Una plantilla es HTML con Liquid, SCSS y unos campos en JSON. Tú haces el molde; quien edita rellena los campos y no puede romper el diseño.

Para
Quien sabe HTML y CSS
Tiempo
Una hora
Necesitas
Rol de desarrollador o administrador
  1. Cómo encaja todo

    Una página de Layo es una pila de secciones. Cada sección es una plantilla rellenada: la plantilla pone el diseño y los campos; quien edita pone el contenido.

    PiezaQué es
    PlantillaEl molde: campos, HTML, SCSS y JS
    SecciónUna plantilla puesta en una página, con su contenido
    TemaColores, fuentes y medidas de toda la web
    BloqueUna sección guardada para usarla en varias páginas
    KitUna página entera guardada para crear otras iguales

    El servidor pinta el HTML (Liquid) y compila el SCSS; en el navegador no hay framework. Cada página carga solo el CSS y el JS de las secciones que tiene.

  2. Dónde se hacen

    En Desarrollo › Plantillas creas una nueva, clonas una de serie para cambiarla sin tocar la original, o importas un .json. El editor tiene una pestaña por pieza:

    PestañaQué va
    GeneralNombre, posición, categoría y descripción
    Campos · ContenidoJSON con lo que rellena quien edita → configuration.*
    Campos · EstilosJSON con las opciones de diseño → stylesConfiguration.*
    HTMLEl molde, con Liquid
    SCSSSu aspecto. Se compila al guardar
    JSOpcional. Se ejecuta una vez por cada sitio donde sale

    El editor de código es el de VS Code y autocompleta los campos y los filtros. Al guardar se comprueban el JSON, el Liquid, el SCSS y el JS: si algo falla, no se guarda y te dice dónde.

    El editor de plantillas de Layo, con las pestañas de campos, HTML y SCSS y la vista previa a la derecha

    TrucoSi la plantilla ya está en páginas, al guardarla el cambio sale en todas a la vez.

  3. La posición

    Dice dónde se puede usar la plantilla:

    positionPara qué
    bodySección normal de una página (la mayoría)
    moduleMódulo dentro de una columna: título, texto, botón…
    header / footerLa cabecera o el pie de toda la web
    postLa cabecera de las entradas del blog
    entryLa página de cada ficha de una colección
    product / cartLa ficha de producto y el carrito de la tienda
  4. Los campos

    Cada pestaña de campos es una lista en JSON. Cada campo lleva name, label y type, y casi siempre un default: el contenido de ejemplo al añadir la sección, que también sale en la miniatura de la biblioteca. Ponlo realista.

    Los tipos: text, textarea, wysiwyg (texto con formato), number, date, link, image, select, boolean, color, color-scheme, menu, form, collection, code, multiple (una lista con sus propios campos) y separator (un título para ordenar el formulario).

    Campos · Contenido
    [
      { "name": "title", "label": "Título", "type": "text", "default": "Por qué trabajar con nosotros" },
      { "name": "intro", "label": "Entradilla", "type": "textarea", "help": "Una o dos frases." },
      {
        "name": "items", "label": "Tarjetas", "type": "multiple", "itemName": "tarjeta", "itemLabel": "title",
        "fields": [
          { "name": "title", "label": "Título", "type": "text" },
          { "name": "text", "label": "Texto", "type": "textarea" }
        ],
        "default": [
          { "title": "Presupuesto en 24 horas", "text": "Nos cuentas qué necesitas y al día siguiente tienes el precio." },
          { "title": "Sin sorpresas", "text": "El precio que firmas es el que pagas." }
        ]
      },
      { "type": "separator", "label": "Botón (opcional)" },
      { "name": "buttonLink", "label": "Enlace del botón", "type": "link", "default": "#contacto" }
    ]

    TrucoEl name solo lleva letras, números y _, no empieza por número y no se repite. Es el nombre que usas en el HTML.

  5. Contenido o estilos

    Contenido es lo que dice la sección: textos, fotos, enlaces, listas. Estilos es cómo se ve, siempre con opciones cerradas (select y boolean): esquema de color, espaciado, columnas, lado de la foto, animar o no.

    En Estilos no van colores libres ni medidas a mano: para eso están el tema y la pestaña Diseño. Así quien edita no puede salirse de la marca. El botón «Insertar estilos estándar» añade los de casi todas:

    Campos · Estilos
    [
      { "name": "colorScheme", "label": "Esquema de color", "type": "color-scheme", "default": "theme-cs-background" },
      { "name": "spacing", "label": "Espaciado vertical", "type": "select", "default": "normal",
        "options": [{ "value": "compact", "label": "Compacto" }, { "value": "normal", "label": "Normal" }, { "value": "large", "label": "Amplio" }] },
      { "name": "animate", "label": "Animar al hacer scroll", "type": "boolean", "default": true }
    ]
  6. El HTML, con Liquid

    Layo usa Liquid, el lenguaje de plantillas de Shopify: {{ … }} pinta un valor y {% … %} es lógica. Lo de Contenido llega como configuration.* y lo de Estilos como stylesConfiguration.*.

    Normas

    • Un solo elemento raíz, con la clase de la plantilla.
    • Cada campo dentro de un {% if %}: si se deja vacío, no queda una etiqueta vacía.
    • {{ }} escapa el HTML. | raw solo con los campos wysiwyg y code, que ya llegan limpios.
    • Lo que va en atributos sale de listas cerradas (un select), nunca de un texto libre.
    • Un solo <h1> por página, en la portada. Las demás secciones empiezan por <h2>.
    HTML
    <section class="ej-ventajas {{ stylesConfiguration.colorScheme | default: 'theme-cs-background' }} space-{{ stylesConfiguration.spacing | default: 'normal' }}">
      <div class="theme-container">
        {% if configuration.title %}<h2 class="ej-ventajas__title">{{ configuration.title }}</h2>{% endif %}
        {% if configuration.items.size > 0 %}
        <ul class="ej-ventajas__list" role="list"{% if stylesConfiguration.animate %} data-fx="fade-up" data-fx-stagger{% endif %}>
          {% for item in configuration.items %}
          <li class="ej-ventajas__card">
            <span class="ej-ventajas__num" aria-hidden="true">{{ forloop.index }}</span>
            {% if item.title %}<h3>{{ item.title }}</h3>{% endif %}
            {% if item.text %}<p>{{ item.text }}</p>{% endif %}
          </li>
          {% endfor %}
        </ul>
        {% endif %}
        {% if configuration.buttonLink %}<a class="theme-btn" href="{{ configuration.buttonLink }}">Pide presupuesto</a>{% endif %}
      </div>
    </section>
  7. Lo que tiene a mano

    VariableQué es
    section.idEl id único de esta sección en la página
    page.title, page.slugLa página donde está
    site.name, site.urlLa web
    site.contactTeléfono, email, dirección y WhatsApp de Ajustes
    site.hours.rowsEl horario de Ajustes
    vars.<nombre>Las variables de Sitio › Variables
    collections.<id>.itemsLas fichas publicadas de una colección

    Además de los filtros de Liquid, Layo trae los suyos: media (una foto con sus versiones pequeñas), media_tagged (las fotos con unas etiquetas), video (YouTube y Vimeo con una portada ligera), latest_posts (las últimas entradas del blog) y t (traduce un texto fijo al idioma de la página).

    Una foto bien puesta, con su tamaño reservado y carga diferida:

    Una foto con el filtro media
    {% if configuration.image %}{% assign img = configuration.image | media %}
    <img src="{{ img.url }}"{% if img.srcset %} srcset="{{ img.srcset }}" sizes="(max-width: 640px) 100vw, 50vw"{% endif %}{% if img.width %} width="{{ img.width }}" height="{{ img.height }}"{% endif %} alt="{{ configuration.imageAlt | default: img.alt }}" loading="lazy" decoding="async">
    {% endif %}

    TrucoPara el teléfono, el email o el horario, léelos de site.contact y site.hours en vez de crear campos: así no se escriben dos veces.

  8. El SCSS y el tema

    Todo el CSS de la página va junto, así que cada plantilla usa clases con su propio prefijo (.ej-ventajas, .ej-ventajas__card). Nunca selectores sueltos como h2 {}.

    Los colores, las fuentes y las medidas salen del tema con variables: --theme-color-primary, --theme-color-on-primary, --theme-color-background, --theme-color-on-background, --theme-color-background-variant, --theme-font-heading, --theme-font-body, --theme-radius, --theme-space y --theme-container. Si se cambia el tema, la plantilla se adapta sola.

    Ya existen theme-container, los esquemas theme-cs-background, theme-cs-background-variant, theme-cs-primary y theme-cs-secondary, y los botones theme-btn y theme-btn--ghost. Cortes: 1023px para tablet y 640px para el móvil.

    SCSS
    .ej-ventajas {
      --space: 2;
      &.space-compact { --space: 1; }
      &.space-large { --space: 3; }
      padding: calc(var(--theme-space) * var(--space)) 0;
    
      &__title { margin: 0 0 32px; font-family: var(--theme-font-heading); font-size: clamp(30px, 4vw, 48px); }
      &__list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin: 0; padding: 0; list-style: none; }
      &__card { padding: 28px; border-radius: var(--theme-radius); background: var(--theme-color-background-variant); }
      &__num { color: var(--theme-color-primary); font-weight: 700; }
    
      @media (max-width: 1023px) { &__list { grid-template-columns: 1fr 1fr; } }
      @media (max-width: 640px) { &__list { grid-template-columns: 1fr; } }
    }

    TrucoNo hace falta escribir nada para «reducir movimiento»: Layo ya quita las animaciones a quien lo tiene activado.

  9. JS y animaciones

    El JS de una plantilla se ejecuta una vez por cada sitio de la página donde sale, al final de la página. el es el envoltorio de esa sección: busca siempre dentro de él, porque puede haber dos copias de la misma sección. Solo se carga en las páginas que la usan, y si falla no rompe las demás.

    La sección tiene que verse y servir sin JS. El JS solo mejora: carruseles, filtros, cuentas atrás.

    Para animar al hacer scroll no hace falta JS: pon data-fx en cualquier etiqueta (fade-up, fade, slide-left, slide-right, zoom, blur o reveal), con data-fx-delay para retrasarla y data-fx-stagger para que los hijos entren uno tras otro.

    JS
    const list = el.querySelector('.ej-carrusel__list');
    for (const b of el.querySelectorAll('[data-dir]')) {
      b.addEventListener('click', () => list.scrollBy({ left: list.clientWidth * Number(b.dataset.dir), behavior: 'smooth' }));
    }
  10. Formularios

    Con un campo de tipo form, quien edita elige uno de sus formularios y configuration.form llega con todo: la dirección de envío, el antispam, los campos con su tipo y su error, y el mensaje de gracias.

    Copia el HTML de la plantilla de serie Formulario o Contacto con formulario y cambia solo el diseño. Funciona sin JavaScript y valida en el servidor.

  11. Importar: una, varias o una web entera

    Qué quieres meterDóndeArchivo
    Una plantillaDesarrollo › Plantillas › Importar…Un .json con una
    Varias a la vezEl mismo botónUn .json con todas
    Una web enteraAdministración › Webs › Exportar e importar › ImportarEl .zip de «Descargar .zip»

    El .json es el mismo que dan Exportar y Exportar todas. Cada plantilla se comprueba como al guardarla: si una falla, no entra ninguna. Si el id ya existe, Layo pregunta si actualizarla (las páginas conservan su contenido) o importarla como copia.

    Una web a medida se hace así: se divide el diseño en secciones, una plantilla por sección con un prefijo común (ac-cabecera, ac-portada…), todas en un solo .json. El .zip lo crea Layo al exportar una web montada y lleva el CSS ya compilado: no se escribe a mano.

    Formato para importar plantillas
    {
      "format": "layo-templates",
      "version": 1,
      "templates": [
        {
          "id": "ej-ventajas",
          "name": "Ventajas en tarjetas",
          "description": "Título, entradilla y tarjetas numeradas. Botón opcional.",
          "category": "Contenido",
          "position": "body",
          "fields": [],
          "styleFields": [],
          "html": "<section class=\"ej-ventajas\">…</section>",
          "scss": ".ej-ventajas { … }",
          "js": ""
        }
      ]
    }
  12. Antes de darla por buena

    • Todos los campos tienen un default realista y la miniatura de la biblioteca se ve bien.
    • Vaciando cualquier campo no quedan huecos ni etiquetas vacías.
    • Clases con prefijo propio, y ningún color, fuente ni radio escrito a mano: todo con var(--theme-…).
    • Se ve bien a 1440, 768 y 390 píxeles, sin scroll horizontal en el móvil.
    • Un solo <h1>, y solo en portadas.
    • Ningún | raw en un texto normal.
    • Funciona sin JavaScript, y el foco del teclado se ve.
La guía siguiente Con tu IA Las instrucciones, listas para copiar, para que Claude, ChatGPT o Cursor te escriban plantillas que se importan sin tocar nada.