Introdução

Este post explica em detalhe como configurei o sistema de comentários do blog usando Giscus (frontend) com GitHub Discussions (backend) sobre uma base Hugo + PaperMod. É um complemento prático ao post de setup inicial, com justificativas, pré-requisitos, passos reproduzíveis e a solução adotada para sincronizar o tema do Giscus com o toggle de tema do PaperMod.

O objetivo é que você consiga replicar o mesmo fluxo no seu repositório e entender as decisões e gargalos que enfrentei.

Por que usar Giscus + GitHub Discussions?

  • comentários ficam no GitHub (sem banco de dados adicional)
  • autenticação via GitHub (menor atrito para desenvolvedores)
  • suporte a Markdown, reações e moderação via GitHub
  • integração leve e moderna para sites estáticos (Cloudflare Pages compatível)
  • histórico e moderação centralizados no repositório/organização

Contra-argumentos e quando não usar:

  • se você precisa de perfis anônimos ou autenticação fora do GitHub, Giscus não é ideal;
  • se quiser evitar depender do GitHub para comentários (privacidade/lock-in), escolha outra solução;
  • para sites com alto tráfego/conteúdo sensível, avalie requisitos de moderação e rate limits.

Pré-requisitos

  • repositório GitHub com Discussions habilitado
  • ser administrador do repositório (para instalar o GitHub App do Giscus)
  • Hugo + PaperMod funcionando (veja config/_default/hugo.toml)

No seu repositório, ative Discussions em: Settings > Features > Discussions.

Instalar e autorizar o Giscus

  1. Instale o GitHub App do Giscus: https://github.com/apps/giscus
  2. Conceda acesso ao repositório onde os comentários devem ser armazenados
  3. Crie a categoria em Discussions (ex.: “Blog Comments”)

Sem essa etapa o Giscus não consegue criar discussions automaticamente.

Gerar a configuração inicial

Use o configurador oficial em https://giscus.app para gerar os parâmetros iniciais. As opções importantes:

  • Repository: usuário/repo (ex.: meu-usuario/meu-repo)
  • Mapping: pathname (recomendado: URL estável)
  • Category: a categoria criada em Discussions
  • Theme / lang / reactions conforme necessidade

O configurador retorna um snippet <script> com data-* attributes, vamos parametrizar isso no Hugo em vez de colar direto no template.

Estrutura no Hugo (recomendo)

Centralize a configuração em config/_default/hugo.toml (ou hugo.yaml) dentro de [params.giscus]. Exemplo (já presente neste repositório):

[params]
  comments = true
  [params.giscus]
    repo = "bortoloso/bortolog"
    repoId = "R_kgDOSet8pA"
    category = "Blog Comments"
    categoryId = "DIC_kwDOSet8pM4C9LyN"
    mapping = "pathname"
    strict = "0"
    reactionsEnabled = "1"
    emitMetadata = "0"
    inputPosition = "bottom"
    theme = "light"
    lang = "pt"
    loading = "lazy"

Essa abordagem facilita manutenção, permite trocar valores por ambiente e evita duplicação nos partials.

Verifique os valores atuais em config/_default/hugo.toml.

O problema do tema (PaperMod x Giscus)

O PaperMod aplica o tema (dark/light) usando três fontes:

  1. localStorage.getItem("pref-theme")
  2. document.documentElement.dataset.theme
  3. window.matchMedia('(prefers-color-scheme: dark)')

Já o Giscus decide o tema somente no momento do carregamento do script. Resultado observado:

  • o blog pode estar em dark mode (localStorage), mas o Giscus inicia com o tema do sistema;
  • ao trocar o tema do blog, o iframe do Giscus não acompanha automaticamente.

Isso gera inconsistência visual e confusão para usuários.

Solução adotada (partial dinamicamente carregado e atualização por postMessage)

A solução foi mover a renderização do script para um partial Hugo (layouts/partials/comments.html) que:

  • lê os parâmetros de Site.Params.giscus;
  • determina o tema atual consultando localStorage, document.documentElement.dataset.theme e prefers-color-scheme;
  • injeta o script do Giscus com data-theme correto;
  • adiciona um listener para detectar toggle do PaperMod e enviar postMessage ao iframe do Giscus para atualizar o tema.

Trecho do partial (reduzido), já aplicado no blog:

{{ if and .Site.Params.comments (not .Params.disableComments) }}
<div id="giscus-container"></div>
<script>
(function () {
  function getTheme() {
    const storedTheme = localStorage.getItem("pref-theme");
    if (storedTheme === "dark" || storedTheme === "light") return storedTheme;
    const htmlTheme = document.documentElement.dataset.theme;
    if (htmlTheme === "dark" || htmlTheme === "light") return htmlTheme;
    return window.matchMedia('(prefers-color-scheme: dark)').matches ? "dark" : "light";
  }

  function renderGiscus() {
    const container = document.getElementById("giscus-container");
    if (!container || container.hasChildNodes()) return;
    const script = document.createElement("script");
    script.src = "https://giscus.app/client.js";
    script.async = true;
    script.crossOrigin = "anonymous";
    script.setAttribute("data-repo", "{{ .Site.Params.giscus.repo }}");
    script.setAttribute("data-theme", getTheme());
    // ... outros atributos ...
    container.appendChild(script);
  }

  function updateGiscusTheme() {
    const iframe = document.querySelector("iframe.giscus-frame");
    if (!iframe) return;
    iframe.contentWindow.postMessage({ giscus: { setConfig: { theme: getTheme() } } }, "https://giscus.app");
  }

  renderGiscus();
  document.addEventListener("click", function (e) {
    const toggle = e.target.closest("#theme-toggle");
    if (!toggle) return;
    setTimeout(updateGiscusTheme, 150);
  });
})();
</script>
{{ end }}

Observações:

  • #theme-toggle é o seletor do botão do PaperMod neste tema; ajustei o timeout para permitir que o PaperMod atualize o localStorage antes de enviar a mensagem.
  • o postMessage para https://giscus.app faz com que o iframe atualize o tema sem precisar recarregar a página.

Configuração detalhada e reprodução passo a passo

  1. Habilite Discussions e crie a categoria (ex.: “Blog Comments”).
  2. Instale o GitHub App do Giscus no repositório.
  3. Copie os valores do configurador do Giscus (repo, repoId, category, categoryId, etc.) para config/_default/hugo.toml em [params.giscus].
  4. Crie/edite layouts/partials/comments.html com o parcial dinâmico (modelo acima).
  5. Teste localmente:
hugo server -D
# abra http://localhost:1313 e verifique comentários e tema
  1. Ajuste theme em [params.giscus] para comportamento padrão (ex.: light), o partial fará override dinâmico.

Testes e validação

  • Verifique que a discussion é criada ao visitar um post pela primeira vez.
  • Troque o tema no toggle do PaperMod e confirme que o iframe do Giscus muda para dark/light sem recarregar a página.
  • Se usar data-loading="lazy", teste em um post com muitos comentários para garantir que o script seja injetado apenas uma vez.

Problemas comuns e resolução

  • Giscus não cria discussion: verifique se o App Giscus tem permissão e se repoId/categoryId estão corretos.
  • Tema incorreto no Giscus: confirme que o localStorage armazena pref-theme e que o seletor do toggle (#theme-toggle) existe no tema.
  • Partial duplicado/carregado duas vezes: verifique se o container tem child nodes antes de injetar o script.
  • CORS / postMessage silencioso: assegure que o targetOrigin é https://giscus.app ao usar postMessage.

Privacidade e moderação

  • Comentários estão no GitHub, política de privacidade do GitHub se aplica.
  • Moderação (fechar discussion, excluir comentários) é feita no GitHub por quem tem permissão na categoria.
  • Se quiser comentários anônimos, Giscus não é a melhor escolha.

Conclusão e recomendações rápidas

  • Giscus + GitHub Discussions é uma solução prática para blogs técnicos hospedados em Pages/Netlify/Vercel.
  • Parametrize via config/_default/hugo.toml e renderize com um partial dinâmico, isso mantém o código limpo e reprodutível.
  • A sincronização de tema via localStorage + postMessage resolve a maior dor observada com PaperMod.
  • Existem diversas outras opções para habilitar comentários em posts, usei o Giscus apenas para permitir, de alguma forma, um meio de interação com as postagens.