Programadores · Formato do tema

O formato do tema

Um tema é JSON e CSS. Sem passo de build, sem framework, sem servidor para gerir. A mesma função que decide se o seu tema instala do nosso lado corre na CLI e no editor, por isso uma verificação limpa aí é um envio limpo. Para cada nó, componente e fonte de dados, veja a referência de temas.

Duas restrições

Saiba estas antes de começar

As duas são a razão pela qual um tema de alguém que nunca conhecemos pode sequer ser instalado numa loja ao vivo.

  • Um tema não leva JavaScript. Nenhum, de forma alguma. Uma secção são dados e é a nossa loja que a desenha, por isso não há nada para isolar. Tudo o que é interativo é um componente que você coloca pelo nome, e vários recebem o arranjo do seu tema, por isso um acordeão no seu tema parece o seu acordeão. Se precisa de um comportamento que não está na lista, diga-nos: isso é uma falha do nosso lado, e é assim que a lista cresce.
  • O estilo é CSS simples sobre os nossos tokens, não Tailwind. Escreva var(--shop-accent), var(--shop-font-heading), var(--shop-radius). O seu theme.json define os valores por omissão, e o comerciante pode mudar qualquer um sem tocar no seu CSS.

Arranjo

A pasta

É isto que o espaço de trabalho, a CLI e um envio em zip usam. Manter tudo só no theme.json também funciona; as pastas são para o seu conforto, não um 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

Mais nenhum ficheiro é aceite: nada de imagens, tipos de letra ou scripts na pasta. Um tema leva no máximo 60 secções e 2 MB no total.

Identidade

theme.json

O topo do tema. A linha $schema dá-lhe completação e ajuda em linha no VS Code e na maioria dos editores, a partir de um esquema gerado do próprio 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": {}
}
CampoO que é
slugLetras minúsculas, dígitos e traços. É permanente, e prefixa os tipos de secção do seu tema.
versionSemver. Veja Versões e revisão para o que cada tipo de versão pode mudar.
pluginsPlugins para os quais o tema faz arranjos, com um intervalo de versões. Veja Plugins abaixo.
tokensOs seus valores por omissão para os design tokens.
settingsEscolhas para todo o tema que o comerciante faz uma vez por loja.
migrationsRenomeações de secções, definições e tokens, para lojas que vêm de uma versão mais antiga.

Design

Tokens e definições

Os tokens são as cores, a tipografia e a forma da loja. As definições são escolhas sobre o arranjo. Ambos têm os seus valores por omissão, e o comerciante pode mudar os dois no ecrã de Design da loja.

TokenVariável CSSPor omissão
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

Estes são os primeiros 8 de 28. A referência lista-os 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 }
]

Uma secção lê uma definição do tema como { "get": "theme.homeLayout" } e as definições dela como { "get": "settings.heading" }. Use uma definição do tema quando a resposta é uma por loja, para o comerciante a mudar uma vez em vez de em cada secção.

Blocos de construção

Secções

Uma secção é o que um comerciante acrescenta a uma página. Declara um formulário, os dados de que precisa, o CSS e a marcação como uma árvore de nós de render. Você nunca constrói o formulário: declarar campos e blocos é o que dá um ao 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 é sempre <your slug>.<name>. O nome do ficheiro é a parte depois do ponto.
  • blocks dá ao comerciante um botão “Acrescentar frase”, limitado por max. O slot desenha os filhos uma vez por bloco, que é o que faz block.text querer dizer esta frase.
  • render é uma lista de nós, destes tipos: box, text, rich, img, link, each, if, slot, icon, money, component. Os valores são expressões como { "get": "product.title" } ou { "lit": "Sale" }.

Fontes de dados

Uma secção declara o que precisa, e a página vai buscá-lo antes de desenhar seja o que for. Duas secções que peçam a mesma lista custam uma consulta. Um tema não pode escrever uma consulta sua.

"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

Os componentes são as partes interativas e que conhecem a loja: product-card, shop-logo, shop-menu, cart-link, search-box e mais. Você coloca-os e estiliza à volta deles, com props onde um componente os aceita. Não pode escrever um.

O carrinho, a página de produto, a pesquisa, o início de sessão e as páginas de um plugin chegam como dados em âmbito mais pequenas partes que fazem uma coisa cada, como sheet, cart-remove, product-form, product-gallery, search-form e login-form. As versões já feitas são construídas com as mesmas partes, por isso um tema pode arranjar qualquer uma delas de outra maneira: um carrinho como popup em vez de gaveta, uma caixa de compra com a ordem dele. A referência lista cada âmbito e cada parte.

Páginas

Modelos

Um modelo é o arranjo inicial de uma página: uma lista de secções colocadas com as definições delas. Os comerciantes reorganizam-no depois.

[
  { "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
blogA lista de artigos do blogue. Precisa do plugin do blogue declarado.
blog-postUm artigo do blogue. Precisa do plugin do blogue declarado.

Um modelo com qualquer outro nome (header, home, collection, product, cart, search, login, register, footer, ou as páginas de um plugin declarado) é reportado quando valida, em vez de instalar em silêncio e não desenhar nada. Uma página leva no máximo 40 secções.

Dependências

Plugins

Algumas páginas pertencem a um plugin, como o blogue. Para arranjar essas páginas, declare o plugin no theme.json com o intervalo de versões contra o qual construiu.

"plugins": { "blog": "^2.0.0" }
  • Os seus modelos podem então usar as páginas e os tipos de secção desse plugin, como blog.index e blog.post, e os componentes dele, como blog-post-list.
  • Instalar o seu tema instala primeiro o plugin, para que as páginas que arranjou existam.
  • Os intervalos têm as formas ^2.0.0, ~2.1.0, >=2.0.0, 2.1.0 ou *. Uma submissão é bloqueada se o catálogo não tiver versão nenhuma dentro do seu intervalo.
  • Se só estiliza as páginas de um plugin com CSS, não declare nada. O seu CSS já lá chega.

Estilo

CSS, e como é isolado

Escreva seletores de classe normais. Na instalação, o seu CSS é isolado no seu tema e cada nome de classe ganha um prefixo para o tema todo, por isso o .card do seu tema não pode colidir com o de outro tema.

Os nomes de classe das secções têm de ser únicos entre secções. Todas as secções de um tema partilham esse único prefixo. Se duas secções estilizarem ambas .grid, as regras de uma reestilizam a outra, e uma submissão com uma colisão dessas é bloqueada. Dê às classes o nome da secção: .picks-grid, .lookbook-grid. As regras destinadas a todas as secções pertencem ao theme.css, que é partilhado de propósito.
  • As propriedades são verificadas contra uma lista. Tudo o que for recusado é reportado quando valida, nunca deitado fora em silêncio.
  • O url() só pode apontar para a nossa CDN de média. Os tipos de letra vêm dos tokens de tipo de letra: nomeie aí uma Google font e ela é buscada por si, sem nenhum @import no seu CSS.
  • O CSS de uma secção tem no máximo 40 kB, e o do tema inteiro no máximo 200 kB.

Imagens

Logótipos e imagens

Os comerciantes carregam o logótipo deles, e um logótipo SVG continua vetorial. É filtrado até ao desenho no carregamento e colocado sempre numa etiqueta img, por isso é seguro em qualquer tema.

Coloque o logótipo da loja com { "node": "component", "use": "shop-logo" }. Quando o seu desenho precisa de uma segunda versão, como um logótipo branco numa barra escura, acrescente uma definição de tema do tipo image e passe-a como src:

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

Dimensione com CSS a caixa onde a imagem está, não a imagem. As fotografias da sua demonstração vêm do catálogo de exemplo na loja do próprio tema, por isso não envia nenhuma.

Velocidade

Regras de desempenho

A Google mede uma loja num telemóvel médio numa rede lenta. Estas regras são o mesmo texto que a CLI escreve no AGENTS.md, e as verificações de publicação pesam a sua página inicial.

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

Escreva-o de forma a poder ser traduzido

Tudo o que um comerciante possa querer reescrever deve ser um campo com um valor por omissão, não uma cadeia literal na marcação.

Um campo aparece no editor do comerciante e no ecrã de tradução dele, por isso o seu título em inglês passa a ser o título em português dele. Um valor lit fica preso em inglês em todas as lojas que instalem o seu tema. Guarde o lit para coisas que não são palavras, como um nome de classe ou um valor de arranjo.

SeguinteVersões e revisão →
Formato do tema · WhizzyCommerce