# Progress ring
> Circular pixel progress ring with the percentage counting up in its centre; it turns green and throws a few sparks when it reaches 100%.
- React: `import { PxRing } from "@/components/lumesec/px-ring"`
- Collection: Pixel UI (https://elements.lumesec.ai/components/pixel-ui)
- Registry item: https://elements.lumesec.ai/r/px-ring.json
- Page: https://elements.lumesec.ai/components/pixel-ui/ring



Live preview: https://elements.lumesec.ai/view/px-ring

Demo source:

```tsx
"use client";

import * as React from "react";

import { PxRing } from "@/components/lumesec/px-ring";

const controlButton =
  "h-[30px] rounded-lg border border-border bg-card px-[11px] font-[inherit] text-[12.5px] font-medium text-foreground hover:border-[color-mix(in_srgb,var(--lumesec)_50%,var(--border))]";

export default function PxRingDemo() {
  const [usage, setUsage] = React.useState(72);

  return (
    <div className="grid w-full max-w-[380px] gap-[14px]">
      <div className="flex flex-wrap items-center justify-center gap-2.5">
        <PxRing value={usage}>Weekly usage</PxRing>
        <PxRing size={84}>Syncing</PxRing>
      </div>
      <div className="flex flex-wrap justify-center gap-1.5">
        <button type="button" className={controlButton} onClick={() => setUsage((value) => Math.min(100, value + 10))}>
          +10%
        </button>
        <button type="button" className={controlButton} onClick={() => setUsage(0)}>
          Reset
        </button>
      </div>
    </div>
  );
}
```

## Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

## Installation

```bash
npx shadcn@latest add @lumesec/px-ring
```

First time with the @lumesec registry? Register it once, or install by URL:

```bash
npx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json
npx shadcn@latest add https://elements.lumesec.ai/r/px-ring.json
```

## Usage

React:

```tsx
import { PxRing } from "@/components/lumesec/px-ring";

export function Example() {
  return <PxRing value={72}>Weekly usage</PxRing>;
}
```

## Behaviour

A circular version with the percentage counting up in its centre. It turns green and throws a few sparks at 100%.

## API reference

### Props

Also accepts every prop of `<div>` (`React.ComponentProps<"div">`), spread onto the root element.

| Prop         | Type              | Default | Description                                                                                                                           |
| ------------ | ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `value`      | `number \| null`  | —       | Progress from 0 to 100, clamped; the ring animates to it. `null` or leaving it out shows a spinning arc.                              |
| `size`       | `number`          | `120`   | Diameter in pixels.                                                                                                                   |
| `children`   | `React.ReactNode` | —       | Caption under the ring; it also labels the progress bar.                                                                              |
| `onComplete` | `() => void`      | —       | Called when the fill reaches 100%, and again each time the value drops below 100 and returns. Not called when the ring starts at 100. |

### Ref

`ref` points at the root `HTMLDivElement`.

## Accessibility

* The ring is a `div` with `role="progressbar"`, `aria-valuemin="0"`, `aria-valuemax="100"` and `aria-valuenow`, which is left out when indeterminate.
* The canvas and the percentage inside it are `aria-hidden`; the number is the visible form of `aria-valuenow`.
* With a caption the ring is labelled by it through `aria-labelledby`; without one its `aria-label` is "Progress".
* There is no live region.
* Reduced motion: the ring and the number jump to the value, no sparks fire at 100%, and the indeterminate arc holds still.

## Theming

Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from `--lumesec`. See [Theming](/docs/theming).

This component reads `--lumesec`, `--lumesec-shine`, `--lumesec-success`, `--foreground` and `--muted-foreground`.

## Notes

* The ring starts at the first `value` instead of filling up from 0.
* The percentage is text in the mono font with tabular figures, 27% of `size` high, and counts along with the fill. It turns green at 100%; the indeterminate ring shows no number.


