Desarrolladores · Formato del tema

El formato del tema

Un tema es JSON y CSS. Sin paso de build, sin framework, sin servidor que llevar. La misma función que decide si su tema instala de nuestro lado corre en la CLI y en el editor, así que una comprobación limpia ahí es una subida limpia. Para cada nodo, componente y fuente de datos, vea la referencia de temas.

Dos restricciones

Sepa estas antes de empezar

Ambas son la razón de que un tema de alguien a quien no conocemos pueda instalarse siquiera en una tienda en producción.

  • Un tema no lleva JavaScript. Ninguno, de ninguna forma. Una sección son datos y la dibuja nuestra tienda, así que no hay nada que aislar. Todo lo interactivo es un componente que usted coloca por su nombre, y varios toman su disposición de su tema, así que un acordeón en su tema parece su acordeón. Si necesita un comportamiento que no está en la lista, díganoslo: eso es una carencia de nuestro lado, y es así como crece la lista.
  • El estilo es CSS plano sobre nuestros tokens, no Tailwind. Escriba var(--shop-accent), var(--shop-font-heading), var(--shop-radius). Su theme.json fija los valores por defecto, y el comerciante puede cambiar cualquiera sin tocar su CSS.

Estructura

La carpeta

Esto es lo que usan el espacio de trabajo, la CLI y una subida en zip. Tenerlo todo solo en theme.json también funciona; las carpetas son para su comodidad, no un segundo formato.

harbour/
  theme.json            identity, tokens, theme settings, plugins
  theme.css             styling for the whole theme
  sections/<name>.json  one file per section: its form, its data, its markup, its CSS
  templates/<page>.json one file per page: which sections, in which order
  AGENTS.md             the performance rules, for coding assistants
  README.md             yours

No se acepta ningún otro archivo: ni imágenes, ni tipografías, ni scripts en la carpeta. Un tema lleva como mucho 60 secciones y 2 MB en total.

Identidad

theme.json

La cabecera del tema. La línea $schema le da autocompletado y ayuda en línea en VS Code y en casi cualquier editor, desde un esquema generado del propio validador.

{
  "$schema": "https://whizzycommerce.com/schema/theme-v1.json",
  "dsl": 1,
  "slug": "harbour",
  "name": "Harbour",
  "version": "1.0.0",
  "description": "Big photography and quiet type for homeware shops.",
  "author": { "name": "Tide Studio", "url": "https://tide.example", "email": "help@tide.example" },
  "docsUrl": "https://tide.example/harbour",
  "specs": { "responsive": true, "rtl": false },
  "plugins": { "blog": "^2.0.0" },
  "tokens": { "colorAccent": "#1f3a5f", "radius": "0" },
  "settings": [],
  "migrations": {}
}
CampoQué es
slugLetras minúsculas, dígitos y guiones. Es permanente, y prefija los tipos de sección de su tema.
versionSemver. Vea Versiones y revisión para saber qué puede cambiar cada tipo de versión.
pluginsPlugins para los que el tema dispone páginas, con un rango de versiones. Vea Plugins abajo.
tokensSus valores por defecto para los design tokens.
settingsElecciones de todo el tema que el comerciante hace una vez por tienda.
migrationsRenombrados de secciones, ajustes y tokens, para tiendas que vienen de una versión anterior.

Diseño

Tokens y ajustes

Los tokens son los colores, la tipografía y la forma de la tienda. Los ajustes son decisiones sobre la disposición. Ambos tienen sus valores por defecto, y el comerciante puede cambiar los dos en la pantalla de Diseño de la tienda.

TokenVariable CSSPor defecto
colorBgvar(--shop-bg)#ffffff
colorFgvar(--shop-fg)#0f1115
colorAccentvar(--shop-accent)#0b0f19
colorMutedvar(--shop-muted)#6b7280
fontHeadingvar(--shop-font-heading)"Inter", system-ui, sans-serif
fontBodyvar(--shop-font-body)"Inter", system-ui, sans-serif
fontSizeBodyvar(--shop-font-size-body)16px
fontSizeHeadingvar(--shop-font-size-heading)60px

Esos son los primeros 8 de 28. La referencia los lista todos.

"settings": [
  { "key": "homeLayout", "label": "Home layout", "type": "select", "default": "roomy",
    "options": [{ "value": "roomy", "label": "Roomy" }, { "value": "compact", "label": "Compact" }] },
  { "key": "showNote", "label": "Show the delivery note", "type": "boolean", "default": true }
]

Una sección lee un ajuste del tema como { "get": "theme.homeLayout" } y los suyos propios como { "get": "settings.heading" }. Use un ajuste del tema cuando la respuesta es una por tienda, para que el comerciante la cambie una vez en lugar de en cada sección.

Piezas

Secciones

Una sección es lo que un comerciante añade a una página. Declara un formulario, los datos que necesita, su CSS y su marcado como un árbol de nodos de render. Usted nunca construye el formulario: declarar campos y bloques es lo que le da uno al comerciante.

{
  "type": "harbour.ticker",
  "label": "Ticker",
  "category": "layout",
  "description": "Short phrases across the page, in the accent colour.",

  "fields": [
    { "key": "speed", "label": "Speed", "type": "select", "default": "slow",
      "options": [{ "value": "slow", "label": "Slow" }, { "value": "fast", "label": "Fast" }] }
  ],
  "blocks": [{
    "type": "phrase", "label": "Phrase", "max": 6,
    "fields": [
      { "key": "text", "label": "Text", "type": "text", "default": "Free delivery" }
    ]
  }],

  "css": ".ticker-band { display: flex; gap: 3rem; background: var(--shop-accent) }",

  "render": [
    { "node": "box", "as": "section", "cls": "ticker-band", "children": [
      { "node": "slot", "children": [
        { "node": "text", "as": "span", "value": { "get": "block.text" } }
      ]}
    ]}
  ]
}
  • type es siempre <your slug>.<name>. El nombre del archivo es la parte después del punto.
  • blocks le da al comerciante un botón “Añadir frase”, limitado por max. slot dibuja sus hijos una vez por bloque, que es lo que hace que block.text signifique esta frase.
  • render es una lista de nodos, de estos tipos: box, text, rich, img, link, each, if, slot, icon, money, component. Los valores son expresiones como { "get": "product.title" } o { "lit": "Sale" }.

Fuentes de datos

Una sección declara lo que necesita, y la página lo busca antes de dibujar nada. Dos secciones que pidan la misma lista cuestan una consulta. Un tema no puede escribir una consulta propia.

"data": [
  { "as": "picks", "source": "collection-products",
    "collection": { "get": "settings.collection" }, "limit": 8 }
],
"render": [
  { "node": "box", "cls": "picks-grid", "children": [
    { "node": "each", "in": { "get": "data.picks" }, "as": "item", "limit": 8,
      "children": [
        { "node": "component", "use": "product-card", "props": { "product": { "get": "item" } } }
      ],
      "empty": [
        { "node": "text", "as": "p", "value": { "lit": "Nothing to show here yet." } }
      ] }
  ]}
]

Componentes

Los componentes son las partes interactivas y que conocen la tienda: product-card, shop-logo, shop-menu, cart-link, search-box y más. Usted los coloca y da estilo alrededor, con props donde un componente los acepta. No puede escribir uno.

El carrito, la página de producto, la búsqueda, el inicio de sesión y las páginas de un plugin llegan como datos en ámbito más pequeñas partes que hacen una cosa cada una, como sheet, cart-remove, product-form, product-gallery, search-form y login-form. Las versiones ya hechas están construidas con las mismas partes, así que un tema puede disponer cualquiera de otra manera: un carrito como popup en vez de cajón, una caja de compra con su propio orden. La referencia lista cada ámbito y cada parte.

Páginas

Plantillas

Una plantilla es la disposición inicial de una página: una lista de secciones colocadas con sus ajustes. Los comerciantes la reordenan después.

[
  { "id": "hero", "type": "hero", "settings": { "heading": "Made to last" } },
  { "id": "picks", "type": "harbour.picks", "settings": { "heading": "New in", "limit": 4 } }
]
IdentificadorPágina
headerHeader
homeHome
collectionCategory page
productProduct page
cartCart page
searchSearch results
loginSign in page
registerCreate account page
footerFooter
blogLa lista de entradas del blog. Necesita el plugin del blog declarado.
blog-postUna entrada del blog. Necesita el plugin del blog declarado.

Una plantilla con cualquier otro nombre (header, home, collection, product, cart, search, login, register, footer, o las páginas de un plugin declarado) se reporta al validar, en vez de instalarse en silencio y no dibujar nada. Una página lleva como mucho 40 secciones.

Dependencias

Plugins

Algunas páginas pertenecen a un plugin, como el blog. Para disponer esas páginas, declare el plugin en theme.json con el rango de versiones contra el que construyó.

"plugins": { "blog": "^2.0.0" }
  • Sus plantillas pueden usar entonces las páginas y los tipos de sección de ese plugin, como blog.index y blog.post, y sus componentes, como blog-post-list.
  • Instalar su tema instala antes el plugin, para que las páginas que dispuso existan.
  • Los rangos toman las formas ^2.0.0, ~2.1.0, >=2.0.0, 2.1.0 o *. Un envío se bloquea si el catálogo no tiene ninguna versión dentro de su rango.
  • Si solo da estilo a las páginas de un plugin con CSS, no declare nada. Su CSS ya llega ahí.

Estilo

El CSS, y cómo se aísla

Escriba selectores de clase normales. Al instalar, su CSS se aísla a su tema y cada nombre de clase recibe un prefijo para todo el tema, así que el .card de su tema no puede chocar con el de otro tema.

Los nombres de clase de las secciones deben ser únicos entre secciones. Todas las secciones de un tema comparten ese único prefijo. Si dos secciones dan estilo a .grid, las reglas de una reestilizan la otra, y un envío con una colisión así se bloquea. Nombre las clases por su sección: .picks-grid, .lookbook-grid. Las reglas pensadas para todas las secciones van en theme.css, que se comparte a propósito.
  • Las propiedades se comprueban contra una lista. Todo lo que se rechaza se reporta al validar, nunca se descarta en silencio.
  • url() solo puede apuntar a nuestra CDN de medios. Las tipografías vienen de los tokens de tipografía: nombre ahí una Google font y se descarga por usted, sin ningún @import en su CSS.
  • El CSS de una sección es como mucho de 40 kB, y el del tema entero de 200 kB.

Imágenes

Logotipos e imágenes

Los comerciantes suben su propio logotipo, y un logotipo SVG sigue siendo vectorial. Se filtra hasta su dibujo al subirlo y se coloca siempre en una etiqueta img, así que es seguro en cualquier tema.

Coloque el logotipo de la tienda con { "node": "component", "use": "shop-logo" }. Cuando su diseño necesita una segunda versión, como un logotipo blanco sobre una barra oscura, añada un ajuste de tema de tipo image y páselo como src:

{ "node": "component", "use": "shop-logo", "props": { "src": { "get": "theme.logoOnDark" } } }

Dimensione con CSS la caja donde va la imagen, no la imagen. Las fotos de su demostración vienen del catálogo de muestra de la tienda propia del tema, así que usted no envía ninguna.

Velocidad

Reglas de rendimiento

Google mide una tienda en un móvil de gama media sobre una red lenta. Estas reglas son el mismo texto que la CLI escribe en AGENTS.md, y las comprobaciones de publicación pesan su portada.

Rules for anyone editing this theme, including an AI assistant. Google measures the shop on a mid-range phone on a slow network, so that is the device to build for.

Pictures

  • The picture at the top of the page (hero, first slide) gets "priority": true on its img node. It is what Largest Contentful Paint times, and without it the browser waits to load it. Only that one: every other picture stays lazy.
  • Give every full-width picture a mobileSrc field for phones, a crop that works on a narrow screen, so a phone does not download a desktop banner.
  • Set sizes on any img that is not full width, for example "(min-width: 768px) 33vw, 50vw" for a three-column grid, so the browser picks a smaller file.
  • Give the box a picture sits in a fixed shape in CSS (aspect-ratio), so the page does not jump when it arrives.
  • Do not put the main picture in a CSS background-image: the browser finds it late and cannot pick a size for it.

Fonts

  • At most two families, heading and body. Each one is a separate download.
  • Leave the font tokens at the core stack if the design does not need a web font: that costs nothing at all.

Weight

  • Keep theme.css lean: no unused rules, no copies of the same rule per section.
  • Above the first screen, place only what is needed there. A long home page is fine; a first screen with a slideshow, a carousel and a video is not.
  • Keep each limits realistic. Twenty-four product cards on a home page is twenty-four pictures and a long page to render; eight is usually enough.
  • Avoid heavy CSS effects on large areas (backdrop-filter, big box-shadow, filter: blur), which are slow to draw on a phone.

Checking

Run https://pagespeed.web.dev on the shop's home page and one product page, on Mobile. Aim for Largest Contentful Paint under 2.5 s and Cumulative Layout Shift under 0.1. Fix the first item Lighthouse lists before anything else.

Idiomas

Escríbalo de forma que se pueda traducir

Todo lo que un comerciante pueda querer reescribir debería ser un campo con un valor por defecto, no una cadena literal en el marcado.

Un campo aparece en el editor del comerciante y en su pantalla de traducción, así que su titular en inglés pasa a ser el suyo en portugués. Un valor lit se queda atascado en inglés en cada tienda que instale su tema. Reserve lit para cosas que no son palabras, como un nombre de clase o un valor de disposición.

SiguienteVersiones y revisión →
Formato del tema · WhizzyCommerce