Zigzag Flow
Timeline alternada com layout zigzag, animacoes de entrada staggered e status badges.
Uso
Quando e como utilizar o componente Zigzag Flow.
- Use para representar fluxos sequenciais ou cronologicos com layout visual alternado (esquerda/direita).
- Ideal para timelines de projetos, historicos de atividades e fluxos de aprovacao.
- Cada item pode conter um status badge para indicar o estado atual da etapa.
- Suporta animacoes de entrada staggered via IntersectionObserver para revelacao progressiva ao scroll.
- Nao recomendado para listas simples sem relacao temporal — prefira componentes de lista nesses casos.
Exemplos
Demonstracao interativa do Zigzag Flow com diferentes status e animacoes.
Anatomia
Estrutura interna do componente.
| Elemento | Classe | Descricao |
|---|---|---|
| Container | .zigzag-flow | Wrapper principal que define o layout zigzag. |
| Titulo | .zigzag-flow__title | Titulo geral do componente. |
| Lista | .zigzag-flow__items | Container dos itens da timeline. |
| Item | .zigzag-flow__item | Cada etapa da timeline, posicionada alternadamente. |
| Dot | .zigzag-flow__dot | Marcador circular no eixo central. |
| Card | .zigzag-flow__card | Area de conteudo com titulo, corpo e meta. |
| Status | .zigzag-flow__status | Badge indicando o estado da etapa. |
| Anexos | .zigzag-flow__attachments | Lista de arquivos ou links anexados ao card. |
| Vazio | .zigzag-flow__empty | Exibido quando nao existem itens. |
Animacoes
Animacoes de entrada com IntersectionObserver e stagger delay.
O Zigzag Flow utiliza IntersectionObserver para detectar quando cada item entra no viewport. Ao se tornar visivel, a classe .zigzag-flow__item--animated e adicionada com um delay staggered, criando um efeito de revelacao progressiva.
Cada item recebe um transition-delay incremental baseado no seu indice, garantindo que os cards aparecam sequencialmente de cima para baixo.
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
entry.target.style.transitionDelay = `${index * 120}ms`;
entry.target.classList.add('zigzag-flow__item--animated');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.15 });
document.querySelectorAll('.zigzag-flow__item').forEach((item) => {
observer.observe(item);
});Propriedades
Classes CSS disponiveis para o componente.
| Propriedade | Tipo | Default | Descrição |
|---|---|---|---|
.zigzag-flow | classe | — | Container principal da timeline zigzag. |
.zigzag-flow__title | classe | — | Titulo do componente. |
.zigzag-flow__items | classe | — | Lista de itens da timeline. |
.zigzag-flow__item | classe | — | Item individual da timeline. |
.zigzag-flow__dot | classe | — | Marcador circular no eixo central da timeline. |
.zigzag-flow__dot--completed | classe | — | Dot com estilo de etapa concluida. |
.zigzag-flow__dot--pending | classe | — | Dot com estilo de etapa pendente. |
.zigzag-flow__dot--error | classe | — | Dot com estilo de etapa com erro. |
.zigzag-flow__dot--warning | classe | — | Dot com estilo de etapa com alerta. |
.zigzag-flow__card | classe | — | Card de conteudo associado ao item. |
.zigzag-flow__card-title | classe | — | Titulo do card. |
.zigzag-flow__card-body | classe | — | Corpo de texto do card. |
.zigzag-flow__card-meta | classe | — | Metadados do card (data, autor, etc.). |
.zigzag-flow__status | classe | — | Badge de status exibido no card. |
.zigzag-flow__status--completed | classe | — | Badge de status concluido. |
.zigzag-flow__status--pending | classe | — | Badge de status pendente. |
.zigzag-flow__status--error | classe | — | Badge de status com erro. |
.zigzag-flow__status--warning | classe | — | Badge de status com alerta. |
.zigzag-flow__attachments | classe | — | Area de anexos dentro do card. |
.zigzag-flow__empty | classe | — | Estado vazio quando nao ha itens. |
.zigzag-flow__item--animated | classe | — | Modificador que ativa animacao de entrada no item. |
Codigo
Snippets de implementacao.
HTML estatico
<div class="zigzag-flow">
<h2 class="zigzag-flow__title">Timeline do Projeto</h2>
<div class="zigzag-flow__items">
<div class="zigzag-flow__item">
<span class="zigzag-flow__dot zigzag-flow__dot--completed"></span>
<div class="zigzag-flow__card">
<h3 class="zigzag-flow__card-title">Etapa 1 — Planejamento</h3>
<p class="zigzag-flow__card-body">Definicao do escopo e cronograma do projeto.</p>
<span class="zigzag-flow__card-meta">12 jan 2026</span>
<span class="zigzag-flow__status zigzag-flow__status--completed">Concluido</span>
</div>
</div>
<div class="zigzag-flow__item">
<span class="zigzag-flow__dot zigzag-flow__dot--pending"></span>
<div class="zigzag-flow__card">
<h3 class="zigzag-flow__card-title">Etapa 2 — Desenvolvimento</h3>
<p class="zigzag-flow__card-body">Implementacao das funcionalidades principais.</p>
<span class="zigzag-flow__card-meta">28 fev 2026</span>
<span class="zigzag-flow__status zigzag-flow__status--pending">Pendente</span>
<div class="zigzag-flow__attachments">
<a href="#">spec-v2.pdf</a>
</div>
</div>
</div>
<div class="zigzag-flow__item">
<span class="zigzag-flow__dot zigzag-flow__dot--error"></span>
<div class="zigzag-flow__card">
<h3 class="zigzag-flow__card-title">Etapa 3 — Revisao</h3>
<p class="zigzag-flow__card-body">Revisao de codigo e testes de integracao.</p>
<span class="zigzag-flow__card-meta">14 mar 2026</span>
<span class="zigzag-flow__status zigzag-flow__status--error">Erro</span>
</div>
</div>
</div>
</div>DataLoader + Factory
<script>
async function loadZigzagFlow(container, url) {
const response = await fetch(url);
const data = await response.json();
const title = document.createElement('h2');
title.className = 'zigzag-flow__title';
title.textContent = data.title;
container.appendChild(title);
const items = document.createElement('div');
items.className = 'zigzag-flow__items';
data.steps.forEach((step) => {
const item = document.createElement('div');
item.className = 'zigzag-flow__item';
item.innerHTML = `
<span class="zigzag-flow__dot zigzag-flow__dot--${step.status}"></span>
<div class="zigzag-flow__card">
<h3 class="zigzag-flow__card-title">${step.title}</h3>
<p class="zigzag-flow__card-body">${step.body}</p>
<span class="zigzag-flow__card-meta">${step.date}</span>
<span class="zigzag-flow__status zigzag-flow__status--${step.status}">${step.label}</span>
</div>
`;
items.appendChild(item);
});
container.appendChild(items);
}
</script>
<div class="zigzag-flow" id="timeline"></div>
<script>
loadZigzagFlow(
document.getElementById('timeline'),
'/api/zigzag-flow.json'
);
</script>Auto-init via data attributes
<div class="zigzag-flow"
data-component="zigzag-flow"
data-src="/api/zigzag-flow.json"
data-animate="true"
data-stagger="120">
</div>
<script>
document.querySelectorAll('[data-component="zigzag-flow"]').forEach((el) => {
const src = el.dataset.src;
const animate = el.dataset.animate === 'true';
const stagger = parseInt(el.dataset.stagger, 10) || 120;
fetch(src)
.then((r) => r.json())
.then((data) => {
renderZigzagFlow(el, data);
if (animate) initStaggerAnimation(el, stagger);
});
});
</script>Estado vazio
<div class="zigzag-flow">
<h2 class="zigzag-flow__title">Timeline do Projeto</h2>
<div class="zigzag-flow__empty">
<p>Nenhuma etapa cadastrada para este projeto.</p>
</div>
</div>Fonte de Dados
Formatos de dados suportados pelo componente.
O Zigzag Flow aceita dados nos formatos JSON e CSV.
JSON — estrutura esperada:
{
"title": "Timeline do Projeto",
"steps": [
{
"title": "Planejamento",
"body": "Definicao do escopo e cronograma.",
"date": "12 jan 2026",
"status": "completed",
"label": "Concluido"
},
{
"title": "Desenvolvimento",
"body": "Implementacao das funcionalidades.",
"date": "28 fev 2026",
"status": "pending",
"label": "Pendente"
}
]
}CSV — estrutura esperada:
title,body,date,status,label
Planejamento,Definicao do escopo e cronograma.,12 jan 2026,completed,Concluido
Desenvolvimento,Implementacao das funcionalidades.,28 fev 2026,pending,PendenteOs valores validos para status sao: completed, pending, error e warning.
Dark Mode
Comportamento do componente no modo escuro.
O Zigzag Flow adapta-se automaticamente ao dark mode via CSS custom properties. Os cards recebem fundo escuro, os dots ajustam suas cores de borda e preenchimento, e os status badges mantêm contraste adequado. Nenhuma classe adicional e necessaria — basta que o tema escuro esteja ativo no :root ou no container pai.