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": {}
}| Campo | O que é |
|---|---|
slug | Letras minúsculas, dígitos e traços. É permanente, e prefixa os tipos de secção do seu tema. |
version | Semver. Veja Versões e revisão para o que cada tipo de versão pode mudar. |
plugins | Plugins para os quais o tema faz arranjos, com um intervalo de versões. Veja Plugins abaixo. |
tokens | Os seus valores por omissão para os design tokens. |
settings | Escolhas para todo o tema que o comerciante faz uma vez por loja. |
migrations | Renomeaçõ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.
| Token | Variável CSS | Por omissão |
|---|---|---|
colorBg | var(--shop-bg) | #ffffff |
colorFg | var(--shop-fg) | #0f1115 |
colorAccent | var(--shop-accent) | #0b0f19 |
colorMuted | var(--shop-muted) | #6b7280 |
fontHeading | var(--shop-font-heading) | "Inter", system-ui, sans-serif |
fontBody | var(--shop-font-body) | "Inter", system-ui, sans-serif |
fontSizeBody | var(--shop-font-size-body) | 16px |
fontSizeHeading | var(--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. Oslotdesenha os filhos uma vez por bloco, que é o que fazblock.textquerer 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 } }
]| Identificador | Página |
|---|---|
header | Header |
home | Home |
collection | Category page |
product | Product page |
cart | Cart page |
search | Search results |
login | Sign in page |
register | Create account page |
footer | Footer |
blog | A lista de artigos do blogue. Precisa do plugin do blogue declarado. |
blog-post | Um 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.indexeblog.post, e os componentes dele, comoblog-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.0ou*. 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.
.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@importno 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": trueon itsimgnode. 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
mobileSrcfield for phones, a crop that works on a narrow screen, so a phone does not download a desktop banner. - Set
sizeson anyimgthat 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.csslean: 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
eachlimits 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, bigbox-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.