Radar Chart
Compare séries entre múltiplos eixos que compartilham um único domínio radial.
Visão geral
RadarChart é o wrapper estilizado sobre o RadarChart do recharts, composto sobre o primitive chart do shadcn (ChartContainer). Cada linha de dados (xKey) se torna um raio no grid polar; cada entrada em config se torna um polígono fechado que compartilha um único máximo radial. Use quando você tem três a sete dimensões que devem ser avaliadas juntas - perfis de habilidade, scores de satisfação, análise de gap de capacidade.
Use RadarChart quando nenhuma dimensão única é o destaque e a forma do polígono carrega significado por todo o conjunto. Para comparar uma dimensão ordenada, LineChart lê mais rápido. Para ranquear categorias em uma única métrica, BarChart é mais legível.
A escala radial é fixa em 100 por padrão. Isso é deliberado: comparação é o trabalho usual deste chart, e uma escala derivada do dado faz alguém com nota máxima 70 preencher o gráfico igual a alguém que tirou 100. Passe um número para outro teto, ou max="auto" quando a faixa não for conhecida de antemão.
A edição React (@gremorie/rx-data) renderiza com recharts dentro do primitive chart do shadcn; a edição Angular (@gremorie/ng-data) renderiza geometria d3 headless dentro de um SVG estilizado. O motor de renderização difere; a superfície pública é idêntica input-por-input.
Anatomia
<radar-chart>
└─ figure (role="img") card surface, aspect-square
├─ svg
│ ├─ polar grid (gridType: polygon | circle, ring count from ticks)
│ ├─ angle axis (spokes from xKey, wrapped to labelWidth)
│ ├─ radius axis ticks (optional via radiusAxis)
│ ├─ radar polygons, one per config key (fill: auto | on | off)
│ ├─ vertex dots, one per row per series (optional via dots)
│ └─ active dot, one per series, on the hovered spoke
├─ tooltip overlay (optional via tooltip)
├─ legend (when config has >1 series)
└─ sr-only data tableA edição React compõe esta superfície através do primitive chart do shadcn:
RadarChart
└─ ChartContainer responsive square frame + injects --color-<key>
└─ RadarChart (recharts) polar plot area over the angular axis (xKey)
├─ ChartTooltip hover card (ChartTooltipContent)
├─ PolarAngleAxis spoke labels (xKey, wrapped to labelWidth)
├─ PolarRadiusAxis the scale: max, ticks, radiusAxis
├─ PolarGrid polygon or circular grid (gridType)
└─ Radar one polygon per series, with dot / activeDotPreview
- Desktop
- Mobile
| month | Desktop | Mobile |
|---|---|---|
| Jan | 186 | 80 |
| Feb | 305 | 200 |
| Mar | 237 | 120 |
| Apr | 173 | 190 |
| May | 209 | 130 |
| Jun | 214 | 140 |
'use client';import { RadarChart, type ChartConfig, type ChartDatum,} from '@gremorie/rx-data';const monthlyData: ChartDatum[] = [ { month: 'Jan', desktop: 186, mobile: 80 }, { month: 'Feb', desktop: 305, mobile: 200 }, { month: 'Mar', desktop: 237, mobile: 120 }, { month: 'Apr', desktop: 173, mobile: 190 }, { month: 'May', desktop: 209, mobile: 130 }, { month: 'Jun', desktop: 214, mobile: 140 },];const monthlyConfig: ChartConfig = { desktop: { label: 'Desktop', color: 'var(--chart-1)' }, mobile: { label: 'Mobile', color: 'var(--chart-2)' },};export function RadarChartPreview() { // These counts run to 305, past the 100 the scale is pinned at by default. // `auto` is the right answer for a range like this one, which has no natural // ceiling — a hardcoded top would need revisiting every time the data moves. return ( <RadarChart data={monthlyData} config={monthlyConfig} xKey="month" max="auto" /> );}Instalação
bash npx gremorie@latest add rx-radar-chart bash pnpm dlx gremorie@latest add rx-radar-chart
bash yarn dlx gremorie@latest add rx-radar-chart
bash bunx --bun gremorie@latest add rx-radar-chart
Uso
import { RadarChart } from "@gremorie/rx-data";
import type { ChartConfig, ChartDatum } from "@gremorie/rx-data";
const data: ChartDatum[] = [
{ skill: "Speed", a: 90, b: 60 },
{ skill: "Power", a: 70, b: 80 },
{ skill: "Stamina", a: 85, b: 70 },
{ skill: "Agility", a: 75, b: 90 },
{ skill: "Skill", a: 95, b: 65 },
];
const config: ChartConfig = {
a: { label: "Player A", color: "var(--chart-1)" },
b: { label: "Player B", color: "var(--chart-2)" },
};
export function PlayerComparison() {
return <RadarChart data={data} config={config} xKey="skill" />;
}npx gremorie@latest add ng-radar-chartPré-requisito
Os componentes Angular emitem classes utilitárias do Tailwind e não embarcam CSS compilado. Sem o setup abaixo em src/styles.css, o componente monta mas renderiza sem estilo nenhum. Requer Tailwind CSS v4.
@import 'tailwindcss';@import '@gremorie/tokens/theme.css';/* Required. Tailwind v4 skips node_modules by default, so without this line the components' utility classes never reach your compiled CSS and every Gremorie component renders unstyled. */@source '../node_modules/@gremorie';A linha @source é obrigatória: o Tailwind v4 ignora node_modules por padrão. Ajuste o caminho relativo se o seu stylesheet não estiver em src/styles.css. Setup completo em Instalação.
import { Component } from '@angular/core';
import { RadarChart } from '@gremorie/ng-data';
import type { ChartConfig, ChartDatum } from '@gremorie/ng-data';
@Component({
selector: 'app-player-comparison',
imports: [RadarChart],
template: `<radar-chart [data]="data" [config]="config" xKey="skill" />`,
})
export class PlayerComparisonComponent {
readonly data: ChartDatum[] = [
{ skill: 'Speed', a: 90, b: 60 },
{ skill: 'Power', a: 70, b: 80 },
{ skill: 'Stamina', a: 85, b: 70 },
{ skill: 'Agility', a: 75, b: 90 },
{ skill: 'Skill', a: 95, b: 65 },
];
readonly config: ChartConfig = {
a: { label: 'Player A', color: 'var(--chart-1)' },
b: { label: 'Player B', color: 'var(--chart-2)' },
};
}API
<RadarChart>
O wrapper estilizado sobre o RadarChart do recharts, composto sobre ChartContainer. Renderiza o frame responsivo, um PolarGrid, o PolarAngleAxis (labels dos raios), um tooltip opcional e um <Radar> por chave de config colorido com var(--color-<key>).
| Prop | Type | Default | Descrição |
|---|---|---|---|
data | ChartDatum[] | - | Dados tabulares. Uma linha por raio. Cada linha carrega o label do raio em xKey mais um valor numérico por chave de config. |
config | ChartConfig | - | Mapeia cada chave de série para { label, color }. A color é qualquer cor CSS ou token, tipicamente "var(--chart-1)" até "var(--chart-5)". ChartContainer injeta cada uma como uma CSS variable var(--color-<key>). |
xKey | string | - | Nome do campo do raio (ângulo) em cada linha. Determina os labels do PolarAngleAxis ao redor do perímetro. |
gridType | "polygon" | "circle" | "polygon" | Forma dos anéis do PolarGrid - polígono de bordas retas (padrão) ou círculos concêntricos. |
max | number | "auto" | 100 | Topo da escala radial, ou "auto" para seguir o maior valor do dado. Fixo para que dois gráficos sejam lidos na mesma régua: com escala derivada, uma nota máxima de 70 preenche o gráfico igual a uma de 100. Isso é certo para porcentagem ou nota e errado acima de 100 — passe um número para essa faixa, ou "auto" quando a faixa não for conhecida de antemão. |
ticks | number | 4 | Número de anéis da grade, e de marcas no eixo radial. |
radiusAxis | boolean | false | Rotula cada anel com seu valor, subindo pela vertical. |
labelWidth | number | - | Quebra os rótulos dos eixos nesta largura em pixels, por palavra. Sem isso, um rótulo longo na lateral do círculo é desenhado em uma linha só, atravessando metade do gráfico. |
dots | boolean | false | Desenha um ponto em cada vértice. O raio sob o mouse ganha um ponto maior de qualquer forma. |
fill | "auto" | "on" | "off" | "auto" | Preenchimento do polígono. auto preenche uma série sozinha e apenas contorna duas ou mais, para que séries sobrepostas continuem legíveis. |
tooltip | boolean | true | Alterna o tooltip de hover. |
className | string | - | Mesclado no ChartContainer. O estilo empacotado centraliza o gráfico com um aspect quadrado. |
Compondo com o primitive chart
Para controle total, componha recharts diretamente dentro de ChartContainer usando os primitives exportados. Isso espelha o bloco de radar oficial do shadcn.
| Export | Papel |
|---|---|
ChartContainer | Frame responsivo; injeta as vars --color-<key> por série a partir do config. |
ChartTooltip | Reexport do Tooltip do recharts; combine com ChartTooltipContent. |
ChartTooltipContent | Corpo de tooltip estilizado. |
ChartLegend | Reexport do Legend do recharts; combine com ChartLegendContent. |
ChartLegendContent | Corpo de legenda estilizado. |
useChart | Hook para ler o config ativo dentro de um child customizado. |
Types
| Type | Shape |
|---|---|
ChartDatum | Record<string, string | number> & { fill?: string } - uma linha de dados do chart. |
ChartConfig | Record<string, { label?: ReactNode; color?: string }> - mapa de label e cor por série. |
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
type ChartDatum,
} from '@gremorie/rx-data';
import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from 'recharts';
const config = {
a: { label: 'Player A', color: 'var(--chart-1)' },
b: { label: 'Player B', color: 'var(--chart-2)' },
} satisfies ChartConfig;
export function Example({ data }: { data: ChartDatum[] }) {
return (
<ChartContainer
config={config}
className="mx-auto aspect-square max-h-[250px]"
>
<RadarChart accessibilityLayer data={data}>
<ChartTooltip cursor={false} content={<ChartTooltipContent />} />
<PolarAngleAxis dataKey="skill" />
<PolarGrid gridType="polygon" />
<Radar
dataKey="a"
fill="var(--color-a)"
fillOpacity={0}
stroke="var(--color-a)"
strokeWidth={2}
/>
<Radar
dataKey="b"
fill="var(--color-b)"
fillOpacity={0}
stroke="var(--color-b)"
strokeWidth={2}
/>
</RadarChart>
</ChartContainer>
);
}Desça a este nível para traços tracejados, preenchimentos de gradiente, dots marcadores nas interseções dos raios ou uma legenda customizada via ChartLegend + ChartLegendContent.
Composição
- Escolha o
xKey- o campo de label do raio. Três a sete raios é o ponto ideal; menos e você obtém um triângulo que esconde a forma, mais e os eixos se aglomeram. - Escreva o
configcom no máximo três séries. Os polígonos permanecem legíveis quando traçados; quatro ou mais ficam difíceis de ler. - Ordene os raios consistentemente entre gráficos na mesma superfície para que os usuários possam comparar formas por memória muscular.
- Escolha o
gridType-polygon(padrão) segue a geometria dos raios;circlelê como um alvo/dartboard quando o alinhamento exato dos raios importa menos. - Decida a escala. Deixe
maxem100para porcentagem ou nota; passe um número para um teto conhecido; use"auto"quando a faixa vier de algum lugar que você não controla. Um topo fixo não é limite suave - o que passar dele desenha fora do anel externo. - Dê largura aos rótulos longos. Acima de quatro ou cinco raios, o
labelWidthé o que impede uma categoria do tamanho de uma frase de atravessar o gráfico.
O chart se auto-dimensiona ao pai através do frame responsivo do ChartContainer, com um aspect quadrado para que os polígonos permaneçam simétricos.
Variações
Comparação multi-série
- Desktop
- Mobile
| month | Desktop | Mobile |
|---|---|---|
| Jan | 186 | 80 |
| Feb | 305 | 200 |
| Mar | 237 | 120 |
| Apr | 173 | 190 |
| May | 209 | 130 |
| Jun | 214 | 140 |
'use client';import { RadarChart, type ChartConfig, type ChartDatum,} from '@gremorie/rx-data';const monthlyData: ChartDatum[] = [ { month: 'Jan', desktop: 186, mobile: 80 }, { month: 'Feb', desktop: 305, mobile: 200 }, { month: 'Mar', desktop: 237, mobile: 120 }, { month: 'Apr', desktop: 173, mobile: 190 }, { month: 'May', desktop: 209, mobile: 130 }, { month: 'Jun', desktop: 214, mobile: 140 },];const monthlyConfig: ChartConfig = { desktop: { label: 'Desktop', color: 'var(--chart-1)' }, mobile: { label: 'Mobile', color: 'var(--chart-2)' },};export function RadarChartPreview() { // These counts run to 305, past the 100 the scale is pinned at by default. // `auto` is the right answer for a range like this one, which has no natural // ceiling — a hardcoded top would need revisiting every time the data moves. return ( <RadarChart data={monthlyData} config={monthlyConfig} xKey="month" max="auto" /> );}const config: ChartConfig = {
a: { label: 'Player A', color: 'var(--chart-1)' },
b: { label: 'Player B', color: 'var(--chart-2)' },
};
<RadarChart data={data} config={config} xKey="skill" />;<radar-chart [data]="data" [config]="config" xKey="skill" />A forma mais comum. Duas séries traçadas deixam o usuário comparar as pegadas dos polígonos num relance. Mantenha a contagem em duas ou três antes que os polígonos sobrepostos derrotem a comparação.
Perfil de série única
<RadarChart
data={data}
config={{ score: { label: 'Score', color: 'var(--chart-1)' } }}
xKey="dimension"
/><radar-chart [data]="data" [config]="config" xKey="dimension" />Para perfis de sujeito único - um jogador, um produto, um time. Um único polígono preenchido lê limpo mesmo com cinco ou seis raios.
Escala com nota
- Self
- Manager
- Peers
| skill | Self | Manager | Peers |
|---|---|---|---|
| Discovery | 82 | 74 | 68 |
| Prototyping | 74 | 66 | 71 |
| Visual craft | 66 | 79 | 83 |
| Product knowledge | 58 | 62 | 55 |
| Problem framing | 77 | 58 | 64 |
| Communication | 85 | 81 | 88 |
| Collaboration | 69 | 84 | 79 |
| Autonomy | 61 | 70 | 58 |
| Delivery | 73 | 67 | 76 |
'use client';import { RadarChart, type ChartConfig, type ChartDatum,} from '@gremorie/rx-data';// A competency review: every value is a 0-100 score, and the same nine// dimensions are rated by three different sources.const reviewData: ChartDatum[] = [ { skill: 'Discovery', self: 82, manager: 74, peers: 68 }, { skill: 'Prototyping', self: 74, manager: 66, peers: 71 }, { skill: 'Visual craft', self: 66, manager: 79, peers: 83 }, { skill: 'Product knowledge', self: 58, manager: 62, peers: 55 }, { skill: 'Problem framing', self: 77, manager: 58, peers: 64 }, { skill: 'Communication', self: 85, manager: 81, peers: 88 }, { skill: 'Collaboration', self: 69, manager: 84, peers: 79 }, { skill: 'Autonomy', self: 61, manager: 70, peers: 58 }, { skill: 'Delivery', self: 73, manager: 67, peers: 76 },];const reviewConfig: ChartConfig = { self: { label: 'Self', color: 'var(--chart-1)' }, manager: { label: 'Manager', color: 'var(--chart-2)' }, peers: { label: 'Peers', color: 'var(--chart-3)' },};export function RadarChartScoredPreview() { return ( <RadarChart data={reviewData} config={reviewConfig} xKey="skill" // Pinned, not derived: every review has to be read against the same // ruler, or someone whose best score is 70 looks identical to someone // who scored 100. max={100} ticks={10} radiusAxis // Nine spokes with two-word labels; without a width the longest is drawn // as one run and reaches back across the plot. labelWidth={90} dots /> );}Tudo o que o chart oferece para uma leitura comparativa, numa superfície só: escala fixa em 0-100 com um anel a cada dez e cada anel rotulado, rótulos de raio quebrados para que nove caibam, e um ponto em cada vértice. É a forma que uma avaliação de competência pede - três fontes avaliando as mesmas dimensões, lidas na mesma régua.
<radar-chart
[data]="data"
[config]="config"
xKey="skill"
[max]="100"
[ticks]="10"
[radiusAxis]="true"
[labelWidth]="90"
[dots]="true"
/>O max={100} é a prop que sustenta o resto. Sem ele a escala segue o dado, e duas avaliações deixam de ser comparáveis: quem tem nota máxima 70 preenche o gráfico igual a quem tirou 100.
Deixando o dado definir a escala
<RadarChart data={data} config={config} xKey="metric" max="auto" /><radar-chart [data]="data" [config]="config" xKey="metric" max="auto" />Quando a faixa não é conhecida de antemão - saída de modelo, uma API que você não controla, contagens em vez de notas - o "auto" mede contra o maior valor presente. O domínio é arredondado para um limite legível, então o polígono mais externo fica logo dentro do anel, não exatamente sobre ele.
Preenchido ou contornado
<RadarChart data={data} config={config} xKey="skill" fill="on" /><radar-chart [data]="data" [config]="config" xKey="skill" fill="on" />O fill é "auto" por padrão: uma série sozinha é preenchida, duas ou mais são contornadas, porque preenchimentos translúcidos empilhados embaçam rápido. Force nos dois sentidos com "on" ou "off" - forçar ligado acima de uma série clareia o preenchimento para a pilha continuar legível.
Grid de polígono
O gridType padrão. Anéis de polígono de bordas retas seguem os raios, enfatizando a geometria por raio.
<RadarChart data={data} config={config} xKey="skill" gridType="polygon" /><radar-chart [data]="data" [config]="config" xKey="skill" gridType="polygon" />Grid de círculo
Defina gridType como "circle" para círculos concêntricos que leem como um alvo ou dartboard, mantendo os anéis como uma escala radial neutra quando o alinhamento exato dos raios importa menos.
<RadarChart data={data} config={config} xKey="skill" gridType="circle" /><radar-chart [data]="data" [config]="config" xKey="skill" gridType="circle" />Acessibilidade
- o
accessibilityLayerdo recharts adiciona navegação por teclado e anúncios de screen reader para as séries plotadas. - os tokens
--chart-1até--chart-5vêm com contraste WCAG AA contra o fundo do card em ambos os temas claro e escuro. - Labels dos raios: cada tick do
PolarAngleAxisé renderizado como<text>SVG ao redor do polígono, mantendo o mapeamento cor-para-série legível junto com o tooltip. - Limite as séries: mantenha a contagem baixa. Polígonos contornados suportam mais que preenchidos, mas acima de cinco séries a paleta categórica para de atribuir cor distinta - da sexta em diante vem um neutro apagado, em vez de repetir a primeira cor e pintar duas séries iguais.
- Hover nunca é obrigatório para ler um valor: o tooltip lê o raio inteiro de uma vez, o alvo é a cunha toda e não o vértice, e todo valor também está na tabela
sr-only.
Relacionados
- LineChart - use uma linha quando uma dimensão ordenada é o destaque.
- BarChart - use barras quando ranquear categorias em uma única métrica.
- RadialChart - layout polar irmão quando as categorias devem ser mostradas como arcos concêntricos.