🎨 Plano de Migração de Tema: Cayman → Minimal Mistakes

Substituição do tema Jekyll do portal, com testes de paridade, critérios de aceite por fase e plano de rollback

💡 Objetivo: Substituir o tema pages-themes/cayman (landing page de projeto único, sem navegação hierárquica) pelo minimal-mistakes-jekyll, resolvendo os problemas estruturais de UI/UX identificados na auditoria de 2026-08-31/09-01 — sem perder nenhum recurso já construído (Mermaid, MathJax, alertas GitHub, quizzes, admonitions, cards, botões, code blocks) e sem quebrar as ~4.000 páginas do portal em produção.


📊 1. Contexto e decisão

1.1 Problema diagnosticado no tema atual (Cayman)

Problema Evidência
Hero fixo de ~250px repetido em toda página Screenshot mobile: 30% da viewport antes de qualquer conteúdo
Sem navegação persistente entre capítulos de um curso Aluno depende de botões Anterior/Próximo (JS frágil, faz scraping de index.html)
Sem TOC fixo (sticky) na página TOC inline no topo do conteúdo, some ao rolar
<li> renderiza menor que <p> (14.4px vs 17.6px) Medido via getComputedStyle
Código inline em 12.9px Medido via getComputedStyle
Bug de busca: snippet mostra código Mermaid cru Reproduzido buscando “docker”

1.2 Candidatos avaliados

Tema Sidebar escalável p/ 40 cursos TOC sticky nativo Mermaid nativo Bugs de compatibilidade Mantido ativamente
Cayman (atual)
Just the Docs (com Collections + nav_fold) nenhum
Minimal Mistakes ⚠️ nativo não, mas resolvido com JS de ~20 linhas (ver §3.2) ✅ nativo título duplicado, <li> pequeno, string pt-BR quebrada
Chirpy ❌ (tema de blog, não de docs) ✅ nativo math: true quebra $ usados como moeda; URLs de post datado incompatíveis

Decisão: Minimal Mistakes. Motivo decisivo: testado o comportamento real do aluno (entra, escolhe 1 curso, fica alternando capítulos, raramente troca de curso) — o nav_fold do Just the Docs reseta a sidebar a cada página de capítulo (confirmado ao vivo clicando em “Próximo Capítulo”), errando exatamente na ação mais repetida. O Minimal Mistakes, com um JS de auto-colapso por URL (não por estado salvo), acerta essa ação em 100% dos casos, e seus 3 bugs de compatibilidade são todos superficiais (CSS, string de tradução, ordem de DOM) — já resolvidos e testados no laboratório.

Ver detalhamento completo da investigação na memória de sessão do agente (não versionada no repo): comparação de 6 configurações de tema, testes em 3 tamanhos de tela, bateria de paridade de recursos.


🗺️ 2. Escopo


🏗️ 3. O que muda tecnicamente

3.1 _config.yml e Gemfile

# Gemfile
gem "minimal-mistakes-jekyll", "~> 4.26"
gem "jekyll-include-cache"
# _config.yml
theme: minimal-mistakes-jekyll
minimal_mistakes_skin: "dark"    # ou o skin escolhido — decisão de design separada
locale: "pt-BR"
defaults:
  - scope: { path: "", type: "pages" }
    values: { layout: single, author_profile: false, toc: true, toc_sticky: true }

_layouts/ e _includes/head-custom.html (Cayman) são removidos — o tema passa a fornecer os próprios. Toda a customização sobrevive em um único arquivo novo: _includes/head/custom.html (hook oficial do Minimal Mistakes).

3.2 Sidebar escalável por curso (o maior item de engenharia novo)

Cada um dos 40 módulos vira uma entrada de topo em _data/navigation.yml, com seus capítulos aninhados. Um script gera esse arquivo a partir dos topicos/index.md de cada módulo (mesma fonte da verdade já usada pelos índices do site hoje).

JS (_includes/head/custom.html) detecta o curso atual pela URL e colapsa todos os outros — testado e funcionando com 4 cursos simulados; precisa reteste de performance com os 40 reais antes do rollout completo (ver §6, Risco R1).

3.3 Inventário completo de correções a portar

# Correção Onde Testado
1 Sidebar auto-colapsada por curso (via URL, não por sessão) head/custom.html (JS) ✅ 4 cursos simulados
2 Remoção do H1 duplicado (tema já renderiza page.title) Script sobre os .md fonte ✅ 120 arquivos de teste
3 <li>/<ul>/<ol> do conteúdo em 1rem (não herdar encolhimento) head/custom.html (CSS)
4 Tradução pt-BR do menu mobile (“Chavear menu” → “Abrir menu”) _data/ui-text.yml (bloco pt-BR: completo)
5 TOC nativo reordenado pro fim do artigo abaixo de 768px head/custom.html (JS)
6 Código e nome do curso na sidebar pequenos no mobile (12px/11.25px → 14px/13.6px, via rem que já escala com a tipografia fluida do tema) head/custom.html (CSS) ✅ 3 telas
7 Mermaid (conversão pre code.language-mermaiddiv.mermaid) head/custom.html (JS, idêntico ao Cayman)
8 MathJax ($...$, incluindo \$ escapado para valores monetários) head/custom.html (script, idêntico ao Cayman)
9 Alertas estilo GitHub ([!NOTE], [!TIP], [!WARNING]…) head/custom.html (JS, idêntico ao Cayman)
10 SVG cru embutido em Markdown Nenhuma ação — kramdown já repassa direto
11 Admonição estilo MkDocs (.admonition) head/custom.html (CSS)
12 Botões estilo MkDocs-Material (.md-button) head/custom.html (CSS)
13 Cards (.course-card/.cards-grid) head/custom.html (CSS)
14 Código: badge de linguagem + botão “Copiar” head/custom.html (JS, idêntico ao Cayman) ✅ clique real testado
15 Quiz interativo (.quiz-container) head/custom.html (JS, idêntico ao Cayman) ✅ clique real testado

3.4 Front matter — mudança em massa (o item mecânico mais volumoso)

Todo arquivo de conteúdo precisa trocar layout: defaultlayout: single + sidebar: {nav: "<slug-do-modulo>"}. Achado da rodada de testes: isso vale para todos os tipos de arquivo (topicos, exercicios, exemplos, quizzes — não descobri necessidade em slides, que usa layout próprio e fica fora desta migração por ora). Script único, idempotente, roda sobre os 40 módulos de uma vez.


🚦 4. Fases de execução

flowchart TD
    F0["Fase 0: Preparação\nScripts de migração + geração de navigation.yml"]
    F1["Fase 1: Piloto (1 módulo)\nmod_11 em branch isolada, site atual intacto"]
    F2["Fase 2: Validação do piloto\nBateria de testes completa (Seção 5)"]
    F3["Fase 3: Rollout gradual\n+5-10 módulos por lote, gate a cada lote"]
    F4["Fase 4: Rollout completo\n40/40 módulos + corte do Cayman"]
    F5["Fase 5: Estabilização\nMonitorar CI/analytics por 1-2 semanas"]

    F0 --> F1 --> F2 -->|passou nos critérios de aceite| F3 --> F4 --> F5
    F2 -.->|falhou| ROLLBACK["Rollback (Seção 7)"]
    F3 -.->|falhou em algum lote| ROLLBACK

    style F0 fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style F1 fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style F2 fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style F3 fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
    style F4 fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
    style F5 fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style ROLLBACK fill:#ffebee,stroke:#f44336,stroke-width:2px

Fase 0 — Preparação (sem tocar no site em produção)

  1. Criar branch theme/minimal-mistakes.
  2. Script gerar_navigation_yml.py: lê mod_XX/topicos/index.md de cada módulo, gera a entrada correspondente em _data/navigation.yml.
  3. Script migrar_front_matter.py: aplica layout: single + sidebar + remove H1 duplicado, com flag --modulo para rodar em 1 módulo por vez (obrigatório na Fase 1) ou --todos (só na Fase 3+).
  4. Montar _includes/head/custom.html definitivo com os 15 itens da tabela §3.3.
  5. Copiar _data/ui-text.yml (bloco pt-BR: corrigido).

Fase 1 — Piloto em 1 módulo

Fase 2 — Validação (gate obrigatório antes de prosseguir)

Ver bateria completa na Seção 5. Só avança para a Fase 3 se 100% dos itens passarem.

Fase 3 — Rollout gradual — ✅ CONCLUÍDA 2026-09-01

Executada em 4 lotes de ~10 módulos, cada um com dry-run + git diff --stat revisado (uniformidade sem outliers) + validate_liquid.py + push + CI real (build + test_links.py) antes de avançar pro próximo:

Resultado: 40/40 módulos com layout: single + sidebar, confirmado por varredura de front matter (zero arquivos dentro do escopo ainda em layout: default — só sobrou nos index.md de subdiretório, slides/, e pastas fora de escopo como projetos/, _legado/, _artefatos/, todos deliberados). CI verde em todos os 4 lotes, ainda 100% em branch (theme/minimal-mistakes), zero exposição em produção.

⚠️ Nota de arquitetura: como o tema é definido uma vez em _config.yml para o site inteiro, não é possível migrar módulo por módulo em produção — todo módulo do site passa a usar o novo tema no mesmo deploy. O “rollout gradual” da Fase 3 é sobre preparar e validar os lotes numa branch de trabalho, não sobre expor parcialmente aos alunos. O corte é único (Fase 4), mas o trabalho de preparação é incremental e cada lote é auditado antes do próximo.

Fase 4 — Corte completo — ✅ CONCLUÍDA 2026-09-01

Site 100% no Minimal Mistakes em produção. Cayman preservado na branch main anterior ao merge (cc326ecf) para referência de rollback (Seção 7).

Fase 5 — Estabilização (em andamento)


🧪 5. Plano de testes

5.1 Bateria por módulo (repetir a cada lote da Fase 3, obrigatória na Fase 2)

Categoria O que testar Como
Sidebar Curso atual expandido, todos os outros colapsados, em qualquer página do módulo (não só na home do módulo) Navegar direto por URL pra 3 páginas diferentes do módulo (topo, meio, fim da lista de capítulos)
TOC Sticky no desktop/tablet, movido pro fim do artigo no mobile 3 telas (ver 5.2)
Tipografia <p>/<li> iguais; código ≥14px no mobile; nome do curso na sidebar ≥13px no mobile getComputedStyle nas 3 larguras, não só visual
Mermaid Diagramas renderizam (não aparece código cru) Abrir um capítulo com flowchart/sequenceDiagram
MathJax $...$ e \$ escapado renderizam sem quebrar texto ao redor Abrir um capítulo com fórmula ou valor monetário em LaTeX
Alertas GitHub [!NOTE]/[!TIP]/[!WARNING] viram caixa colorida Buscar um arquivo com esse padrão (grep -rl "\[!NOTE\]")
Quiz Clique numa alternativa errada → marca vermelho, marca a certa em verde, mostra feedback Clique real, não só inspeção visual
Code blocks Badge de linguagem (some em text/plaintext), botão “Copiar” (clipboard real), ligatures desligadas (->, == não viram glifo único), código dentro de <details> funciona igual Clicar em “Copiar” de verdade e checar navigator.clipboard; abrir um <details> de gabarito
Título Sem duplicação (só 1 <h1> visível) Inspeção visual + document.querySelectorAll('h1').length
Busca Resultado não mostra código Mermaid cru no snippet Buscar um termo que aparece num capítulo com diagrama
Links quebrados Zero links quebrados após a mudança de layout _scripts/test_links.py no build completo do lote

5.2 Bateria de telas (rodar pelo menos 1x por lote, não em toda página)

Tela Largura real alvo O que confirmar
📱 Mobile ~650-660px Sidebar vira botão “Abrir menu” (texto correto, não “Chavear menu”); TOC no fim do artigo; sem hambúrguer redundante
📟 Tablet ~800-1100px Sidebar completa visível sem hambúrguer sobrando; TOC lateral OU no fim (decisão de design já tomada: abaixo de 768px vai pro fim)
🖥️ Desktop >1400px Sidebar + conteúdo + TOC lateral, os 3 simultâneos

Medir sempre via window.innerWidth real (não confiar só no valor pedido ao redimensionar a janela — a ferramenta de automação usada nesta investigação apresentou inconsistência entre o valor solicitado e o efetivo).

5.3 Critérios de aceite da Fase 2 (gate)

A Fase 3 só começa se, no módulo piloto:

⚠️ Ressalva achada na bateria de telas: em ~984px (faixa “tablet” definida em §5.2), a sidebar do Minimal Mistakes continua escondida atrás do botão “Abrir menu” — o breakpoint nativo do tema pra virar sidebar persistente é mais alto que a faixa 800-1100px assumida no plano (o TOC, que usa um breakpoint customizado em 768px definido em head/custom.html, já volta pro lado corretamente nessa mesma largura). Não é um bug — é o comportamento padrão do tema — mas é uma divergência da expectativa original. Decisão adiada pra Fase 3: aceitar o breakpoint nativo do MM ou customizá-lo via CSS pra abrir mais cedo.

🐛 Bug real achado e corrigido nesta rodada: 5 capítulos no site (Vue <script setup>/<Transition>, C <string.h>, C#/Java List<T>/<T>) têm títulos com sintaxe literal de tag HTML. gerar_navigation_yml.py gravava isso cru no navigation.yml; o template de sidebar do tema injeta sem escapar, e o navegador interpretava <script setup> como uma tag <script> de verdade, engolindo o resto da página como “código” (tela em branco + SyntaxError no console). Corrigido escapando &/</> no gerador e no JS da busca global (mesmo risco, via search.json).


⚠️ 6. Riscos conhecidos e mitigação

# Risco Mitigação
R1 Tempo de build do Jekyll com _data/navigation.yml de 40 cursos × 20 capítulos (~760+ links) não foi medido em escala real — só com 4 cursos simulados FECHADO 2026-09-01: medido no CI real (Ubuntu, PR #2, run 33525061322) com os 40 módulos/800 capítulos reais: 45.2 segundos. Bem dentro de qualquer timeout razoável — não precisa excluir categorias da navegação. Build local via Docker no Windows trava de forma consistente (I/O do bind mount com ~4.600 arquivos); usar sempre o CI real como medição autoritativa
R2 Front matter de ~4.000 arquivos alterado em massa — risco de regex genérico corromper conteúdo (já aconteceu uma vez nesta investigação: regex gulosa com re.DOTALL apagou um capítulo inteiro) Script de remoção de H1 sempre isola o front matter primeiro com uma expressão, e só depois aplica uma segunda expressão sem DOTALL no corpo; rodar em 1 módulo, git diff revisado manualmente, antes de rodar em todos. Validado no piloto do mod_11: 81 arquivos migrados, diff uniforme (5 linhas alteradas por arquivo, sem outliers)
R3 Sidebar com 40 cursos pode reintroduzir o problema de escala do Minimal Mistakes se o JS de auto-colapso falhar silenciosamente em algum caso de borda (ex: módulo com nome/URL atípica) Testar o JS especificamente com nomes de módulo que têm caracteres especiais/emoji na URL antes do rollout completo. Achado novo no piloto (não previsto aqui): títulos de capítulo com sintaxe de tag HTML literal (<script setup>, List<T>) quebravam a sidebar inteira a partir daquele item — corrigido escapando &/</> em gerar_navigation_yml.py. Nomes de módulo com emoji (usados em todos os 40) já testados e ok
R4 minimal_mistakes_skin (dark) pode não bater exatamente com a paleta de cores atual do site Decisão de design separada, não bloqueia a migração técnica — pode ficar pendente pra um ajuste fino pós-corte. Decisão tomada: skin dark fixo, sem toggle runtime (o site tinha um botão de alternância clara/escura no Cayman que não tem equivalente nativo no MM; portá-lo exigiria estilizar o chrome do tema do zero)
R5 Comentários/analytics/qualquer integração amarrada a classes CSS específicas do Cayman Auditar assets/css/style.scss e _includes/head-custom.html atuais por qualquer seletor não coberto na tabela §3.3 antes do corte final. Auditoria feita no piloto — achou 2 gaps reais que a tabela §3.3 não cobria (porque a investigação original só comparou head-custom.html, não o _layouts/default.html inteiro): busca global (Ctrl+K, modal, consome /search.json) e botão flutuante “voltar ao topo” — ambos portados. Botões Anterior/Próximo (scraping frágil de index.html) e o TOC manual por JS ficam obsoletos de propósito, substituídos pelos nativos do MM. Breadcrumb customizado do Cayman também não foi portado (ver achado da Fase 2 na §5.3) — o nativo do MM foi tentado primeiro e desativado por gerar link quebrado
R6 Perda de trabalho não commitado durante a migração Toda a Fase 0-3 acontece em branch dedicada (theme/minimal-mistakes); nenhum commit direto em main até a Fase 4

⏮️ 7. Plano de rollback

7.1 Rollback durante as Fases 0-3 (nada em produção ainda)

Trivial — a branch theme/minimal-mistakes simplesmente não é mergeada. Zero impacto em produção. Pode ser abandonada ou retomada a qualquer momento.

7.2 Rollback após a Fase 4 (corte já em produção)

# Reverte o commit de merge do corte (assumindo commit único de merge)
git revert -m 1 <hash-do-commit-de-merge>
git push origin main

7.3 Rollback parcial (1 módulo com problema, resto ok)

Se só um módulo específico apresentar problema depois do corte completo:


📎 8. Referências