Polar Area Chart
ProNightingale rose / polar area chart with area-proportional (√) or linear radius scaling, padded wedges, labelled grid rings and a configurable start angle. Pure SVG.
import { SvPolarChartComponent } from '@styloviz-pro/charts';Demo & usage
Basic usage
ProUsage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Linear radius
ProPadded wedges
ProSector interactions
ProWhen to use
- Cyclical categories where the wrap-around matters — weekdays, months, compass bearings.
- Comparing magnitudes across equal-width categories with a shared origin.
- For non-cyclical categories, a bar chart is easier to read; for multi-series profiles, use the Radar chart.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the sectors are being fetched. |
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| emptyMessage | string | 'No data to plot' | Copy for the no-data state. |
| filtered | boolean | false | True when the emptiness is caused by a filter rather than an empty set. |
| sectors | readonly PolarSector[] | [] | Wedges to plot, drawn clockwise from `startAngle`. |
| valuePolicy | SvValuePolicy | 'omit' | What to do with a sector this chart cannot plot — a negative value, `NaN`, `Infinity`. Radius is the encoding here, and there is no radius below the centre, so a negative has no honest wedge. It also reaches the share the tooltip and the data table quote, so leaving it in the sum would misstate every OTHER sector's percentage. - `omit` (default) — drop the wedge, and disclose the count. The remaining wedges each widen, since the slice is `360 / count`. - `reject` — refuse the dataset and show the error state. `clamp` is not supported: a wedge at radius zero has no area to mark, and the table would print a flat `0` indistinguishable from a real zero. It falls back to `omit` with a dev warning. `render` likewise. |
| maxValue | number | 0 | Scale maximum. `0` (the default) rounds one out of the data. |
| scaleMode | PolarScaleMode | 'sqrt' | How a value becomes a radius. |
| size | number | 300 | Chart diameter in pixels. |
| levels | number | 4 | Number of concentric grid rings. |
| padAngle | number | 1 | Angular gap between wedges, in degrees. |
| startAngle | number | 0 | Where the first wedge starts, in degrees clockwise from twelve o'clock. |
| showLabels | boolean | true | Show the labels around the rim. |
| showScale | boolean | true | Show the value scale on the grid rings. |
| title | string | '' | Chart title, rendered above the plot. |
| ariaLabel | string | '' | Accessible name override. Falls back to a generated summary. |
| customClass | string | '' | Extra CSS classes applied to the root wrapper. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed load. |
| sectorHover | PolarSectorEvent | Emitted when a wedge is hovered or keyboard-focused. |
| sectorSelect | PolarSectorEvent | Emitted when a wedge is clicked or activated with Enter/Space. |
Wire it to your API
This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — drive it from whatever you already use.
Template
ProBound to the names this component actually exposes.
Host class
ProDrive the three async inputs from your own fetch.
- This component does not emit a query, so it renders what you give it. Fetch on your own trigger and bind `sectors`, `loading` and `error`.
- Set `filtered` when a search or filter is active. It is the difference between "you have no orders" and "no orders match this filter" — telling a customer with 4,000 orders the first one reads as a bug.
- Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.
Full walkthrough: Connecting to a backend.
Customization & theming
- The shape of the rose depends on the order of the sectors, and the chart says so under the plot: every sector gets the same angular slice, so the silhouette is decided by which values sit next to each other. Under `linear` scaling it also states that area is not the value — a wedge's area grows with the square of its radius, so a doubled value covers four times the ink. `sqrt`, the default, is what keeps area proportional.
- √ or linear radius scaling.
- Grid ring count and value labels.
- Pad angle between wedges and a custom start angle.
- Per-sector colours.
- `valuePolicy` decides what happens to a sector this chart cannot draw — a negative, `NaN` or `Infinity`. Radius is the encoding and there is no radius below the centre. `omit` (default) drops it and keeps it out of the share denominator; `reject` refuses the dataset. `clamp` is not offered: a wedge at the centre has no area to mark, and its table row would read as a genuine `0`, so a request for it falls back to `omit` with a dev warning.
Best practices
- Do: Leave `scaleMode` at `sqrt` so the AREA encodes the value — that is what a reader compares.
- Do: Order the sectors by their natural cycle, not by size.
- Don't: Do not use `linear` unless the domain reads that way by convention; it makes a doubled value cover four times the space.
- Tip: A single sector is drawn as a two-arc disc — one SVG arc cannot express a full revolution.
Accessibility
- Every wedge is a keyboard-focusable `role="button"` named with its value and share of the total.
- The plot is a labelled `role="group"`; Enter/Space emits `sectorSelect` and focus emits `sectorHover`.
- Under the default √ scale wedge AREA is the value, which no reader can invert by eye. `sv-data-summary-table` publishes each sector's value and share of the total, in the cycle order the wedges are drawn in — the order is part of the data on a polar chart.
- Whatever `valuePolicy` did is stated under the plot, not only in `console.warn`. An all-zero week is disclosed rather than replaced by the empty state: unlike a pie this chart still has its grid, its scale and every rim label, so all-zero is a legible picture of a real answer.
Feedback on Polar Area Chart
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.