[Pitch] Construí um widget de changelog em menos de 15KB usando Vanilla JS e Shadow DOM — e por quê não usei React
Fala, pessoal.
Acabei de lançar o Pushdock, uma ferramenta de changelog
para founders de SaaS. Quero compartilhar algumas decisões
técnicas que tomei durante o build que podem ser úteis
para quem está construindo widgets embeddáveis.
O CONTEXTO
Algumas ferramentas de changelog como o Beamer têm um widget
embeddável que os clientes instalam no próprio site.
O problema: alguns desses widgets podem pesar até 200KB. Isso impacta
diretamente o Core Web Vitals do site do cliente —
especialmente o LCP e o TBT.
Minha meta era diferente: menos de 15KB, zero dependências,
instalação com uma única linha de script.
POR QUE NÃO USEI REACT
A primeira decisão foi não usar React no bundle do widget.
React + ReactDOM sozinhos somam ~45KB gzipados.
Antes de escrever qualquer lógica do produto,
já estaria 3x acima da minha meta.
A solução foi Vanilla JS puro. Manipulação direta do DOM,
event listeners nativos, fetch API. Sem abstração,
sem overhead de reconciliação, sem virtual DOM.
O resultado: 11.8KB gzipados no momento do lançamento.
SHADOW DOM PARA ISOLAMENTO DE CSS
O segundo problema de widgets embeddáveis é o CSS.
Quando você injeta HTML no site de outra pessoa,
o CSS dela vaza para dentro do seu widget,
e o CSS do seu widget vaza para fora.
O resultado são estilos quebrados, z-index infinitos
e bugs impossíveis de reproduzir.
A solução é o Shadow DOM — uma feature nativa do browser
que cria um escopo completamente isolado:
`const host = document.createElement('div')
document.body.appendChild(host)
const shadow = host.attachShadow({ mode: 'open' })
shadow.innerHTML = `
O CSS dentro do shadow root não afeta o documento principal,
e o CSS do documento principal não afeta o shadow root.
Zero conflito, independente do site do cliente.
Suporte nos browsers: 96%+ globalmente.
Pode usar em produção sem preocupação.
INSTALAÇÃO COM UMA LINHA
A meta de UX era que o cliente instalasse o widget
sem precisar de npm, build step ou arquivo de configuração:
<script src="https://pushdock.app/changelog.js" data-project="seu-slug" data-position="bottom-right" data-accent="#6366f1"> </script>
O script carrega de forma assíncrona (não bloqueia o render),
lê os data attributes para configuração, cria o shadow DOM
e renderiza o widget. Tudo em menos de 50ms numa conexão 3G.
O QUE O PRODUTO FAZ
Além do widget, o Pushdock tem:
→ Página pública de changelog (SSG com Next.js 15)
→ Página pública de roadmap (SSG com Next.js 15)
→ Geração de posts com IA via OpenAI
→ Notificações por email para subscribers (double opt-in)
→ Integrações com Github Releases, Slack e Zapier (outras estão à caminho)
→ RSS feed automático
→ Flat pricing por conta — sem cobrança por MAU
É uma alternativa ao Beamer e AnnounceKit focada em
founders de SaaS que querem algo leve, simples e com
preço previsível.
Acabei de lançar hoje com zero clientes pagantes.
Se quiserem testar: https://pushdock.app
PERGUNTAS QUE ANTECIPO
P: Por que não usou um Web Component com Custom Elements?
R: Shadow DOM sem Custom Elements é suficiente para o
caso de uso e tem compatibilidade ligeiramente melhor.
Custom Elements adicionaria complexidade sem benefício real.
P: Como você faz o cache do widget?
R: O changelog.js / roadmap.js tem Cache-Control de 1 hora no CDN da Vercel.
A lista de posts da API tem revalidate de 60 segundos
no Next.js. O badge de "não lidos" usa localStorage
para persistir o timestamp da última visita.
P: Qual foi o maior desafio técnico?
R: Z-index. Mesmo com Shadow DOM, o widget precisa
aparecer acima de outros elementos do site do cliente.
A solução foi um z-index alto (9999) no host element
fora do shadow root, não dentro dele.
Aberto para perguntas técnicas sobre a implementação.
Se alguém tiver curiosidade sobre alguma parte específica
do código, posso detalhar nos comentários.
— Luiz
https://pushdock.app