Radial chart
Revenue by channel
Usage
Import the component and the stylesheet once. See Getting started to install the package.
import { RadialChartCard } from '@lilt-ui/charts';
import '@lilt-ui/charts/styles.css';<RadialChartCard
title="Revenue by channel"
data={channelRevenue}
category="channel"
value="revenue"
/>Omit title to leave out the visible title. Set header={false} to remove the whole header, and use aria-label to name a chart inside your own card. legend={false} removes the legend independently. surface="ghost" controls only the visual frame. See Card composition.
Data
category names a text field and value a numeric one; TypeScript rejects anything else. The headline is the total and each share is a slice’s part of it. null rows stay in the legend as “No data” but take no room in the circle. A slice can’t be negative, so a negative value shows an error instead of a misleading chart.
// One row per category, in any order. Names must be unique.
const channelRevenue = [
{ channel: 'Organic search', revenue: 42800 },
{ channel: 'Direct', revenue: 24100 },
{ channel: 'Referral', revenue: null }, // not reported: listed, not drawn
];
<RadialChartCard
title="Revenue by channel"
data={channelRevenue}
category="channel" // a text field
value="revenue" // a numeric field, never negative
limit={4} // four slices, the rest as "Other"
/>For a working period select, pass ranges. Each range brings its own data and delta.
<RadialChartCard
title="Revenue by channel"
category="channel"
value="revenue"
ranges={[
{ id: 'month', label: 'This month', data: thisMonth, delta: 0.124 },
{ id: 'last-month', label: 'Last month', data: lastMonth, delta: 0.038 },
]}
/>Nested donuts
Inner rings show departments; outer rings show their teams. Pinning retains a branch and its ancestors. Leaf-only paths prevent parent totals from being counted twice. All three loading styles share the skeleton exit, entrance, pointer/keyboard pinning, touch glide, reduced motion, and accessible readings table. Aggregate marks report null as their original datum.
import { NestedDonutCard } from '@lilt-ui/charts';
<NestedDonutCard
title="Operating spend"
data={expenseHierarchy}
path="path"
value="spend"
valueFormat={{ "style": "currency", "currency": "USD", "maximumFractionDigits": 0 }}
/>| Prop | Type | Description |
|---|---|---|
title | string | Optional visible title. Omit to render no title. Also supplies the accessible name unless aria-label is set. |
header | boolean | Defaults to true. False removes the whole header: title, headline, delta and period controls, with no reserved space. Pass data directly when your app owns period selection. |
aria-label | string | Accessible chart name, independent of the visible title. Falls back to title, then a generic chart name. |
data | Row[] | One row per observation. Omit when using ranges. |
height | number | Minimum plot height, default 260. Dense lanes expand to keep every mark visible. |
depth | boolean | Lit tiles, solid bars, or spheres. Data positions remain exact; loading keeps the same depth treatment. |
color | string | Override the palette with one CSS color. |
aggregate | 'sum' | 'mean' | 'max' | Resting headline summary; Strip defaults to mean, the others to sum. |
headline | number | Override the resting headline. |
delta | number | Fractional change shown alongside the headline. |
deltaTone | 'default' | 'inverse' | 'neutral' | Use inverse when a decrease is good. |
range | string | Static period label. |
ranges | { id, label, data, headline?, delta? }[] | Period choices with their own data. |
defaultRange | string | Initial period id. |
locale | string | Locale for numbers and dates; defaults to en-US. |
formatValue | (value: number) => string | Custom value formatter. |
badge | ReactNode | Content for the glass tab above the card. |
className | string | Class on the card frame. |
style | CSSProperties | ChartStyle | Inline styles and scoped chart variables. |
onSelectionChange | (selection | null) => void | Reports id, original row (or null for an absent day), value, and pinned state. |
renderReadout | (selection | null) => ReactNode | Consumer-owned readout, including when the header is hidden. |
valueFormat | Intl.NumberFormatOptions | Formats the headline, tiles, and axis, e.g. currency. |
surface | 'elevated' | 'outline' | 'ghost' | Card treatment. Ghost removes the visual frame only; header and legend are independent controls. |
palette | 'iris' | 'cobalt' | 'emerald' | Series colors. Iris by default; explicit series colors still win. |
numberStyle | 'count' | 'pop' | 'slide' | 'roll' | 'flow' | 'scramble' | How the headline animates when its value changes. Count (default) counts to the new value. |
loading | boolean | Shows a neutral loading skeleton, not data. |
loadingStyle | 'shimmer' | 'draw' | 'breathe' | Shimmer sweeps a highlight over still shapes; draw reveals them in order; breathe lets them settle. Follows the selected look, including depth, and respects reduced motion. |
empty | 'dots' | 'shape' | ReactElement | null | What the card shows when the period has no data. Dots (default) gathers a quiet dot field around the message; shape lays the chart’s own outline behind it. Pass `<ChartEmpty>` with your own words, any element of your own, or null for nothing. Both looks stay still, so empty never reads as loading, and the headline reads a dash, never zero. |
motion | 'auto' | 'none' | None turns off decorative motion; reduced motion does this automatically. |
legend | false | 'tiles' | 'inline' | 'list' | 'pills' | 'bars' | Legend layout; false hides it. Correlation defaults to false. |
legendSwatch | 'square' | 'dot' | 'line' | Legend marker, square by default. |
path | text key of Row | Unique leaf paths separated by /; up to eight levels. |
value | numeric key of Row | Nonnegative leaf area. Missing leaves contribute no known area. |
hole | number | Inner hole as a fraction of radius, default 0.3. |
onKeyDown | KeyboardEventHandler<HTMLElement> | Keyboard handler on the card frame. Chart inspection also has its own keyboard handling. |
Props
Every prop RadialChartCard accepts, in the order you are likely to reach for them.
| Prop | Type | Description |
|---|---|---|
title | string | Optional visible title. Omit to render no title. Also supplies the accessible name unless aria-label is set. |
header | boolean | Defaults to true. False removes the whole header: title, headline, delta and period controls, with no reserved space. Pass data directly when your app owns period selection. |
aria-label | string | Accessible chart name, independent of the visible title. Falls back to title, then a generic chart name. |
data | Row[] | One row per category. Omit when using ranges. |
category | text key of Row | Names each slice. Must be unique. |
value | numeric key of Row | Sizes each slice. Never negative; null is No data. |
variant | 'donut' | 'pie' | 'semi' | 'rings' | Donut (default) shows the share in the middle, pie fills the circle, semi is a half donut, and rings give each category its own ring. |
legend | false | 'tiles' | 'inline' | 'list' | 'pills' | 'bars' | False removes the legend without changing the data. Ranked rows with shares (list), value tiles, an inline key, tinted pills, or bars against the largest. Rings default to tiles, semi to inline. |
legendSwatch | 'square' | 'dot' | 'line' | The mark beside each legend label. Defaults to a rounded square. |
limit | number | Slices before the rest become “Other”. Defaults to 5; Infinity shows all. |
otherLabel | string | Label of the combined slice. |
sort | 'descending' | 'ascending' | 'input' | Largest first by default; input keeps your order. |
headline | number | Resting headline. Defaults to the total of all slices. |
delta | number | Fractional change for the chip. |
deltaTone | 'default' | 'inverse' | 'neutral' | Use inverse when a decrease is good. |
range | string | Static period label in the header. |
selected | string | null | The pinned category, by name, when you own the selection, so outside filters and a click on the chart share it. `null` pins nothing; omit it and the card keeps its own pin. |
onSelectedChange | (category: string | null) => void | Asked when a click, tap, or Escape pins or releases a category. |
colors | Record<string, string> | Fixed colors by category name, e.g. `{ Critical: "#ef4444" }`. Others take the palette and keep it as values change. |
center | ({ category, value, share, active, pinned }) => ReactNode | Replaces the middle of a donut or half donut, which by default shows the share of the hovered, pinned, or largest category. |
ranges | { id, label, data, delta?, headline? }[] | Turns the period label into a select. Each range swaps the rows. |
depth | boolean | Lights every slice or ring as a tube, makes ring tracks grooves, and adds a soft shadow. Outlines keep their exact angles. |
valueFormat | Intl.NumberFormatOptions | Formats the headline, tiles, and axis, e.g. currency. |
surface | 'elevated' | 'outline' | 'ghost' | Card treatment. Ghost removes the visual frame only; header and legend are independent controls. |
palette | 'iris' | 'cobalt' | 'emerald' | Series colors. Iris by default; explicit series colors still win. |
numberStyle | 'count' | 'pop' | 'slide' | 'roll' | 'flow' | 'scramble' | How the headline animates when its value changes. Count (default) counts to the new value. |
loading | boolean | Shows a neutral loading skeleton, not data. |
loadingStyle | 'shimmer' | 'draw' | 'breathe' | Shimmer sweeps a highlight over still shapes; draw reveals them in order; breathe lets them settle. Follows the selected look, including depth, and respects reduced motion. |
empty | 'dots' | 'shape' | ReactElement | null | What the card shows when the period has no data. Dots (default) gathers a quiet dot field around the message; shape lays the chart’s own outline behind it. Pass `<ChartEmpty>` with your own words, any element of your own, or null for nothing. Both looks stay still, so empty never reads as loading, and the headline reads a dash, never zero. |
motion | 'auto' | 'none' | None turns off decorative motion; reduced motion does this automatically. |
defaultRange | string | Initial range id; defaults to the first supplied range. |
locale | string | Locale used for number formatting. Defaults to en-US. |
formatValue | (value: number) => string | Custom value text, taking precedence over valueFormat. |
badge | ReactNode | Content in the tab above the card. A comparison badge can replace this while a comparison is active. |
className | string | Additional class on the card frame. |
style | CSSProperties | ChartStyle | Inline styles and scoped --lilt-* chart variables. |