Charts
ProgressRing
A circular determinate progress indicator: one SVG circle with a dash offset, which is why it needs no path maths, no measurement and no client JS.
Import
Charts live at their own subpath with their own stylesheet, so an app that never draws one pays nothing for them.
import { ProgressRing } from '@the_viveksingh/vivek-ui/charts'
import '@the_viveksingh/vivek-ui/charts.css'A single metric
Real `role="progressbar"` semantics, so it is announced as progress, not an image.
<ProgressRing value={68} label="Storage used" showValue />Custom content and scale
<ProgressRing value={7} max={10} size={140} thickness={12} label="Onboarding">
<strong>7/10</strong>
<span>steps done</span>
</ProgressRing>Accessibility
Every chart carries role="img" with a generated accessible name, and renders a real <table> alongside it — visually hidden, never display: none — so a screen-reader user gets the actual numbers instead of "chart". Set accessibleTable={false} only if you have provided the data in a table elsewhere on the page.
No series is ever encoded by colour alone: each carries a distinct dash pattern and marker shape on top of its colour, so the chart survives greyscale printing and every common form of colour blindness.
The palette is verified rather than asserted. Each theme has its own six colours, because one set cannot clear the 3:1 non-text contrast threshold against both a white and a near-black plot surface. Separation is measured under simulated protanopia, deuteranopia and tritanopia: for one to four series — which is nearly every chart — the closest pair sits at ΔE 17, slightly better than the unmodified Okabe-Ito palette it is derived from. Past four series the dash patterns and marker shapes carry the distinction.
Hostile data
Real series contain gaps and rubbish. Every chart is tested against empty arrays, a single point, all-equal values, negatives crossing zero, NaN, ±Infinity, 1e308 and 1e-320, with an assertion that no rendered SVG attribute ever contains NaN. A bad row degrades the chart; it does not blank your page.
Props
Generated from the package's own type declarations, so this table cannot drift from the code.
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | — | Current value. Out-of-range and non-finite input is clamped, never NaN. |
max | number | — | — |
size | number | — | Diameter in px. The ring scales down responsively but never past this. |
thickness | number | — | Stroke width in px. Clamped so it can never exceed the radius. |
label | string | — | Accessible name, e.g. "Storage used". |
showValue | boolean | — | Percentage in the middle of the ring. |
children | ReactNode | — | Arbitrary centre content, e.g. a big number over a caption. Wins over `showValue`. |
color | string | — | Any CSS colour for the filled arc. Defaults to the `--vk-chart-1` token. |
trackColor | string | — | Any CSS colour for the unfilled track. |
formatValue | (percent: number) => string | — | Formats the centre percentage. Must be locale-stable to stay hydration-safe. |
startAngle | number | — | Degrees clockwise from 12 o clock where the arc begins. |
square | boolean | — | Square off the arc ends. |
title | string | — | SVG `<title>` for the graphic. |
description | string | — | — |
Every remaining prop is spread onto the root element, so all standard HTML and ARIA attributes work. className and style are merged with the library's own, never replaced, and the ref forwards to the root DOM node.