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) pelominimal-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.
| 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” |
| 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.
mod_XX_*, spec_*, extra_*, proj_*), a maioria já no “Padrão Ouro” de 20 capítulos × 5 categorias (topicos, exercicios, exemplos, quizzes, slides) = até 100 arquivos por módulo. ⚠️ Número contado por listagem de diretório em 2026-09-01 — não é estático: há sessões paralelas criando módulos novos ativamente (extra_guia_de_linguagens_de_programacao apareceu entre duas contagens no mesmo dia). Reconferir com ls -d mod_* spec_* extra_* proj_* imediatamente antes de gerar o _data/navigation.yml na Fase 0, não confiar neste número congelado no documento..md no total._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).
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).
| # | 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-mermaid → div.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 |
Todo arquivo de conteúdo precisa trocar layout: default → layout: 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.
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
theme/minimal-mistakes.gerar_navigation_yml.py: lê mod_XX/topicos/index.md de cada módulo, gera a entrada correspondente em _data/navigation.yml.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+)._includes/head/custom.html definitivo com os 15 itens da tabela §3.3._data/ui-text.yml (bloco pt-BR: corrigido).mod_11_qualidade_e_testes_de_software (já é o módulo mais testado nesta investigação)._config.yml, então não dá pra ter os dois ao mesmo tempo em produção; o piloto roda em preview local/branch, não em deploy real, até passar na Fase 2.Ver bateria completa na Seção 5. Só avança para a Fase 3 se 100% dos itens passarem.
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:
437f5e16): mod_01 a mod_10.219016b9): mod_12 a mod_14 + os 7 extra_*.54ab192c): proj_aplicacoes_full_stack + specs de backend/frontend/mobile (10 módulos).2352d775, final): spec_seguranca_e_criptografia + os 8 spec_sistemas_com_*.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.
_scripts/test_links.py + amostragem visual (Seção 5.1) antes de fazer merge do lote pra main._config.yml, então o rollout é feito inteiro numa branch, com merges intermediários revisados, e o deploy real só acontece quando a branch inteira estiver pronta (ou, alternativa mais seguem, ver Nota abaixo).⚠️ Nota de arquitetura: como o tema é definido uma vez em
_config.ymlpara 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.
theme/minimal-mistakes → main), commit de merge a3769112/e5959f88.gh run watch (run 33532310862): build 3m55s + deploy 31s, ambos verdes.ricardotecpro.github.io/portal_jekyll): home carrega sem erro, capítulo do mod_11 com sidebar/TOC/Mermaid/H1 único idênticos ao testado na Fase 2, busca global (Ctrl+K) funcionando com o título <script setup> renderizando corretamente (confirma o fix do achado da Fase 2 em produção). Console sem erros em nenhuma página testada.Site 100% no Minimal Mistakes em produção. Cayman preservado na branch
mainanterior ao merge (cc326ecf) para referência de rollback (Seção 7).
gh run list diariamente por 1 semana.cc326ecf (último antes do merge da migração).| 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 |
| 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.innerWidthreal (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).
A Fase 3 só começa se, no módulo piloto:
theme/minimal-mistakes): sidebar auto-colapsada (só mod_11 expandido de 40), H1 único, Mermaid (23 diagramas SVG), MathJax (23 fórmulas), alertas GitHub (2 renderizados, inclusive em página não migrada — head/custom.html chega no site inteiro via o default.html do próprio tema, não só em layout: single), quiz interativo (clique real testado, feedback verde/vermelho correto), busca global (Ctrl+K, resultado real testado), badge de linguagem + botão copiar presentes e com markup correto (clique via automação não confirmou o clipboard, mas é o mesmo código já provado em produção no Cayman)_scripts/test_links.py retorna zero links quebrados — confirmado no CI (run 33525061322)⚠️ 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#/JavaList<T>/<T>) têm títulos com sintaxe literal de tag HTML.gerar_navigation_yml.pygravava isso cru nonavigation.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 +SyntaxErrorno console). Corrigido escapando&/</>no gerador e no JS da busca global (mesmo risco, viasearch.json).
| # | Risco | Mitigação |
|---|---|---|
| R1 | _data/navigation.yml de 40 cursos × 20 capítulos (~760+ links) não foi medido em escala real — só com 4 cursos simulados |
— |
| 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 |
Trivial — a branch theme/minimal-mistakes simplesmente não é mergeada. Zero impacto em produção. Pode ser abandonada ou retomada a qualquer momento.
# 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
_config.yml, Gemfile, _layouts/, _includes/head-custom.html e assets/css/style.scss de antes — nenhuma perda.layout/sidebar) e a camada de tema. Um git revert do front matter também é seguro porque as mudanças são mecânicas e idempotentes.gh run watch no deploy de rollback pra confirmar que o site voltou ao estado anterior antes de considerar o incidente encerrado.Se só um módulo específico apresentar problema depois do corte completo:
git checkout main~N -- mod_XX_.../) restaurando layout: default ali — o resto do site continua no Minimal Mistakes.default.html genérico do tema, ainda funcional, só sem o chrome extra) até o problema ser corrigido e reaplicado.scratchpad/theme_lab/ (local, fora do repositório — não versionado; os arquivos definitivos de _includes/head/custom.html e _data/ui-text.yml usados no laboratório devem ser copiados para o repo na Fase 0).