HystoreWiki

10 min de leituraVer em Markdown

Guia do tema em código da Hystore

Um tema em código da Hystore é uma pasta de arquivos Liquid, CSS e JSON que desenha a vitrine da loja: cabeçalho, rodapé, início, loja, páginas de produto, notícias, jogadores. O código da própria loja cuida do carrinho, do pagamento, da conta e do login, que nunca carregam o tema.

Este guia é a referência para pessoas e para assistentes de IA que editam um tema. Tudo aqui é conferido pelos servidores da Hystore: um arquivo que quebra uma regra é recusado ao salvar, com o arquivo, a linha e o motivo.

Fluxo de trabalho

npx @hystudios/hystore login            # aprove no painel (sem token para colar)
npx @hystudios/hystore theme pull       # baixe o rascunho para esta pasta
npx @hystudios/hystore theme dev        # acompanhe a pasta, envie a cada gravação, prévia ao vivo
npx @hystudios/hystore theme check      # confira na sua máquina, sem enviar
npx @hystudios/hystore theme push       # envie as mudanças para o rascunho
npx @hystudios/hystore theme publish    # peça a publicação; quem tem permissão aprova no painel
npx @hystudios/hystore theme versions   # as últimas 30 versões publicadas
npx @hystudios/hystore theme rollback 3 # traga a versão 3 de volta para o rascunho
npx @hystudios/hystore theme new        # comece uma pasta a partir do tema Base da Hystore
  • As edições vão sempre para o rascunho. A loja no ar só muda quando um pedido de publicação é aprovado no painel (Personalização > Tema em código).
  • O link de prévia (do theme dev) mostra o rascunho no endereço real da loja por 24 horas e recarrega sozinho a cada envio.
  • Se alguém mudou um arquivo no painel depois do seu último pull, o envio desse arquivo é recusado como conflito: faça o pull, junte e envie de novo.

Regras fixas

Isto é recusado (o arquivo não é salvo):

  • <script>, <style>, <iframe>, <form>, <input>, <textarea>, <select>, <object>, <embed>, <base>, <meta>, <link>, <foreignObject> de SVG.
  • Atributos de evento (onclick, onload, ...), links javascript:, vbscript: e data: (exceto data:image/png|jpeg|gif|webp;base64), srcdoc, http-equiv, referências numéricas de caractere (&#...;): escreva o próprio caractere.
  • No CSS: @import, expression(), behavior, -moz-binding, @namespace, @document, image-set(), escapes com barra invertida fora de strings, e url() que não seja assets/<nome>, um arquivo https://fonts.gstatic.com/... ou uma imagem pequena data:image/...;base64. A classe hystore-required é reservada.
  • As tags Liquid include, layout e block (use render, o arquivo de layout e hystore_block).

Não existe JavaScript nas páginas do tema. A interação vem de atributos data-hystore-* e de elementos <hystore-*> (veja Peças interativas). A loja limpa o HTML desenhado de novo, e a política de segurança de conteúdo bloqueia scripts. O que precisa de JavaScript vai num app isolado (veja Apps).

Um tema só lê dados públicos da loja e o nick e a skin de quem está olhando. Ele nunca vê saldo, e-mail, pedido ou pagamento, e não muda preços: a compra passa por data-hystore-add-to-cart, tratado pela loja.

Pasta

theme.json                 nome, modos de cor, configurações gerais (obrigatório)
layout/theme.liquid        moldura de todas as páginas; precisa mostrar {{ content }} (obrigatório)
pages/<página>.liquid      um arquivo por página (pages/home.liquid é obrigatório)
templates/<página>.json    seções de uma página, em ordem, editáveis em "Personalizar tema"
sections/<nome>.liquid     blocos com um {% schema %}, usados por templates ou {% section %}
components/<nome>.liquid   peças usadas com {% render 'nome' %}
styles/<nome>.css          todos os arquivos são carregados, em ordem de nome
locales/pt.json en.json es.json   textos do filtro | t
config/values.json         valores escolhidos em "Personalizar tema"
assets/<nome>.<ext>        imagens (png, jpg, webp, gif, avif até 3 MB) e fontes (woff, woff2 até 1 MB)
apps/<app>/<arquivo>       apps isolados com JavaScript: html, js, css, json (veja Apps)
  • Nomes: letras minúsculas, números, - e _, um nível dentro de cada pasta (sections/hero.liquid, não sections/home/hero.liquid); apps usam dois (apps/roleta/app.js).
  • Limites: 400 arquivos de texto, 256 KB por arquivo, 5 MB de texto; 100 arquivos de mídia, 40 MB. Arquivos SVG não entram como mídia (<svg> direto no Liquid pode).
  • Páginas: home, shop, category, product, search, news, post, page, players, 404, maintenance. Uma página que o tema não tem é desenhada pelo layout clássico da loja.
Página Endereço Objetos a mais
home /
shop /loja, /loja/<servidor> categories, server (com servidor)
category /loja/<servidor>/<categoria> category, products (48)
product /loja/<servidor>/<categoria>/produto/<slug> product
search /busca?q=... search.query, search.results (48 produtos)
news /noticias
post /noticias/<slug> post
page /p/<slug> page.slug
players /jogadores
404, maintenance

Carrinho (/carrinho), pagamento, perfil (/perfil), compras, senha e entrar (/entrar) são páginas da própria loja: aponte links para elas, nunca as refaça.

theme.json

JSON
{
  "name": "Meu tema",
  "version": "1.0.0",
  "author": "Eu",
  "color_modes": ["dark", "light"],
  "settings": [
    { "id": "accent", "type": "color", "label": "Cor de destaque", "default": "" },
    { "id": "radius", "type": "range", "label": "Arredondamento", "min": 0, "max": 28, "default": 14 },
    { "id": "show_search", "type": "checkbox", "label": "Busca no cabeçalho", "default": true }
  ]
}
  • color_modes: os modos que o tema desenha (dark, light); escuro quando ausente. No modo claro a página não tem a classe dark no <html>: estilize com html:not(.dark) ... ou html.dark ....
  • Tipos de configuração: text, textarea, richtext, image, url, color, number, range (min, max, step), checkbox, select (options: textos ou { "value", "label" }), product, category, server, list (linhas de texto). Até 60 configurações, id em minúsculas.
  • As configurações gerais são {{ settings.<id> }} em qualquer lugar, e cores e números também viram variáveis CSS --theme-<id> na raiz do tema.
  • A cor primária da loja é hsl(var(--primary)) (texto sobre ela: hsl(var(--primary-foreground))).

config/values.json guarda o que o dono da loja escolheu: { "settings": { "accent": "#ff5500" } }. Mantenha esse formato ao editá-lo.

Layout e páginas

layout/theme.liquid envolve todas as páginas e precisa ter {{ content }} onde a página entra. A loja põe uma área obrigatória depois do tema (anúncios nos planos gratuitos, links legais); ela não pode ser escondida nem estilizada.

Uma página é pages/<página>.liquid, templates/<página>.json ou os dois. Com os dois, o arquivo da página põe as seções do template com {{ template_sections }}.

Liquid
{%- comment -%} pages/home.liquid {%- endcomment -%}
<div class="intro">{{ 'home.welcome' | t }}</div>
{{ template_sections }}

Seções

Uma seção é sections/<nome>.liquid, terminando com um {% schema %}:

Liquid
<section class="hero">
  <h1>{{ section.settings.title | default: store.name }}</h1>
  {%- for block in section.blocks -%}
    <p>{{ block.settings.text }}</p>
  {%- endfor -%}
</section>
{% schema %}
{
  "name": "Destaque",
  "settings": [
    { "id": "title", "type": "text", "label": "Título", "default": "" }
  ],
  "blocks": [
    { "type": "line", "name": "Linha", "settings": [{ "id": "text", "type": "text", "label": "Texto" }] }
  ]
}
{% endschema %}
  • section é { id, type, settings, blocks }; as configurações vêm com os padrões. Uma seção vê o que a página dela vê (product, dados carregados...).
  • Um template lista seções (no máximo 40 desenhadas por página):
JSON
{
  "sections": {
    "hero": { "type": "hero", "settings": { "title": "Bem-vindo" } },
    "news": { "type": "latest-news", "disabled": true }
  },
  "order": ["hero", "news"]
}

type é o nome do arquivo em sections/; "disabled": true esconde.

  • {% section 'hero' %} desenha uma seção num lugar fixo (layout, página); os valores vêm de config/values.json em sections.hero.settings.

Componentes

components/<nome>.liquid, usados com {% render 'nome', chave: valor %}. Um componente vê só o que recebe (mais as variáveis globais).

Liquid
{% render 'product-card', product: item %}

Textos em três línguas

locales/pt.json, en.json e es.json guardam textos (objetos aninhados podem). {{ 'nav.shop' | t }} mostra o texto na língua de quem visita, caindo no português e depois na própria chave. Um arquivo de língua faltando é um aviso.

Dados

Em todo lugar:

Objeto Campos
store id, name, slogan, description, logo, ip, type (game ou digital), game (minecraft, hytale ou null), currency
route path, page, params (server_id, category_slug, product_slug, slug), query.q
modules forum, wiki, support, forms, launcher, discord, vip (true quando ligado)
customer logged_in, nick, skin_url
cart count
settings configurações gerais do theme.json
locale pt, en ou es
color_mode dark ou light

Mais dados, quando precisar, com {% load %} (no máximo 24 por página; o mesmo carregamento é buscado uma vez só):

Liquid
{% load products, limit: 8, featured: true as featured %}
{% for p in featured %}{% render 'product-card', product: p %}{% endfor %}
Load Opções Traz
products limit (12), category, server, featured, query lista de produtos
product slug, server um produto ou null
categories server lista de categorias
servers lista de { id, name, url, image, online }
server_status { online, players_online, players_max, version } ou null
news limit (6), page lista de posts
post slug um post ou null
ranking period (month ou all), limit (5) lista de { position, nick, total_spent, orders, skin_url }
top_supporter { nick, total_spent, purchases, skin_url } ou null
goal { target, current, percent, ends_at, rewards } ou null
discord { url, server_id, widget }
launcher { name, version, downloads } ou null
socials mapa instagram, discord, twitter, tiktok, telegram, whatsapp → URL
online_players { total, servers: [{ name, count, max, players }] }
  • Produto: id, name, slug, url, price, original_price, on_sale, image, short_description, description (HTML), duration_days, min_quantity, featured, sold_out, digital, category_slug, server_id.
  • Categoria: id, name, slug, url, description, image, product_count.
  • Post: id, title, slug, url, author, image, excerpt, content (HTML), published_at.

Filtros

Da Hystore:

  • t: texto na língua de quem visita, {{ 'cart.add' | t }}.
  • money: preço na moeda da loja, {{ product.price | money }}.
  • asset_url: endereço de uma mídia enviada, {{ 'logo.webp' | asset_url }}.
  • skin_url: cabeça do Minecraft de um nick, {{ customer.nick | skin_url: 64 }}.
  • raw: só para HTML que a loja já limpou (product.description, post.content). Todo o resto é escapado.

Do Liquid: abs, append, at_least, at_most, capitalize, ceil, compact, concat, date, default, divided_by, downcase, escape, escape_once, first, floor, join, json, last, lstrip, map, minus, modulo, newline_to_br, plus, prepend, remove, remove_first, replace, replace_first, reverse, round, rstrip, size, slice, sort, sort_natural, split, strip, strip_html, strip_newlines, sum, times, truncate, truncatewords, uniq, upcase, url_decode, url_encode, where.

Tags: if/elsif/else, unless, case/when, for (break, continue), assign, capture, echo, liquid, comment, raw, cycle, increment, decrement, tablerow, mais render, section, load, hystore_block, schema.

Blocos prontos

{% hystore_block 'nome', chave: valor %} põe uma peça da loja que precisa de dados ao vivo ou da lógica da loja:

heading, image, button, icon, video, card, testimonial, countdown, banner, spacer, divider, server-hero, products, featured-product, categories, servers, ranking, top-supporter, goal, news, discord, launcher, server-status, server-ip, socials, logo, menu, account-actions, language, color-mode, legal-links, copyright.

O nome vai entre aspas. As opções são texto, números ou true/false.

Peças interativas

Atributos (a loja faz o trabalho, sem JavaScript no tema):

Atributo O que faz
data-hystore-add-to-cart="<id do produto>" põe o produto no carrinho (pede login antes)
data-hystore-open="cart|account|login|search" abre esse lugar
data-hystore-copy="texto" copia o texto (IP do servidor...)
data-hystore-color-mode="toggle|dark|light" troca o modo de cor
data-hystore-language="pt|en|es" troca a língua
data-hystore-cart-count o elemento sempre mostra a quantidade do carrinho
data-hystore-tabs botões [data-hystore-tab="x"] mostram [data-hystore-panel="x"]
data-hystore-accordion (="single" deixa um aberto) cada [data-hystore-accordion-item] tem um [data-hystore-accordion-toggle] e um [data-hystore-accordion-panel]
data-hystore-modal="id" abre #id, que tem data-hystore-modal-panel; data-hystore-modal-close fecha
data-hystore-carousel [data-hystore-slides] rola, [data-hystore-prev]/[data-hystore-next], data-hystore-autoplay="5000"
data-hystore-countdown="2026-12-24T23:59:00Z" preenche [data-hystore-days], [data-hystore-hours], [data-hystore-minutes], [data-hystore-seconds] (ou o próprio texto)
data-hystore-animate ganha data-visible quando aparece na tela (anime no CSS)

Elementos que a loja preenche: <hystore-cart-count>, <hystore-account> (entrar ou o menu da conta; com data-cart mostra também o botão do carrinho da loja), <hystore-server-status>, <hystore-online-players>, <hystore-search data-placeholder="...">, <hystore-skin data-nick="Steve" data-size="128">, <hystore-video data-youtube="<id ou link>">, <hystore-app data-app="<nome>"> (um app isolado, veja Apps).

Links dentro da loja (<a href="/loja">) trocam de página sem recarregar; páginas de conta e pagamento sempre abrem como página nova.

Imagens e fontes

Ponha em assets/ (a ferramenta envia; o servidor confere os bytes).

Liquid
<img src="{{ 'banner.webp' | asset_url }}" alt="{{ store.name }}">
CSS
@font-face { font-family: "Display"; src: url(assets/display.woff2) format("woff2"); font-display: swap; }
.hero { background-image: url(assets/banner.webp); }

Apps

Um app é uma pequena página com JavaScript (calculadora, roleta de prêmios, mini jogo, animação) que o tema coloca com <hystore-app data-app="roleta"></hystore-app>. Ele fica em apps/<app>/ e começa pelo index.html:

apps/roleta/index.html   a página (obrigatória)
apps/roleta/app.js       o script
apps/roleta/app.css      o estilo
apps/roleta/app.json     { "name": "Roleta de prêmios", "height": 420, "permissions": ["customer"] }

Ele roda isolado, num quadro em outro domínio (hystoreapps.cloud): sem rede (fetch, XHR e WebSocket bloqueados), sem cookies nem armazenamento, sem formulários, sem janelas novas, e não consegue mudar nem navegar a página da loja. Ele fala com a loja só pela ponte:

HTML
<script src="/sdk/v1.js"></script>
<script src="app.js"></script>
JavaScript
hystore.products({ category: 'vips', limit: 6 }).then(function (products) {
  // [{ id, name, url, price, priceText, originalPrice, onSale, image, shortDescription, ... }]
});
hystore.categories();                    // categorias ativas
hystore.addToCart(productId, 1);         // 'added', ou 'login' (a loja pede para entrar)
hystore.navigate('/loja');               // uma página da loja
hystore.open('cart');                    // 'cart' | 'login' | 'account' | 'search'
hystore.context();                       // { locale, colorMode, store: { name, currency } }
hystore.customer();                      // { loggedIn, nick, skinUrl }, só com "customer" no app.json
hystore.resize(600);                     // ou hystore.autosize() para seguir a página
  • Tudo é dado público: o preço vem da loja, e o carrinho recebe só o id do produto. Páginas de dinheiro e de conta nunca mostram apps.
  • Carregue só os arquivos do próprio app e o /sdk/v1.js; scripts e estilos de outros sites, quadros e formulários são recusados ao salvar. Imagens podem ser imagens data: ou as imagens e fontes do tema pelo endereço completo (https://theme-assets.hystore.com.br/..., como GET /v1/theme/assets e a ferramenta list_assets do MCP informam).
  • app.json é opcional: height de 40 a 2000 pixels (320 se faltar), name para leitores de tela, permissions só customer.
  • Limites: 10 apps por tema, 20 arquivos e 512 KB por app.
  • Mensagens demais são ignoradas; pôr no carrinho e trocar de página têm limites próprios, pequenos.

Antes de publicar

  • npx @hystudios/hystore theme check sem erros.
  • Todo texto que o visitante lê passa por | t, em pt, en e es.
  • O tema funciona nos modos de cor que declara e no celular.
  • Os links de carrinho, conta e login apontam para /carrinho, /perfil e /entrar (ou usam data-hystore-open).