Sankey Diagram
Diagrama de fluxo SVG com paths proporcionais conectando fontes a destinos. Ideal para visualizar distribuicao de recursos, orcamentos e fluxos entre categorias.
Uso
Quando e como utilizar o diagrama Sankey para representar fluxos proporcionais entre origens e destinos.
- JSON — Carrega dados de um endpoint JSON via DataLoader com mapeamento de campos source/target/value.
- Inline — Dados passados diretamente via JavaScript como array de objetos, sem fetch externo.
- HTML estatico — SVG escrito manualmente com paths e retangulos usando classes CSS, sem JavaScript.
Exemplos
Veja abaixo os exemplos interativos do diagrama Sankey, incluindo variantes de dados, tooltips e dark mode.
Anatomia
Estrutura interna do componente e seus elementos.
| Parte | Elemento | Descricao |
|---|---|---|
| Container | .sankey-diagram | Wrapper principal com position relative e width 100% |
| Titulo | .sankey-diagram__title | Heading centralizado, Space Grotesk 18px w600 |
| SVG Wrapper | .sankey-diagram__svg-wrapper | Wrapper responsivo com overflow hidden |
| SVG | .sankey-diagram__svg | SVG com viewBox dinamico e preserveAspectRatio |
| Node Rect | .sankey-diagram__node-rect | Retangulo do no com fill colorido e border-radius |
| Node Label | .sankey-diagram__node-label | Texto do nome do no, Inter 13px w500 |
| Node Value | .sankey-diagram__node-value | Valor numerico, Space Grotesk 12px w700 |
| Link | .sankey-diagram__link | Path Bezier cubico com fill-opacity 0.3 e hover 0.5 |
| Tooltip | .sankey-diagram__tooltip | Tooltip absoluto com source, target e valor |
| Legenda | .sankey-diagram__legend | Flex row centralizada com gap 16px e flex-wrap |
| Legend Item | .sankey-diagram__legend-item | Dot colorido + texto 13px |
| Vazio | .sankey-diagram__empty | Estado sem dados, borda dashed e padding 48px |
SVG Internals
Como as curvas Bezier, o layout dos nos e a renderizacao multi-coluna funcionam internamente.
Curvas Bezier
Os links entre nos sao desenhados como paths SVG com curvas Bezier cubicas (C). O ponto de controle horizontal e posicionado a 50% da distancia entre o no de origem e o no de destino, criando uma curva suave e proporcional.
M x0,y0 C cx0,y0 cx1,y1 x1,y1Onde cx0 = x0 + (x1 - x0) * 0.5 e cx1 = x1 - (x1 - x0) * 0.5. A espessura do path e proporcional ao valor do fluxo.
Layout dos nos
Os nos sao distribuidos automaticamente em colunas verticais. A altura de cada retangulo e proporcional a soma dos valores de entrada e saida do no. Os nos dentro de cada coluna sao empilhados verticalmente com um gap configuravel (padrao 8px).
Multi-coluna
O diagrama suporta ate 3 niveis de profundidade (colunas), posicionados automaticamente no eixo X do viewBox:
| Coluna | Posicao X | Conteudo |
|---|---|---|
| 0 | Extremo esquerdo | Nos de origem (sources) |
| 1 | Centro | Nos intermediarios |
| 2 | Extremo direito | Nos de destino (targets) |
A largura de cada coluna de nos e fixa (padrao 24px). O espaco entre colunas e calculado como (viewBoxWidth - 3 * nodeWidth) / 2.
Layout de Nos
Como os nos sao classificados e posicionados em colunas automaticamente.
O algoritmo de posicionamento classifica cada no com base nas conexoes do dataset:
| Tipo | Coluna | Regra |
|---|---|---|
| Source | 0 | No que aparece apenas como source nos dados |
| Intermediary | 1 | No que aparece como target de um fluxo e source de outro |
| Target | 2 | No que aparece apenas como target nos dados |
A classificacao e automatica — basta que os dados contenham registros onde o mesmo nome aparece em ambos os campos source e target para criar nos intermediarios.
Exemplo de fluxo multi-nivel:
Orcamento Total → Sanidade Animal → Vacinacao
→ Monitoramento
→ Defesa Vegetal → Controle de PragasNeste caso, "Orcamento Total" e coluna 0, "Sanidade Animal" e "Defesa Vegetal" sao coluna 1, e "Vacinacao", "Monitoramento" e "Controle de Pragas" sao coluna 2.
Propriedades
Classes CSS disponiveis para estruturar e estilizar o diagrama Sankey.
| Propriedade | Tipo | Default | Descrição |
|---|---|---|---|
.sankey-diagram | classe | — | Container principal do diagrama de fluxo. |
.sankey-diagram__title | classe | — | Titulo centralizado acima do SVG (18px w600). |
.sankey-diagram__svg-wrapper | classe | — | Wrapper responsivo com overflow hidden e aspect-ratio. |
.sankey-diagram__svg | classe | — | SVG principal com viewBox dinamico (width 100%, height auto). |
.sankey-diagram__node-rect | classe | — | Retangulo de cada no com fill colorido e border-radius 4px. |
.sankey-diagram__node-label | classe | — | Texto do nome do no, posicionado ao lado do retangulo (13px w500). |
.sankey-diagram__node-value | classe | — | Valor numerico do no, abaixo do label (12px w700). |
.sankey-diagram__link | classe | — | Path SVG com curva Bezier conectando fonte ao destino (fill-opacity 0.3). |
.sankey-diagram__tooltip | classe | — | Tooltip absoluto exibido no hover do link ou no. |
.sankey-diagram__tooltip--visible | classe | — | Modificador que torna o tooltip visivel (opacity 1, pointer-events auto). |
.sankey-diagram__legend | classe | — | Legenda horizontal abaixo do SVG com flex-wrap. |
.sankey-diagram__legend-item | classe | — | Item da legenda (dot colorido + texto 13px). |
.sankey-diagram__legend-dot | classe | — | Circulo colorido 12x12 na legenda. |
.sankey-diagram__empty | classe | — | Estado vazio com borda tracejada e texto centralizado. |
Codigo
Snippets prontos para copiar e usar no seu projeto.
HTML estatico (SVG)
<div class="sankey-diagram">
<h3 class="sankey-diagram__title">Distribuicao Orcamentaria</h3>
<div class="sankey-diagram__svg-wrapper">
<svg class="sankey-diagram__svg" viewBox="0 0 800 400">
<!-- Nos coluna 0 (sources) -->
<rect class="sankey-diagram__node-rect" x="0" y="20" width="24" height="360"
fill="#3B82F6" rx="4"/>
<text class="sankey-diagram__node-label" x="32" y="200">Orcamento Total</text>
<text class="sankey-diagram__node-value" x="32" y="218">R$ 10.000</text>
<!-- Nos coluna 1 (intermediarios) -->
<rect class="sankey-diagram__node-rect" x="388" y="20" width="24" height="180"
fill="#3B82F6" rx="4"/>
<text class="sankey-diagram__node-label" x="420" y="110">Sanidade Animal</text>
<rect class="sankey-diagram__node-rect" x="388" y="220" width="24" height="160"
fill="#10B981" rx="4"/>
<text class="sankey-diagram__node-label" x="420" y="300">Defesa Vegetal</text>
<!-- Links (paths Bezier) -->
<path class="sankey-diagram__link" d="M24,20 C200,20 200,20 388,20
L388,200 C200,200 200,380 24,380 Z" fill="#3B82F6" fill-opacity="0.3"/>
</svg>
</div>
<div class="sankey-diagram__legend">
<span class="sankey-diagram__legend-item">
<span class="sankey-diagram__legend-dot" style="background: #3B82F6"></span>
Sanidade Animal
</span>
<span class="sankey-diagram__legend-item">
<span class="sankey-diagram__legend-dot" style="background: #10B981"></span>
Defesa Vegetal
</span>
</div>
</div>DataLoader + Factory
<div id="sankey-container"></div>
<script type="module">
import { DataLoader } from '../core/data-loader.js';
import { renderSankeyDiagram } from '../components/sankey-diagram.js';
const data = await DataLoader.load({
endpoint: '/data/sankey/orcamento-distribuicao.json'
});
renderSankeyDiagram(
document.getElementById('sankey-container'),
data,
{ source: 'source', target: 'target', value: 'value', color: 'color' },
{ title: 'Distribuicao Orcamentaria — 2025', legend: true, tooltip: true }
);
</script>Auto-init via data attributes
<div data-arenito="sankey-diagram"
data-endpoint="/data/sankey/orcamento-distribuicao.json"
data-format="json"
data-title="Distribuicao Orcamentaria"
data-mapping='{"source":"source","target":"target","value":"value","color":"color"}'
data-legend="true"
data-tooltip="true">
</div>Estado vazio
<div class="sankey-diagram">
<h3 class="sankey-diagram__title">Fluxo de Recursos</h3>
<div class="sankey-diagram__empty">Nenhum dado encontrado para os filtros selecionados.</div>
</div>Fonte de Dados
O componente espera dados em formato JSON como um array de objetos com campos source, target, value e color.
JSON (formato recomendado)
Cada objeto no array representa um fluxo (link) entre dois nos. O componente constroi os nos automaticamente a partir dos nomes unicos em source e target.
[
{ "source": "Orcamento Total", "target": "Sanidade Animal", "value": 4500, "color": "#3B82F6" },
{ "source": "Orcamento Total", "target": "Defesa Vegetal", "value": 3200, "color": "#10B981" },
{ "source": "Orcamento Total", "target": "Inspecao", "value": 2300, "color": "#F59E0B" },
{ "source": "Sanidade Animal", "target": "Vacinacao", "value": 2000, "color": "#3B82F6" },
{ "source": "Sanidade Animal", "target": "Monitoramento", "value": 2500, "color": "#3B82F6" },
{ "source": "Defesa Vegetal", "target": "Controle de Pragas", "value": 1800, "color": "#10B981" },
{ "source": "Defesa Vegetal", "target": "Certificacao", "value": 1400, "color": "#10B981" }
]Mapeamento de campos
| Campo | Chave no mapping | Padrao | Descricao |
|---|---|---|---|
| Origem | source | 'source' | Nome do no de origem do fluxo |
| Destino | target | 'target' | Nome do no de destino do fluxo |
| Valor | value | 'value' | Valor numerico que define a espessura do link |
| Cor | color | 'color' | Cor hexadecimal do link e dos nos relacionados |
Opcoes de display
| Opcao | Tipo | Padrao | Descricao |
|---|---|---|---|
title | string | — | Titulo exibido acima do diagrama |
legend | boolean | true | Exibe a legenda abaixo do SVG |
tooltip | boolean | true | Habilita tooltip no hover de links e nos |
nodeWidth | number | 24 | Largura dos retangulos dos nos em pixels |
nodeGap | number | 8 | Espacamento vertical entre nos na mesma coluna |
linkOpacity | number | 0.3 | Opacidade dos paths de link (0 a 1) |
emptyText | string | 'Nenhum dado encontrado.' | Texto do estado vazio |
Dark Mode
O diagrama adapta automaticamente ao tema escuro via classe .dark.
No dark mode, os links reduzem a opacidade para evitar que cores vibrantes dominem a interface em fundo escuro. Os nos mantem suas cores mas com leve ajuste de brilho.
| Propriedade | Light | Dark |
|---|---|---|
| Link fill-opacity | 0.3 | 0.2 |
| Link hover opacity | 0.5 | 0.4 |
| Node rect opacity | 1.0 | 0.85 |
| Tooltip background | --surface-base-neutral-container-default | --surface-base-neutral-container-default |
| Tooltip shadow | rgba(0,0,0,0.12) | rgba(0,0,0,0.32) |
| Label color | --text-base-neutral-default | --text-base-neutral-default |
Os tokens semanticos de superficie e texto herdam automaticamente os valores do dark mode, sem necessidade de ajuste manual nos labels e tooltips.