Blocks

A pasta Blocks concentra os blocos reutilizáveis que podem ser disponibilizados no CMS para páginas configuradas com content_for_page.

Cada bloco deve possuir dois arquivos com o mesmo nome base:

  • Blocks/<type>.html: responsável pela renderização do bloco na página.
  • Blocks/<type>.schema.json: responsável por descrever os campos editáveis do bloco no CMS e o contrato usado na validação do salvamento.

Exemplo de estrutura

Blocks/
  banner_button.html
  banner_button.schema.json
  banner_carousel.html
  banner_carousel.schema.json
  button_carousel.html
  button_carousel.schema.json
  image_showcase.html
  image_showcase.schema.json
  product_carousel.html
  product_carousel.schema.json

Regras importantes

  • O nome do arquivo deve ser igual ao type do bloco.
  • Para um bloco com type: "banner_button", os arquivos esperados são Blocks/banner_button.html e Blocks/banner_button.schema.json.
  • O arquivo .html é carregado na renderização da página.
  • O arquivo .schema.json é carregado para montar os campos do CMS e validar os dados enviados no salvamento.
  • Se o .schema.json não existir, o bloco não poderá ser salvo.
  • Se o .html não existir, o bloco não poderá ser renderizado corretamente.

Guia de criação de schema.json

O schema.json define como o bloco aparece para edição no CMS e quais valores a plataforma aceita.

Estrutura base

{
  "name": "Banner com botão",
  "type": "banner_button",
  "settings": [
    {
      "label": "Título principal",
      "name": "title",
      "type": "text",
      "required": false
    }
  ]
}

Propriedades do bloco

PropriedadeTipoObrigatóriaDescrição
namestringrecomendadaNome amigável exibido para quem está configurando o bloco.
typestringsimIdentificador técnico do bloco. Deve ser o mesmo valor usado em content_for_page e no nome dos arquivos.
settingsarraysimLista de campos editáveis do bloco.
varyByAudiencebooleannãoHabilita variação de conteúdo por audiência neste bloco. Pode ser declarada em qualquer bloco, sem restrição de tipo. Quando true, qualquer campo de settings pode variar. Requer o add-on de audiências ativo na loja. Padrão: false.

Propriedades de cada item em settings

PropriedadeTipoObrigatóriaDescrição
labelstringrecomendadaTexto exibido no CMS para o usuário da plataforma.
namestringsimNome técnico do campo. Ele vira a chave dentro de content e pode ser acessado no HTML por block_data.<name>.
typestringsimTipo do campo.
requiredbooleannãoDefine se o campo é obrigatório.
optionsarrayobrigatória para selectLista de opções válidas para campos fechados (select/options).

Tipos de campos suportados em settings

TipoDescrição
textTexto simples. O valor deve ser uma string com tamanho máximo limitado. É o tipo padrão.
stringAlias de text. Comportamento idêntico — aceita string com limite de tamanho.
numberValor numérico. Aceita apenas valores do tipo número (inteiro ou decimal).
urlURL. Aceita string e rejeita URLs perigosas.
image_pickerSeletor de imagem. Aceita string (URL ou caminho) e rejeita URLs perigosas.
colorCor. Aceita string em formato hexadecimal (#RGB, #RRGGBB, #RRGGBBAA) ou rgba.
selectSeleção única. O valor deve ser uma das opções definidas na lista options.
optionsAlias de select. Comportamento idêntico — valida contra a lista de opções permitidas.
checkboxCaixa de seleção. Aceita apenas valores booleanos (true ou false).
booleanAlias de checkbox. Comportamento idêntico.
hotsite_productsID de hotsite. Aceita apenas inteiro positivo. O Storefront busca automaticamente os produtos desse hotsite e os injeta em block_data.<name> como uma lista de produtos. Máximo de 50 produtos retornados.
product_listLista de IDs de produtos. Aceita array não vazio de inteiros positivos. Os produtos são buscados e injetados em block_data.<name> respeitando a ordem declarada. IDs inválidos são ignorados. Máximo de 50 IDs.
rangevalor numérico com mínimo/máximo.

Exemplo completo com vários tipos

{
  "name": "Hero promocional",
  "type": "promo_hero",
  "settings": [
    {
      "label": "Título",
      "name": "title",
      "type": "text",
      "required": true
    },
    {
      "label": "Produtos por linha",
      "name": "items_visible",
      "type": "number",
      "required": false
    },
    {
      "label": "Link do botão",
      "name": "button_url",
      "type": "url",
      "required": false
    },
    {
      "label": "Imagem principal",
      "name": "background_image",
      "type": "image_picker",
      "required": true
    },
    {
      "label": "Cor do texto",
      "name": "text_color",
      "type": "color",
      "required": false
    },
    {
      "label": "Tema visual",
      "name": "theme",
      "type": "select",
      "required": false,
      "options": [
        { "label": "Claro", "value": "light" },
        { "label": "Escuro", "value": "dark" }
      ]
    },
    {
      "label": "Exibir selo",
      "name": "show_badge",
      "type": "checkbox",
      "required": false
    }
  ]
}

Exemplos isolados

  • Campo select:
{
  "label": "Tamanho do título",
  "name": "title_size",
  "type": "select",
  "required": false,
  "options": [
    { "label": "Pequeno", "value": "sm" },
    { "label": "Médio", "value": "md" },
    { "label": "Grande", "value": "lg" },
    { "label": "Extra grande", "value": "xl" }
  ]
}
  • Campo color:
{
  "label": "Cor do texto",
  "name": "text_color",
  "type": "color",
  "required": false
}
  • Campo image_picker:
{
  "label": "Imagem",
  "name": "image",
  "type": "image_picker",
  "required": true
}

Como os dados chegam no HTML do bloco

No arquivo Blocks/<type>.html, os valores das configurações do bloco ficam disponíveis em block_data.

Exemplo:

{{~
  title = block_data.title
  if title | string.empty
    title = "Título padrão"
  end
~}}

<section>
  <h2>{{ title }}</h2>
</section>

Isso significa que o name de cada item em settings deve ser pensado como uma chave de dados reutilizável tanto pelo CMS quanto pelo template.

Blocos com variação por audiência

Um bloco pode oferecer ao lojista versões de conteúdo diferentes por audiência do cliente logado. É um recurso opcional da loja (add-on contratado por aditivo) e opt-in por bloco.

Não há restrição de tipo de bloco. Banner, carrossel de banners, vitrine de produtos, faixa de texto, bloco de imagem, bloco autoral da agência — qualquer bloco do tema pode declarar a flag. E, uma vez declarada, qualquer campo do bloco pode variar: texto, número, url, image_picker, color, select, checkbox, range, product_list e hotsite_products.

Habilitando

Declare varyByAudience como true no nível do bloco, irmão de name, type e settings:

{
  "name": "Vitrine de produtos",
  "type": "product_carousel",
  options: {
        resolveAudienceSegments: true
    }
  "settings": [
    { "label": "Título",   "name": "title",    "type": "text" },
    { "label": "Produtos", "name": "products", "type": "product_list", "required": true }
  ]
}

Regras:

  • Não existe tipo de campo de audiência. Não declare nada em settings para isso — a flag no bloco é suficiente para o editor desenhar o seletor.
  • Não existe flag por campo. Marcado o bloco, qualquer setting dele pode variar.
  • Bloco sem a flag é rejeitado se um salvamento tentar enviar variações para ele (HTTP 400).
  • O teto é de 2 audiências distintas por bloco, validado no salvamento.

O que muda no .html do bloco

Nada. O bloco continua recebendo apenas block_data, e o que chega ali já é o conteúdo resolvido para o cliente: a versão padrão com a variação da audiência aplicada por cima, quando houve match. O contêiner de variações nunca é entregue ao template.

{{~
  titulo = block_data.title
  if titulo | string.empty
    titulo = "Destaques"
  end
~}}

<section class="vitrine">
  <h2>{{ titulo }}</h2>
  {{ spot_carousel products: block_data.products }}
</section>

Herança: a variação declara só o que muda

O editor apresenta todos os campos do bloco em cada audiência, pré-preenchidos com o valor da versão padrão — mas a plataforma guarda apenas as diferenças. Campo igual à base, ou vazio, é herança e não vira override (false e 0 não contam como vazio).

Consequência para quem desenha o bloco: a versão padrão precisa fazer sentido sozinha e estar sempre completa. Ela é o que a maior parte do tráfego vê, e é dela que as audiências herdam tudo o que não sobrescreverem. Um campo required no schema é exigido na versão padrão, não nas variações.

Precedência

Um cliente pode pertencer a várias audiências. Vence a primeira configurada pelo lojista naquele bloco. Variações nunca são mescladas entre si, e a ordem em que o Wake Experience devolve as audiências do cliente é irrelevante.

Boas práticas

  • Não escreva condicional de audiência no Scriban. O bloco não sabe qual audiência venceu, e não deve saber. Diferença de conteúdo é decisão do lojista no Design Studio.
  • Cuidado com o tamanho. O conteúdo do layout inteiro tem teto de 56.375 caracteres. Um bloco com muitos campos, multiplicado por audiências e por número de blocos na página, consome esse orçamento rápido. Prefira campos enxutos.
  • Habilite onde houver intenção de segmentar. Tecnicamente não há limite de quantos blocos do tema podem declarar a flag. Na prática, marcar blocos que o lojista nunca vai segmentar só polui o painel — combine com ele quais blocos entram.

Comportamento completo (identificação do cliente, cookie, cache, preview e limites) em Design Studio — Conteúdos por audiência.

Blocos em templates que atendem várias URLs

Um mesmo template pode atender várias URLs (ver urlMatch em
pages.json), e cada uma dessas
URLs pode ter um layout exclusivo configurado pelo lojista no Design Studio.

Isso não muda nada na criação do bloco: o mesmo par
Blocks/<type>.html + Blocks/<type>.schema.json atende tanto o layout do
template quanto os layouts exclusivos por URL. Não existe schema, campo ou flag
específica para layout exclusivo.

O que muda é a recomendação de como escrever o bloco:

  • Não presuma uma URL fixa dentro do bloco. Se o bloco precisa de um link,
    um título ou uma imagem diferente por página, isso deve ser um campo em
    settings, editável pelo lojista — não uma condicional no Scriban baseada
    na URL corrente.
  • Não use o bloco para diferenciar páginas. O caminho suportado para "esta
    URL precisa de um banner diferente" é o layout exclusivo por URL no Design
    Studio, não uma ramificação dentro do HTML do bloco.
  • O bloco continua recebendo apenas block_data. O conteúdo entregue em
    block_data é o do layout que venceu a resolução para aquela URL — exclusivo
    se houver, do template caso contrário. O bloco não precisa (e não consegue)
    saber qual dos dois foi usado.

Resumo

Para disponibilizar um bloco no CMS desta primeira versão, a agência deve seguir este fluxo:

  • Criar Blocks/<type>.html.
  • Criar Blocks/<type>.schema.json.
  • O content_for_page pode ser declarado em dois níveis:
    • No item de customs: vale para as URLs atendidas por aquele custom.
    • No nível da página (ex.: no objeto hotsite): vale para as URLs atendidas pelo template base da página; também é usado como fallback quando o custom correspondente não declara content_for_page próprio.
  • Garantir que os nomes dos campos em settings[].name sejam os mesmos usados no block_data do HTML.
  • Validar se os tipos escolhidos no schemaestão entre os suportados pela plataforma.
  • Se o bloco deve oferecer variação por audiência, declarar "varyByAudience": true no schema — e garantir que a versão padrão do bloco esteja completa, já que é dela que as audiências herdam.