import { RingChart } from "@elabs-ai/components-charts";npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-chartsPrompt for your coding agent
Paste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.
Add the brand-ui RingChart chart (from @elabs-ai/components-charts) to this project. 1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`. 2. Look RingChart up before writing code: `npx -y @elabs-ai/components-cli docs RingChart` prints its real props, variants, what it pairs with and what to avoid. 3. If @elabs-ai/components-charts is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts`). If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`. 4. Use it here: [the screen or file, and what it should show] 5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop. 6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.
Use it for
- What is it made of? Several progress values.
- one proportion against its maximum, read as a single ring
Avoid
- several categories matter — use a pie or unit chart
Examples
Default
Loading
Loading skeleton (RM-183) — shown while `status="loading"`, sized like the real chart.
Fixed Size
Fixed pixel size — bypasses ParentSize and uses a concrete dimension.
With Accessible Label
Accessible variant — announces label + description to screen readers on focus.
Partial Fill
Fewer rings at different fill levels.
Drilldown
Click a ring — or Tab in and press Enter — to drill into it.
Tick Ring
Selection states
API
RingChart
| Prop | Type | Description |
|---|---|---|
datarequired | RingData[] | Data array - each item represents a ring |
size | number | Chart size in pixels. If not provided, uses parent container size |
plotHeight | Responsive<ChartPlotHeight> | The plot's own height (ADR 0039): px, or `{ aspect }` (width ÷ height), optionally per breakpoint. |
strokeWidth | number | Stroke width of each ring. Default: 12 |
ringGap | number | Gap between rings. Default: 6 |
baseInnerRadius | number | Inner radius of the innermost ring. Default: 60 |
animationDuration | number | How long each ring's enter animation (the ring growing in, then its progress arc sweeping) runs, in milliseconds. The gaps between rings are set by `enterStaggerScale`, not by this. Ignored when `enterTransition` is set. Default: 1100 |
className | string | Additional class name for the container |
hoveredIndex | number | null | Controlled hover state - index of hovered ring |
onHoverChange | (index: number | null) => void | Callback when hover state changes |
startAngle | number | Start angle in radians. Default: -PI/2 (top) |
endAngle | number | End angle in radians. Default: 3*PI/2 (full circle) |
enterTransition | Transition | Framer Motion transition for ring enter animation. Wins over `animationDuration`. |
enterStaggerScale | number | Scales ring stagger delays (1 = default). |
geometryScrubbing | boolean | High-frequency geometry updates (e.g. studio NumberField scrub). Uses plain SVG paths instead of Motion `d` morphing. |
childrenrequired | ReactNode | Child components (Ring, RingCenter, etc.) |
onDatapointClick | ChartDatapointClickHandler | Drill-down (#349). Fires when a ring is activated by pointer OR keyboard. Setting it mounts a keyboard-operable target layer OUTSIDE the aria-hidden SVG — one tab stop, arrow keys to traverse. Unset changes nothing. |
copyValueOnActivate | boolean | Put the datapoint's exact value on the clipboard when it is activated — the recovery path for a compact axis label. Default `false`; a chart with no interaction props still renders byte-identical DOM. A consumer-supplied `onDatapointClick` always wins. |
datapointLabel | ChartDatapointLabel | Override the accessible name of each keyboard drill-down target (#349). |
maxInteractiveDatapoints | number | Dev-warning threshold on the number of keyboard targets. Default 500 (#349). |
accessibleLabel | ChartA11yProps["accessibleLabel"] | Accessible name for the chart region (announces to AT on focus). |
accessibleDescription | ChartA11yProps["accessibleDescription"] | Supplemental description read by AT (e.g. ring names + values). |
labels | "outside" | { placement?: "outside" | "none" } | Outside-label mode for the high-decoration tick ring (lieflat F4 "Tick Donut", #RM-030). `"outside"` draws a dotted leader line from each segment's ticks to its label, placed outside the ring, and reserves layout room for it. Only takes visible effect at `--decoration` ≥ 8 (the smooth-arc rendering below that threshold has no leader/label treatment); unset renders exactly as before. Also accepts `PieChart`'s `labels` OBJECT shape (RM-114 API parity only — `{ placement: "outside" }` maps to this same tick-ring mode; every other `placement`/`show`/`matchColor`/`minAngle` field is a no-op here, `RingChart` has no inside-label or leader-collision treatment of its own). `{ placement: "none" }` and every other object shape behave as unset. |
palette | ChartPalette | Colour ramp for the rings (RM-186), through `resolvePalette`; an item's own `color` still wins. Unset: the twelve categorical colours, cycled, as before. |
selectionStates | ChartSelectionStatesResolver<Record<string, unknown>> | Maps a mark (category, optional series key, optional raw row) to its selection state. Unset → the chart renders exactly as before. |
dimExcluded | boolean | Dim `excluded` marks and add their non-hue channel (the dashed frame). Default `true`; `false` keeps the `data-selection` attribute (so a host can style it) but paints nothing. |
margin | number | Partial<Margin> | Space around the plot, in pixels: one number for every side, or per side. |
status | ChartStatus | Show the loading skeleton until the data is ready. |
empty | ChartEmptyState | Title and message shown when there is nothing to plot. |
Ring
| Prop | Type | Description |
|---|---|---|
indexrequired | number | |
color | string | |
animate | boolean | |
showGlow | boolean | |
lineCap | RingLineCap |
RingCenter
| Prop | Type | Description |
|---|---|---|
defaultLabel | string | Label shown below the value. Default: "Total" when not hovering |
formatOptions | ChartStatFlowFormat | Format options for NumberFlow. Default: standard notation |
children | (props: { value: number; label: string; isHovered: boolean; data: { label: string; value: number; maxValue: number; color?: string }; }) => ReactNode | Custom render function for complete control over center content |
className | string | Additional class name for the container |
valueClassName | string | Class name for the value text. Scales with center size via container queries. |
labelClassName | string | Class name for the label text. Scales with center size via container queries. |
prefix | string | Prefix to show before the number (e.g., "$") |
suffix | string | Suffix to show after the number (e.g., "%") |