Aparência
Fontes de produtos
Responsabilidades e fontes comerciais
Liquid define estrutura e textos fixos; CSS define toda a aparência; JSON define presença e ordem das sections. dataSources declara somente critérios comerciais. Resultados e paginação são calculados pelo servidor e chegam em section.data e category.pagination. Não existem settings nem section.settings.
behavior está reservado para evolução funcional futura, como um behavior.perPage persistente e validado. Ele não é aceito hoje, não deve aparecer vazio ou nos formatos suportados pela IA e nunca poderá conter aparência. Antes de ser implementada, a extensão deverá definir campos, limites e precedência.
Uma vitrine curta pode declarar exatamente uma fonte products:
json
{
"sections": [
{
"id": "premium_products",
"type": "product_list",
"dataSources": {
"products": {
"type": "products",
"query": {
"source": "catalog",
"minPriceAmount": 30001,
"inStock": true,
"orderBy": "price_asc",
"limit": 8
}
}
}
}
]
}source aceita catalog (padrão) e home. A fonte home aceita source, limit e o filtro stock ou inStock, preserva a seleção editorial e usa o catálogo quando a seleção filtrada está vazia. catalog aceita categoryId, minPriceAmount, maxPriceAmount, inStock ou stock, featured, orderBy (default, price_asc, price_desc, newest ou name_asc) e limit. Valores monetários são centavos inclusivos: a partir de R$ 300 é 30000, enquanto acima de R$ 300 é 30001. categoryId precisa identificar categoria ativa da própria loja.
stock aceita all (sem filtro), in_stock (saldo físico positivo) e available (saldo positivo ou venda sem estoque habilitada). Produtos variáveis exigem uma combinação válida; o saldo do pai não substitui a combinação. Saldo negativo ou zero só atende a available quando a venda sem estoque estiver permitida. Esse filtro não garante purchasable: estado comercial, mínimo e autorização ainda são avaliados pelo servidor. inStock: true equivale ao filtro físico; não declare stock e inStock juntos. O filtro é aplicado antes do limite, count e paginação.
Cada vitrine pede de 1 a 24 itens (padrão 8). Um template aceita no máximo oito fontes e soma até 96 itens, contando instâncias repetidas. Vitrines não têm total nem paginação: section.data.products contém status, items e pagination: null. Uma consulta vazia continua vazia. Consultas idênticas no mesmo carregamento compartilham uma Promise privada da requisição, mas cada instância recebe os próprios dados por section.id.
O bundle efetivo inteiro, incluindo alterações em memória da prévia, é validado antes de qualquer vitrine ser consultada. A resolução do template é a mesma do ThemeEngine. O mapa privado nasce no começo da requisição e é compartilhado pela seleção principal e pelas vitrines; ele não é exposto ao Liquid nem reutilizado por outro visitante. Somente section.data da instância é injetado na section correspondente.
A loja usada nas fontes vem explicitamente da autorização interna ou do resolvedor público, nunca de shop.id no contexto de apresentação. Antes da publicação, referências de categoria de todos os templates efetivos são validadas para essa loja sem carregar suas listagens. Uma referência inválida impede a nova revisão sem alterar a revisão ativa.
São inválidos nomes ou tipos de fonte diferentes, page, perPage, offset, cursor, SQL, campos visuais, resultados em data, settings, behavior, when e propriedades desconhecidas. Alterar templates/static.json alcança todas as páginas institucionais; alterar um component alcança todas as suas utilizações.
Na evolução do contrato, “Carregar mais” poderá reutilizar a seleção paginada principal; um futuro behavior.perPage poderá fornecer um padrão persistente depois de ter precedência e limites definidos; e novas fontes comerciais deverão ter validação e carregamento próprios. Em todos os casos, o visual continuará pertencendo exclusivamente a Liquid e CSS. Nenhum desses recursos futuros está ativo neste MVP.
Consultas declaradas no Liquid
A tag query_products permite à IA programar consultas nas sections e components existentes, sem criar um botão administrativo nem acessar o banco diretamente:
liquid
{% query_products vitrine, source: 'catalog', stock: 'available', limit: 8 %}
{% for product in vitrine.items %}
{% render 'product_card', product: product %}
{% else %}
<p>Nenhum produto nesta seleção.</p>
{% endfor %}catalog e home usam os mesmos parâmetros e resultados das fontes JSON: status, items e pagination: null. Literais e variáveis Liquid são aceitos; valores calculados com filtros devem ser atribuídos antes com assign. Não invente SQL, URLs de API, identificadores de loja, page, perPage ou offset como argumentos. Consultas monetárias sem autorização retornam restricted, sem consultar os produtos. A ausência de resultados não autoriza outra consulta sem os filtros pedidos pelo lojista.
Na página de busca ou categoria, use source: 'listing' para consultar a seleção paginada atual com um critério de estoque definido pelo tema:
liquid
{% query_products resultado, source: 'listing', stock: 'available' %}
<p>{{ resultado.total }} produtos</p>
{% for product in resultado.items %}
{% render 'product_card', product: product %}
{% endfor %}
{% if resultado.pagination.hasNextPage %}
<a href="{{ resultado.pagination.nextUrl }}">Próxima página</a>
{% endif %}listing aceita apenas source, stock e orderBy. Herda da requisição o termo de busca, a categoria, marcas, variações, faixa de preço e paginação. O visitante pode restringir ainda mais o estoque físico com stock=1 na URL, mas não remover o filtro obrigatório declarado no tema. orderBy fornece a ordenação quando a URL não contém sort; deve ser uma ordenação válida para a página (relevance somente em busca, default somente em categoria). As restrições de preço da loja continuam tendo precedência. page e perPage vêm da URL validada (24 itens por padrão, até 100); não são configuração persistente na tag.
O resultado de listing contém status: 'ready', items, o alias products, total, filters, sort, activeFilters, clearFiltersUrl e pagination. filters.availability.policy informa all, in_stock ou available, enquanto selected descreve o filtro físico escolhido na URL. As facetas seguem o mesmo critério obrigatório; as URLs já preservam filtros e o namespace da prévia. Busca sem termo retorna lista vazia. Fora de busca/categoria, listing gera erro.
Use o mesmo resultado para produtos, total, filtros e paginação. A tag atribui uma variável local; não substitui automaticamente page.products, category.products ou o listing original. Pode usar listing como variável alvo dentro da section para reaproveitar seus components, passando a variável explicitamente no render. Uma section não altera o contexto das demais. O contexto principal continua sendo preparado; seleções diferentes podem causar consultas adicionais. Consultas idênticas compartilham o cache privado da requisição.
Há até oito execuções de query_products por documento, inclusive em loops, components e layout, com orçamento de 200 itens. Cada vitrine reserva seu limit (até 24); cada listing reserva 100 itens, o máximo permitido pela URL. Esses limites são adicionais ao orçamento das fontes JSON. Ultrapassá-los produz erro, sem consulta extra nem resultado parcial. Falha de banco não vira vitrine vazia.
Sintaxe e parâmetros conhecidos são validados no bundle; valores dinâmicos e pertencimento da categoria são validados antes da consulta correspondente. Categoria inválida em um ramo não executado não é validada antecipadamente como referência JSON. Loja e política de preço vêm do servidor, nunca de variáveis Liquid. A tag funciona na loja publicada e na prévia com serviço de catálogo; renderizações isoladas precisam fornecer o carregador e falham explicitamente quando ele não existe.