Pular para o conteúdo

Grade de posts incorporável (widget)

Uma tag <script> que renderiza uma grade dos seus posts publicados dentro de uma página que você não controla — o site de um parceiro, uma landing page, um app shell. Ela lê a API pública de leitura e monta o DOM por conta própria. Sem iframe.

O bundle tem 4,4 KB com gzip e não carrega mais nada.

  1. Crie uma chave pública de leitura.

    No admin, abra Configurações → Integrações API → Criar chave, escolha Pública de leitura (pk_) e registre as origens onde o widget pode rodar (por exemplo https://parceiro.com e https://www.parceiro.com). O token pk_… é exibido uma única vez.

  2. Cole o snippet.

    <div data-canverly-grid data-key="pk_YOURKEY" data-limit="6"></div>
    <script src="https://docs.canverly.com/widget/v1/grid.js"
    integrity="sha384-pHV9vZdn2P2/wjNayBRVklmrBLFKkPT5Hz6ZAHiodvsxUo4C9Vr5pbxdIbje3MEF"
    crossorigin="anonymous" defer></script>

    Mantenha os atributos integrity e crossorigin. São eles que fazem o navegador recusar o arquivo se um único byte dele algum dia divergir do que você auditou.

  3. Confira a origem.

    Se a grade não aparecer, abra o console. [canverly-grid] origem não está na allowlist da chave significa que a origem da página não está registrada na chave — adicione-a no admin. Nesse caso a API responde 403: a chave é válida, a página de onde ela é usada é que não é.

Atributo Padrão Significado
data-key — Obrigatório. A chave pk_…. data-site é aceito como alias.
data-limit 6 1–50. Valores fora do intervalo são ajustados localmente, com um aviso no console.
data-category — Slug de categoria.
data-tag — Slug de tag.
data-language — Tag BCP-47, ex.: pt-BR.
data-locale <html lang> ou pt-BR Locale para datas e rótulos.
data-link-target _self Use _blank para abrir os posts em uma nova aba (os links sempre levam rel="noopener noreferrer").
data-empty-text localizado Mensagem exibida quando o site não tem posts correspondentes.
data-api https://api.canverly.com Base da API. Só https: (ou localhost) é aceito.

Várias grades podem conviver na mesma página; cada elemento [data-canverly-grid] é renderizado de forma independente. O estado atual é exposto como data-state: loading, ready, empty ou error.

Se a página hospedeira envia uma CSP, ela precisa de três origens:

script-src https://docs.canverly.com
connect-src https://api.canverly.com
img-src https://canverly.b-cdn.net

Os estilos não exigem mudança na CSP: o widget traz o CSS dentro do bundle e o aplica por meio de uma constructable stylesheet, que style-src 'self' não bloqueia. Só o fallback legado carrega grid.css como <link>, e esse precisa de style-src https://docs.canverly.com. Toda classe tem o prefixo cvw-, então nada vaza para os estilos da página hospedeira.

O widget nunca atribui HTML. Todo valor que vem da API entra por createElement + textContent, e toda URL tem o esquema verificado antes de chegar ao DOM. Um post com o título <script>alert(1)</script> é renderizado como esse texto literal, de forma visível — ele não executa e não some silenciosamente. Isso é garantido por testes que verificam o DOM resultante (nenhum nó <script>, nenhum <img> extra, o nó do título tem zero elementos filhos), além de um teste de mutação que falha se alguém trocar textContent por innerHTML.

  • 600 requisições/minuto por chave, mais 60 requisições/minuto por (chave, IP) para chaves públicas. A dimensão por IP existe para que um único visitante hostil não consiga esgotar a cota de todas as páginas que incorporam a sua grade.
  • Uma visualização de página = uma requisição. O widget renderiza uma única página de resultados e ignora next_cursor de propósito.
  • Em qualquer falha, o widget não renderiza nada visível e registra no console. Ele nunca lança erro para a página hospedeira.

A versão está no caminho, e os bytes em um caminho publicado nunca mudam. /widget/v1/grid.js será sempre o arquivo cujo hash está acima; um build novo é publicado em um caminho novo. É isso que permite ao snippet fixar o integrity e o que nos permite servir o arquivo com cache imutável de um ano. Se algum dia lançarmos uma mudança incompatível, ela vira /widget/v2/grid.js e esta página ganha um snippet novo — o que você colou continua funcionando.

Os hashes atuais são publicados ao lado do arquivo em /widget/v1/INTEGRITY.txt.