Todas as atividades
Exibição Visual

Antes e Depois

Exibe a transformação entre dois estados com animação ou transição. O aprendiz vê o estado inicial e o estado final com destaque para as mudanças, ideal para comparar versões, progressões e melhorias.

Análise antes depois comparação animação before after

Experimente

Email Profissional: Revisão

Versão Original

De: joao@empresa.com

Assunto: reunião

Olá, precisamos marcar uma reunião urgente sobre o projeto que está atrasado. Quando você pode? Responde logo.

1 Assunto vago e sem contexto
2 Tom impositivo e sem cortesia
3 Sem proposta de horário

3 problemas → 3 melhorias

Versão Revisada

De: joao@empresa.com

Assunto: Reunião de alinhamento — Projeto Alpha (semana 24)

Bom dia, Maria. Gostaria de agendar 30 minutos para alinharmos o cronograma do Projeto Alpha. Tenho disponibilidade na terça às 14h ou quarta às 10h. Qual horário funciona melhor para você?

1 Assunto específico com contexto
2 Tom cordial e direto
3 Duas opções de horário concretas

Código-fonte

PHP

Dados de exemplo

Linhas pareadas por índice — a linha 3 do "antes" corresponde à linha 3 do "depois". O JS usa esse índice pra comparar só as linhas certas entre si.

1 <?php
2 $antes = [
3 'linhas' => [
4 'Produto: Fone de Ouvido',
5 'Preço: R$ 89,90',
6 '',
7 'Fone de ouvido bom pra usar no dia a dia, com bateria que dura bastante e som de qualidade.',
8 ],
9 'problemas' => [
10 'Descrição genérica, sem números concretos',
11 'Não menciona diferenciais em relação aos concorrentes',
12 'Falta um contexto de uso que ajude a pessoa a se identificar',
13 ],
14 ];
15 $depois = [
16 'linhas' => [
17 'Produto: Fone de Ouvido',
18 'Preço: R$ 89,90',
19 '',
20 'Fone de ouvido com cancelamento de ruído, 30 horas de bateria e som Hi-Fi — ideal para quem trabalha em ambientes barulhentos.',
21 ],
22 'melhorias' => [
23 'Números concretos (30 horas, cancelamento de ruído)',
24 'Diferencial técnico específico (som Hi-Fi)',
25 'Contexto de uso que ajuda a pessoa a se identificar',
26 ],
27 ];
28 ?>

Estrutura da comparação

As linhas de cada lado recebem data-linha com o mesmo índice — é o que permite ao JS comparar a linha certa do "antes" com a linha certa do "depois", mesmo que a ordem de renderização mude no futuro.

1 <div class="antes-depois" data-tipo="antes-depois">
2
3 <div class="caixa caixa-antes anima anima-1">
4 <div class="caixa-cabecalho">✕ Versão original</div>
5 <div class="caixa-linhas">
6 <?php foreach ($antes['linhas'] as $i => $linha): ?>
7 <p data-linha="<?= $i ?>"><?= htmlspecialchars($linha) ?></p>
8 <?php endforeach; ?>
9 </div>
10 <div class="caixa-lista">
11 <?php foreach ($antes['problemas'] as $i => $problema): ?>
12 <div class="caixa-item"><span class="numero numero-antes"><?= $i + 1 ?></span> <?= htmlspecialchars($problema) ?></div>
13 <?php endforeach; ?>
14 </div>
15 </div>
16
17 <div class="seta anima anima-2">
18 <span class="seta-icone">↓</span>
19 <p class="seta-resumo"><?= count($antes['problemas']) ?> problemas → <?= count($depois['melhorias']) ?> melhorias</p>
20 </div>
21
22 <div class="caixa caixa-depois anima anima-3">
23 <div class="caixa-cabecalho">✓ Versão revisada</div>
24 <div class="caixa-linhas">
25 <?php foreach ($depois['linhas'] as $i => $linha): ?>
26 <p data-linha="<?= $i ?>"><?= htmlspecialchars($linha) ?></p>
27 <?php endforeach; ?>
28 </div>
29 <div class="caixa-lista">
30 <?php foreach ($depois['melhorias'] as $i => $melhoria): ?>
31 <div class="caixa-item"><span class="numero numero-depois"><?= $i + 1 ?></span> <?= htmlspecialchars($melhoria) ?></div>
32 <?php endforeach; ?>
33 </div>
34 </div>
35
36 </div>
CSS

Caixas antes/depois

Mesma estrutura visual pros dois lados — só a cor muda (vermelho pro "antes", verde pro "depois").

1 .caixa {
2 border-radius: 12px;
3 overflow: hidden;
4 font-family: inherit;
5 }
6 .caixa-antes {
7 border: 1px solid rgba(239, 68, 68, .3);
8 background: rgba(127, 29, 29, .05);
9 }
10 .caixa-depois {
11 border: 1px solid rgba(34, 197, 94, .3);
12 background: rgba(20, 83, 45, .05);
13 }
14 .caixa-cabecalho {
15 padding: 8px 16px;
16 font-size: 13px;
17 font-weight: 700;
18 }
19 .caixa-antes .caixa-cabecalho {
20 background: rgba(127, 29, 29, .15);
21 color: #dc2626;
22 }
23 .caixa-depois .caixa-cabecalho {
24 background: rgba(20, 83, 45, .15);
25 color: #16a34a;
26 }

Linhas de texto

Fonte monoespaçada — o conteúdo comparado costuma ser texto "de verdade" (e-mail, descrição, trecho de código), então a fonte de código deixa a comparação mais clara que uma fonte de leitura corrida.

1 .caixa-linhas {
2 padding: 12px 16px;
3 font-family: 'Courier New', monospace;
4 font-size: 12px;
5 line-height: 1.6;
6 }
7 .caixa-linhas p {
8 margin: 0;
9 }

Destaque de palavra

Calculado pelo JS (diffPalavras) — fundo fraco, sem risco. Um risco sobre uma frase inteira (não uma palavra solta) atrapalha a leitura corrida da comparação.

1 .palavra-removida {
2 background: rgba(239, 68, 68, .18);
3 border-radius: 3px;
4 padding: 0 2px;
5 }
6 .palavra-adicionada {
7 background: rgba(34, 197, 94, .2);
8 border-radius: 3px;
9 padding: 0 2px;
10 font-weight: 600;
11 }

Lista de problemas/melhorias

O número em cada item deixa explícito que o problema N do "antes" foi resolvido pela melhoria N do "depois" — sem isso, as duas listas ficam soltas, sem uma ligação visível entre elas.

1 .caixa-lista {
2 padding: 8px 16px 16px;
3 border-top: 1px solid rgba(0, 0, 0, .08);
4 font-size: 12px;
5 }
6 .caixa-item {
7 display: flex;
8 align-items: center;
9 gap: 8px;
10 padding: 3px 0;
11 }
12 .caixa-antes .caixa-item { color: #dc2626; }
13 .caixa-depois .caixa-item { color: #16a34a; }
14 .numero {
15 flex-shrink: 0;
16 width: 16px;
17 height: 16px;
18 border-radius: 999px;
19 font-size: 10px;
20 font-weight: 700;
21 display: flex;
22 align-items: center;
23 justify-content: center;
24 }
25 .numero-antes { background: rgba(239, 68, 68, .15); color: #dc2626; }
26 .numero-depois { background: rgba(34, 197, 94, .15); color: #16a34a; }

Seta central

Além de indicar "desça o olho", mostra um resumo em números do que mudou.

1 .seta {
2 display: flex;
3 flex-direction: column;
4 align-items: center;
5 gap: 2px;
6 margin: 8px 0;
7 }
8 .seta-icone {
9 font-size: 20px;
10 color: #6366f1;
11 }
12 .seta-resumo {
13 margin: 2px 0 0;
14 font-size: 11px;
15 color: #64748b;
16 }

Animação de entrada

As três partes (antes, seta, depois) entram em sequência — reforça a narrativa "uma coisa leva à outra". Respeita prefers-reduced-motion.

1 @keyframes entrada {
2 from { opacity: 0; transform: translateY(8px); }
3 to { opacity: 1; transform: translateY(0); }
4 }
5 .anima {
6 animation: entrada .45s ease-out both;
7 }
8 .anima-1 { animation-delay: 0s; }
9 .anima-2 { animation-delay: .15s; }
10 .anima-3 { animation-delay: .3s; }
11 @media (prefers-reduced-motion: reduce) {
12 .anima { animation: none; }
13 }
JavaScript

Diff por palavra

Maior subsequência comum (LCS) clássica entre as palavras de duas linhas — acha o que se manteve, o que saiu (só existe em "a") e o que entrou (só existe em "b"). Roda em cima do texto que já está na página, sem precisar de anotação manual no conteúdo.

1 document.addEventListener('DOMContentLoaded', function () {
2 function tokenizar(texto) {
3 return texto.split(/(\s+)/).filter(function (t) { return t !== ''; });
4 }
5
6 function diffPalavras(a, b) {
7 var m = a.length, n = b.length;
8 var dp = [];
9 for (var i = 0; i <= m; i++) dp.push(new Array(n + 1).fill(0));
10 for (var i = 1; i <= m; i++) {
11 for (var j = 1; j <= n; j++) {
12 dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] + 1 : Math.max(dp[i - 1][j], dp[i][j - 1]);
13 }
14 }
15
16 var i = m, j = n;
17 var resultA = [], resultB = [];
18 while (i > 0 && j > 0) {
19 if (a[i - 1] === b[j - 1]) {
20 resultA.unshift({ texto: a[i - 1], tipo: 'igual' });
21 resultB.unshift({ texto: b[j - 1], tipo: 'igual' });
22 i--; j--;
23 } else if (dp[i - 1][j] >= dp[i][j - 1]) {
24 resultA.unshift({ texto: a[i - 1], tipo: 'removido' });
25 i--;
26 } else {
27 resultB.unshift({ texto: b[j - 1], tipo: 'adicionado' });
28 j--;
29 }
30 }
31 while (i > 0) { resultA.unshift({ texto: a[i - 1], tipo: 'removido' }); i--; }
32 while (j > 0) { resultB.unshift({ texto: b[j - 1], tipo: 'adicionado' }); j--; }
33 return { a: resultA, b: resultB };
34 }
35
36 function escaparHtml(texto) {
37 var div = document.createElement('div');
38 div.textContent = texto;
39 return div.innerHTML;
40 }

Aplicar o diff nas linhas correspondentes

Cada linha do "antes" procura, pelo mesmo data-linha, a linha equivalente no "depois". Só marca quando as duas têm texto e são diferentes — linhas em branco (espaçadoras) ou idênticas ficam como estão.

1 document.querySelectorAll('[data-tipo="antes-depois"]').forEach(function (el) {
2 el.querySelectorAll('.caixa-antes .caixa-linhas p').forEach(function (linhaAntes) {
3 var linhaDepois = el.querySelector('.caixa-depois .caixa-linhas p[data-linha="' + linhaAntes.dataset.linha + '"]');
4 if (!linhaDepois) return;
5
6 var textoAntes = linhaAntes.textContent;
7 var textoDepois = linhaDepois.textContent;
8 if (!textoAntes.trim() || !textoDepois.trim() || textoAntes === textoDepois) return;
9
10 var diff = diffPalavras(tokenizar(textoAntes), tokenizar(textoDepois));
11 linhaAntes.innerHTML = diff.a.map(function (p) { return p.tipo === 'igual' ? escaparHtml(p.texto) : '<span class="palavra-removida">' + escaparHtml(p.texto) + '</span>'; }).join('');
12 linhaDepois.innerHTML = diff.b.map(function (p) { return p.tipo === 'igual' ? escaparHtml(p.texto) : '<span class="palavra-adicionada">' + escaparHtml(p.texto) + '</span>'; }).join('');
13 });
14 });
15 });