Skip to content

Radar Chart

ChartsSince 0.4.0
import { RadarChart } from '@absoluteui/components/radar-chart';
import '@absoluteui/components/radar-chart/style.css';

Using Angular? See Radar Chart for Angular.

An accessible SVG radar (polar) chart that compares series across several quantitative axes arranged clockwise around a circle, starting at the top. It provides:

  • one closed polygon per series (translucent fill with fill), with a focusable vertex per axis;
  • a shared radial scale with rings, spokes, and ring tick labels (all aria-hidden);
  • keyboard navigation around the axes (Left/Right) and between series (Up/Down), with a tooltip on focus and hover;
  • a legend of toggle buttons, a generated summary, and a data table (one row per axis);
  • marker shapes and dash patterns (with patterns) as non-color cues (WCAG 1.4.1).

Axes keep clockwise order in RTL, like pie charts. Nothing animates.

  • Comparing the profile of 1-5 items across 3-12 comparable measures on the same scale (skills, product attributes, scores).
  • Precise comparison of values: use Bar Chart (grouped).
  • Measures on different scales or units (the scale is shared).
  • Many series (polygons overlap) or ordered data such as time: use Line Chart.

Two profiles on a fixed scale with series toggling

Section titled “Two profiles on a fixed scale with series toggling”
import { RadarChart, type RadarChartSeriesToggleDetail } from '@absoluteui/components/radar-chart';
const host = document.createElement('div');
document.body.append(host);
const chart = RadarChart.create(host, {
label: 'Candidate skills',
axes: ['Design', 'Frontend', 'Backend', 'Testing', 'Operations'],
series: [
{ id: 'ana', label: 'Ana', values: [4, 5, 3, 4, 2] },
{ id: 'ben', label: 'Ben', values: [2, 3, 5, 3, 4] },
],
valueAxis: { min: 0, max: 5, tickCount: 5 },
});
host.addEventListener('aui-series-toggle', (event) => {
const { hidden } = (event as CustomEvent<RadarChartSeriesToggleDetail>).detail;
host.dataset.hidden = hidden.join(',');
});
chart.setSeriesVisible('ben', false);
import { RadarChart } from '@absoluteui/components/radar-chart';
const host = document.querySelector<HTMLElement>('#coverage');
if (host) {
RadarChart.create(host, {
label: 'Test coverage by module',
axes: ['Auth', 'Billing', 'Search', 'Reports', 'Admin', 'API'],
series: [
{ id: 'unit', label: 'Unit', values: [0.9, 0.75, 0.6, null, 0.8, 0.95], color: 1 },
{ id: 'e2e', label: 'End-to-end', values: [0.6, 0.5, 0.4, 0.3, 0.2, 0.7], color: 4 },
],
valueAxis: { min: 0, max: 1 },
valueFormat: { style: 'percent' },
fill: false,
dataTable: 'visible',
});
}
import { RadarChart } from '@absoluteui/components/radar-chart';
import '@absoluteui/components/radar-chart/style.css';
const host = document.querySelector<HTMLElement>('#products');
if (host) {
RadarChart.create(host, {
label: 'Product comparison',
axes: ['Price', 'Quality', 'Support', 'Features', 'Speed'],
series: [
{ id: 'ours', label: 'Ours', values: [7, 8, 9, 6, 8] },
{ id: 'rival', label: 'Rival', values: [8, 6, 5, 8, 7] },
],
valueAxis: { min: 0, max: 10 },
});
}
import { RadarChart } from '@absoluteui/components/radar-chart';
const host = document.createElement('div');
document.body.append(host);
RadarChart.create(host, {
label: 'Skills',
axes: ['Speed', 'Power', 'Range', 'Armor'],
series: [
{ id: 'a', label: 'Alpha', values: [3, 5, 2, 4] },
{ id: 'b', label: 'Beta', values: [4, 2, 5, 1] },
],
});
<div class="aui-chart aui-radar-chart [aui-chart--patterns]">
<div class="aui-chart__header">title, description</div>
<ul class="aui-chart__legend" aria-label="Legend"><li><button aria-pressed="true">swatch, Alpha</button></li>...</ul>
<div class="aui-chart__body">
<svg class="aui-chart__svg" role="group" aria-roledescription="radar chart" aria-labelledby="T" aria-describedby="S">
<g class="aui-chart__grid" aria-hidden="true">rings (aui-radar-chart__ring), spokes</g>
<g class="aui-chart__axis" aria-hidden="true">ring ticks, axis names (aui-radar-chart__axis-label)</g>
<g class="aui-radar-chart__series-layer">
<g class="aui-radar-chart__series aui-chart__series--c1" aria-hidden="true"><path class="aui-chart__area"/><path class="aui-chart__line aui-chart__dash--1"/></g>
</g>
<g class="aui-radar-chart__points">
<g class="aui-radar-chart__series aui-chart__series--c1">
<path class="aui-chart__point aui-chart__mark" role="img" aria-label="Speed, Alpha: 3" tabindex="0"/>...
</g>
</g>
</svg>
<div class="aui-chart__tooltip" aria-hidden="true" hidden/>
</div>
<p class="aui-chart__summary" hidden/> <div class="aui-chart__data">Show data table, table</div>
</div>
RadarChart.create(host: HTMLElement, options: RadarChartOptions): RadarChart

Global configuration key: radarChart (see Configuration).

Option Type Default Description
label string Chart title: visible text and the accessible name of the plot (aria-labelledby); also the data table caption. Required.
hideLabel boolean false Visually hide the title; it stays the accessible name.
description string Visible description below the title, also referenced by aria-describedby (text only).
summary string Replaces the generated accessible summary (axes, scale, each series’ highest and lowest axis, hidden series).
valueFormat Intl.NumberFormatOptions Number format for values in tooltips, vertex labels, the data table, the summary, and ring ticks (ticks use fixed fraction digits).
formatValue (value: number) => string Custom value text; overrides valueFormat everywhere except ring ticks.
hiddenSeries readonly string[] Ids of series hidden initially; update() replaces the hidden set.
axes readonly string[] Axis names, clockwise from the top. At least three. Required.
series readonly ChartSeries[] Series: { id, label, values: (number | null)[], color?: 1-8 } with exactly one value per axis. null, NaN, and infinities are drawn at the center and read “No value”. Required.
valueAxis RadarChartValueAxis Shared radial scale { min?, max?, tickCount? (2-20, default 4 rings) }. min defaults to 0, or the data minimum when negative; max to the data maximum; nice-rounded unless min or max is fixed. label and includeZero are validated but not used by the radar chart.
legend 'auto' | 'show' | 'hide' "auto" auto: legend of toggle buttons when there is more than one series. Globally configurable.
dataTable 'toggle' | 'visible' | 'hidden' | 'none' "toggle" Data table fallback (one row per axis, one column per series): behind a Show data table button (built on first open), always visible, visually hidden (screen readers only), or none. Globally configurable.
tooltip boolean true Tooltip on pointer hover and keyboard focus. Globally configurable.
patterns boolean false Dash patterns on the polygon outlines so series never differ by color alone (always on in forced colors); marker shapes differ regardless. Globally configurable.
height number 300 Plot height in CSS px (integer 120-2000); the width follows the container. Globally configurable.
fill boolean true Fill each polygon with a translucent (12%) series color; the outline and vertices carry the data. Globally configurable.
locale string "en-US" Number and message locale. Globally configurable. Inherited from common configuration.
direction 'ltr' | 'rtl' "ltr" Axes keep clockwise order in RTL (like pie charts); rtl swaps ArrowLeft/ArrowRight so forward follows the reading direction. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement The host element.
element SVGSVGElement The plot (svg role=“group”), named by the title and described by the summary.
summary string The generated (or custom) accessible summary.
hiddenSeries string[] Ids of hidden series, in data order.
Method Description
update(options: Partial<RadarChartOptions>): void Merge, validate, and re-render. Invalid options throw and keep the previous state. Keyboard focus stays on the same vertex.
setSeriesVisible(id: string, visible: boolean): void Show or hide a series programmatically; emits no event.
destroy(): void Remove rendering, host classes, listeners, the resize observer, and the configuration subscription.
Event Detail Bubbles Cancelable Description
aui-series-toggle { id: string; visible: boolean; hidden: string[] } Yes No The user toggled a legend entry (pointer or keyboard). Not emitted for update() or setSeriesVisible().

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-chart Host (shared chart chrome).
aui-radar-chart Host modifier for this chart type.
aui-chart--patterns patterns option on: dash patterns shown.
aui-chart__header / __title / __title--hidden / __description Title and description.
aui-chart__legend / __legend-entry / __legend-item / __legend-item--hidden / __legend-label Legend list and toggle buttons.
aui-chart__swatch aria-hidden legend and tooltip key (marker and dash).
aui-chart__body / __svg / __layer Plot container (height from –aui-chart-height), SVG, and drawing layer.
aui-chart__grid / __gridline / aui-radar-chart__ring Rings and spokes (aria-hidden).
aui-chart__axis / __tick-label / aui-radar-chart__axis-label Ring tick labels and axis names (aria-hidden).
aui-chart__series--c1 ... --c8 Palette slot classes; set –aui-chart-color from color-chart-<n>.
aui-radar-chart__series-layer / __series / __points Polygon groups (aria-hidden) and the vertex groups.
aui-chart__area / __line / __dash--1 ... --4 Translucent polygon fill, outline, and dash patterns.
aui-chart__point Vertex marker (shape by series position).
aui-chart__mark / __mark--active / __mark--hover Focusable vertex and its states.
aui-chart__tooltip / __tooltip-title / __tooltip-rows / __tooltip-row / __tooltip-label / __tooltip-value Tooltip (aria-hidden; the focused vertex carries the same text).
aui-chart__empty Empty state text (No data, All series are hidden).
aui-chart__summary Hidden summary referenced by aria-describedby.
aui-chart__data / __data--hidden / __table-toggle / __table-scroll / __table / __cell--numeric Data table fallback.

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-border | --aui-color-border-strong | --aui-color-chart-1 | --aui-color-chart-2 | --aui-color-chart-3 | --aui-color-chart-4 | --aui-color-chart-5 | --aui-color-chart-6 | --aui-color-chart-7 | --aui-color-chart-8 | --aui-color-chart-axis | --aui-color-chart-grid | --aui-color-focus-ring | --aui-color-hover | --aui-color-link | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-muted | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-size-xs | --aui-font-weight-medium | --aui-font-weight-regular | --aui-font-weight-semibold | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-md | --aui-radius-sm | --aui-shadow-md | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-tooltip-max-width | --aui-touch-target-min

interface RadarChartConfig extends ChartBaseConfig { fill: boolean }
interface RadarChartValueAxis { label?: string; min?: number; max?: number; tickCount?: number; includeZero?: boolean }
interface RadarChartOptions extends ChartCommonOptions, Partial<RadarChartConfig> {
axes: readonly string[];
series: readonly ChartSeries[];
valueAxis?: RadarChartValueAxis;
}
type RadarChartSeriesToggleDetail = { id: string; visible: boolean; hidden: string[] };

Pattern: SVG radar chart as a named, described group (role=“group” with aria-roledescription “radar chart”) of role=“img” vertices with two-dimensional roving focus (around the axes, between series); legend of toggle buttons; data table fallback with one row per axis.

Key Context Action
ArrowRight / ArrowLeft Vertex focused Next (clockwise) / previous axis of the same series; swapped in RTL so forward follows the reading direction.
ArrowDown / ArrowUp Vertex focused Same axis in the next / previous visible series (single visible series: next / previous axis).
Home / End Vertex focused First (top) / last axis of the series.
PageUp / PageDown Vertex focused Ten axes back / forward (clamped to the ends).
Escape Tooltip visible Hides the tooltip without moving focus (WCAG 1.4.13); it returns on the next move.
Tab / Shift+Tab Any Legend buttons, then one tab stop for the plot (the active vertex), then the Show data table button and table region.
Enter / Space Legend button Shows or hides the series (native button).
Element Role
svg.aui-chart__svg group with aria-roledescription “radar chart” (localized), named by the title
vertices (path.aui-chart__point) img with an aria-label: axis, series, formatted value
legend list (ul) named “Legend”; entries are toggle buttons (aria-pressed)
data table table (native) with caption, column headers (Axis, then each series), and row headers (axes); wide tables scroll in a focusable region
rings, spokes, tick labels, axis names, polygons, tooltip aria-hidden (decorative duplicates of the accessible data)
Attribute Element Value
aria-labelledby svg id of the visible (or visually hidden) title
aria-describedby svg description id (when provided) and the generated summary id
aria-roledescription svg Localized “radar chart”
aria-label each vertex Localized “{axis}, {series}: {value}”, for example “Power, Beta: 2”; missing values read “No value”
tabindex active vertex 0 on exactly one vertex (roving); other vertices have none
aria-pressed legend button true while the series is visible
aria-expanded / aria-controls Show data table button Table open state and the table region id

Roving focus: one vertex is in the tab sequence and remembers the last visited vertex, also across re-renders (resize, update, series toggles). Toggling a legend entry keeps focus on its button; toggling the table keeps focus on the table button.

Criterion Level Note
1.1.1 Non-text Content A
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
1.4.10 Reflow AA
1.4.11 Non-text Contrast AA
1.4.13 Content on Hover or Focus AA
2.1.1 Keyboard A
2.4.3 Focus Order A
2.4.7 Focus Visible AA
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • The generated summary lists the type, series and axis counts, the axis names in order, the scale, each series’ highest and lowest axis, and hidden series, for example “radar chart, 2 series, 4 axes: Speed, Power, Range, and Armor. Scale {from} to {to}. Alpha: highest 5 on Power, lowest 2 on Range. …”
  • Meaning never depends on color alone: series differ by marker shape and (with patterns) dash pattern, and the legend keys repeat them.
  • Axes keep clockwise order in RTL (a radar has no reading axis, like pie charts); only ArrowLeft/ArrowRight are swapped.
  • Missing values are drawn at the center and read “No value”.
  • Forced colors: series use CanvasText and keep shapes and dashes; axis names use CanvasText; focus uses Highlight.
  • No animation.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver, TalkBack) is pending.
  • aria-roledescription support varies; screen readers without it announce “group”.
  • Polygon area depends on axis order and exaggerates differences; the summary and table give exact values.

default, unfilled (fill: false), empty (“No data”: no series or no finite values), all series hidden (“All series are hidden”), series hidden, vertex focused (tooltip), vertex hovered, table open.

From Trigger To
any legend button series hidden or shown; scale recomputed over visible series; aui-series-toggle
plot not focused Tab active vertex focused, tooltip shown
vertex focused arrow keys, Home, End, PageUp, PageDown another vertex focused
tooltip visible Escape tooltip hidden
  • The summary lists the type, counts, the axis names in order, the scale, each series’ highest and lowest axis, and hidden series: “radar chart, 2 series, 4 axes: Speed, Power, Range, and Armor. Scale … Alpha: highest 5 on Power, lowest 2 on Range. …”
  • Series differ by color, marker shape, and (with patterns) dash pattern; legend keys repeat them.
  • Missing values are drawn at the center and read “No value”.
  • Forced colors: series use CanvasText and keep shapes and dashes; focus uses Highlight.
  • No animation. Manual screen-reader verification is pending.

The width follows the container (ResizeObserver); the height is height. The radius fits the smaller dimension after reserving room for the axis names, which are truncated when space is short. Reflows at 320 CSS px.

Axes keep clockwise order from the top in RTL (a radar has no reading axis, like pie charts; decision C5). ArrowLeft/ArrowRight are swapped so “forward” follows the reading direction. The legend, tooltip, and table follow the document direction.

Series colors come from color-chart-1..8 by palette slot (series position, or color); each meets 3:1 against surfaces in all shipped themes. The 12% fill is decorative; outlines and vertices carry the data.

configureAbsoluteUI({ components: { radarChart: { fill: false, height: 360 } } });

Changes apply to existing instances immediately.

Instance options override global and theme defaults. All options are dynamic.

All text uses textContent; SVG uses createElementNS; colors are classes. No innerHTML or style attributes.

No persistence, network access, or telemetry.

Budget: RadarChart.create with 5 series x 12 axes < 30 ms in headless Chromium (e2e performance test), including the first render.

Up to about 5 series and 12 axes; more work but polygons overlap and labels crowd.

  • Missing values (null, NaN, infinities): the vertex is at the center and reads “No value”.
  • Values outside a fixed valueAxis.min/max are clamped to the center or the outer ring; labels and table keep the real value.
  • Negative data: the scale starts at the data minimum.
  • Hiding series recomputes the scale over the visible series; all hidden: “All series are hidden”.
Code Category Cause
RADAR_CHART_OPTIONS configuration Missing label; fewer than 3 axes or non-string axes; series ids not unique or empty; values not one per axis or non-numeric; invalid color, valueAxis (non-finite, max <= min, tickCount outside 2-20), or formatters.
CFG_INVALID_VALUE / CFG_UNKNOWN_OPTION configuration Invalid or unknown configuration.

A failed construction leaves the host untouched.

  • One shared radial scale; no per-axis scales.
  • Missing values collapse to the center instead of leaving a gap.
  • Manual screen-reader verification is pending.
  • Per-axis scales or units; polar area (rose) and radial bar charts.
  • Selection and brushing.