Linha do Tempo
Apresenta eventos ou marcos em ordem cronológica. O aprendiz navega pela sequência temporal, ideal para histórico de evolução, processos com datas ou jornadas do usuário.
Experimente
Marcos da Aviação Comercial
Primeiro Voo Motorizado
Os irmãos Wright realizam o primeiro voo controlado de uma aeronave mais pesada que o ar, em Kitty Hawk.
Primeiro Voo Comercial Regular
Surge a primeira linha aérea regular entre cidades europeias, iniciando o transporte comercial de passageiros.
Era dos Jatos
O Boeing 707 entra em operação comercial, reduzindo drasticamente o tempo de viagens transatlânticas.
Jumbo Jet
O Boeing 747 populariza viagens de longa distância em massa, com capacidade muito maior que os jatos anteriores.
Superjumbo
O Airbus A380 estreia como a maior aeronave comercial de passageiros já construída.
Combustíveis Sustentáveis
Fabricantes e companhias aéreas investem em combustível de aviação sustentável (SAF) para reduzir a pegada de carbono do setor.
Código-fonte
Dados de exemplo
Cada evento tem data, título e descrição. O campo "atual" (booleano, presente só no evento certo) diz qual marco é o "você está aqui" — nunca assuma que é o primeiro nem o último, deixe explícito nos dados.
| 1 |
<?php
|
| 2 |
$titulo = 'Marcos da Fotografia';
|
| 3 |
$eventos = [
|
| 4 |
['data' => '1826', 'titulo' => 'Primeira Fotografia Permanente', 'descricao' => 'Niépce fixa a primeira imagem permanente numa placa de estanho, após horas de exposição à luz.'],
|
| 5 |
['data' => '1888', 'titulo' => 'Câmera Kodak', 'descricao' => 'George Eastman lança a primeira câmera portátil com filme em rolo, popularizando a fotografia amadora.'],
|
| 6 |
['data' => '1935', 'titulo' => 'Filme Colorido Kodachrome', 'descricao' => 'Chega ao mercado o primeiro filme colorido de sucesso comercial, tornando fotos coloridas acessíveis ao público geral.'],
|
| 7 |
['data' => '1975', 'titulo' => 'Primeira Câmera Digital', 'descricao' => 'Um engenheiro constrói o primeiro protótipo de câmera digital, gravando imagens numa fita cassete.'],
|
| 8 |
['data' => 'Hoje', 'titulo' => 'Fotografia Computacional', 'atual' => true, 'descricao' => 'Smartphones combinam várias capturas e processamento por IA numa única imagem, superando limitações físicas da lente.'],
|
| 9 |
];
|
| 10 |
$total = count($eventos);
|
| 11 |
?>
|
Cabeçalho
Só o título, centralizado — mesmo tratamento dos outros componentes de exibição do site.
| 1 |
<div class="linha-tempo">
|
| 2 |
<div class="cabecalho">
|
| 3 |
<p class="titulo"><?= htmlspecialchars($titulo) ?></p>
|
| 4 |
</div>
|
Marcador e conector
A linha entre um marcador e o próximo é um elemento por evento (não uma barra única cobrindo o cartão inteiro) — por isso ela nunca sobra abaixo do último marcador: o último evento simplesmente não recebe conector nenhum.
| 1 |
<div class="eventos">
|
| 2 |
<?php foreach ($eventos as $i => $evento): $ultimo = $i === $total - 1; ?>
|
| 3 |
<div class="evento">
|
| 4 |
<div class="coluna-marcador">
|
| 5 |
<div class="marcador<?= !empty($evento['atual']) ? ' atual' : '' ?>">
|
| 6 |
<?= htmlspecialchars($evento['data']) ?>
|
| 7 |
</div>
|
| 8 |
<?php if (!$ultimo): ?>
|
| 9 |
<div class="conector"></div>
|
| 10 |
<?php endif; ?>
|
| 11 |
</div>
|
Conteúdo do evento
O último evento tem menos espaço embaixo (não precisa "empurrar" nada) — os demais têm espaço extra reservado, que é exatamente o espaço que o conector do marcador preenche ao lado.
| 1 |
<div class="conteudo <?= $ultimo ? 'ultimo' : '' ?>">
|
| 2 |
<h3><?= htmlspecialchars($evento['titulo']) ?></h3>
|
| 3 |
<p><?= htmlspecialchars($evento['descricao']) ?></p>
|
| 4 |
</div>
|
| 5 |
</div>
|
| 6 |
<?php endforeach; ?>
|
| 7 |
</div>
|
| 8 |
</div>
|
Cartão e cabeçalho
Cabeçalho como caixa de título própria, mesmo tratamento dos outros componentes de exibição do site.
| 1 |
.linha-tempo {
|
| 2 |
border: 1px solid #e2e8f0;
|
| 3 |
border-radius: 16px;
|
| 4 |
overflow: hidden;
|
| 5 |
font-family: inherit;
|
| 6 |
}
|
| 7 |
.cabecalho {
|
| 8 |
padding: 20px 24px;
|
| 9 |
text-align: center;
|
| 10 |
background: #f1f5f9;
|
| 11 |
border-bottom: 1px solid #cbd5e1;
|
| 12 |
}
|
| 13 |
.titulo {
|
| 14 |
margin: 0;
|
| 15 |
font-size: 18px;
|
| 16 |
font-weight: 700;
|
| 17 |
color: #0f172a;
|
| 18 |
}
|
Estrutura do evento
A linha do evento não centraliza verticalmente o marcador com o conteúdo (align-items: stretch, o padrão do flexbox) — assim a coluna do marcador estica para ter a mesma altura do texto ao lado, e o conector consegue preencher exatamente esse espaço com flex:1.
| 1 |
.eventos {
|
| 2 |
padding: 20px;
|
| 3 |
}
|
| 4 |
.evento {
|
| 5 |
display: flex;
|
| 6 |
gap: 20px;
|
| 7 |
}
|
| 8 |
.coluna-marcador {
|
| 9 |
display: flex;
|
| 10 |
flex-direction: column;
|
| 11 |
align-items: center;
|
| 12 |
flex-shrink: 0;
|
| 13 |
}
|
Marcador
Fundo sólido (não translúcido) é obrigatório aqui — o marcador precisa cobrir por completo o conector que passa atrás dele. Um fundo com opacidade deixaria a linha visível por dentro do círculo, exatamente o bug que este componente teve na versão publicada do site.
| 1 |
.marcador {
|
| 2 |
flex-shrink: 0;
|
| 3 |
width: 48px;
|
| 4 |
height: 48px;
|
| 5 |
border-radius: 999px;
|
| 6 |
border: 2px solid #94a3b8;
|
| 7 |
background: #f1f5f9;
|
| 8 |
color: #475569;
|
| 9 |
display: flex;
|
| 10 |
align-items: center;
|
| 11 |
justify-content: center;
|
| 12 |
font-size: 12px;
|
| 13 |
font-weight: 700;
|
| 14 |
}
|
| 15 |
.marcador.atual {
|
| 16 |
border-color: #6366f1;
|
| 17 |
background: #e0e7ff;
|
| 18 |
color: #4338ca;
|
| 19 |
}
|
Conector entre marcadores
flex:1 faz o conector esticar até preencher exatamente a altura da coluna do marcador (que por sua vez segue a altura do conteúdo ao lado) — não existe número mágico de pixels aqui, o comprimento se ajusta sozinho ao tamanho do texto de cada evento.
| 1 |
.conector {
|
| 2 |
flex: 1;
|
| 3 |
width: 2px;
|
| 4 |
min-height: 12px;
|
| 5 |
margin: 4px 0;
|
| 6 |
background: #cbd5e1;
|
| 7 |
}
|
Conteúdo do evento
O evento comum ganha um respiro extra embaixo (padding-bottom) pra abrir espaço visual antes do próximo — o último evento não precisa disso, porque não vem mais nada depois dele.
| 1 |
.conteudo {
|
| 2 |
flex: 1;
|
| 3 |
padding-top: 8px;
|
| 4 |
padding-bottom: 24px;
|
| 5 |
}
|
| 6 |
.conteudo.ultimo {
|
| 7 |
padding-bottom: 4px;
|
| 8 |
}
|
| 9 |
.conteudo h3 {
|
| 10 |
margin: 0 0 4px;
|
| 11 |
font-size: 16px;
|
| 12 |
font-weight: 600;
|
| 13 |
color: #1e293b;
|
| 14 |
}
|
| 15 |
.conteudo p {
|
| 16 |
margin: 0;
|
| 17 |
font-size: 15px;
|
| 18 |
line-height: 1.6;
|
| 19 |
color: #64748b;
|
| 20 |
}
|