# Progress bar
> Pixel progress bar: determinate with a hot leading edge and a green sweep at 100%, or an indeterminate comet when there is no value.
- React: `import { PxProgress } from "@/components/lumesec/px-progress"`
- Collection: Pixel UI (https://elements.lumesec.ai/components/pixel-ui)
- Registry item: https://elements.lumesec.ai/r/px-progress.json
- Page: https://elements.lumesec.ai/components/pixel-ui/progress



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

Demo source:

```tsx
"use client";

import * as React from "react";

import { PxProgress } from "@/components/lumesec/px-progress";

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 PxProgressDemo() {
  const [upload, setUpload] = React.useState(0);
  const ticker = React.useRef(0);

  // start part-way, so the bar glides on first view
  React.useEffect(() => {
    const frame = requestAnimationFrame(() => setUpload(38));
    return () => {
      cancelAnimationFrame(frame);
      window.clearInterval(ticker.current);
    };
  }, []);

  const startUpload = () => {
    let progress = 0;
    window.clearInterval(ticker.current);
    setUpload(0);
    ticker.current = window.setInterval(() => {
      progress = Math.min(100, progress + 2 + Math.random() * 5);
      setUpload(Math.round(progress));
      if (progress >= 100) window.clearInterval(ticker.current);
    }, 120);
  };

  return (
    <div className="grid w-full max-w-[380px] gap-[14px]">
      <PxProgress value={upload} label="Uploading dataset" doneLabel="Upload complete" />
      <PxProgress label="Indexing" />
      <div className="flex flex-wrap justify-center gap-1.5">
        <button type="button" className={controlButton} onClick={startUpload}>
          Start upload
        </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-progress
```

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-progress.json
```

## Usage

React:

```tsx
import { PxProgress } from "@/components/lumesec/px-progress";

export function Example() {
  return <PxProgress value={38} label="Uploading dataset" doneLabel="Upload complete" />;
}
```

## Behaviour

Determinate with a hot leading edge and a green sweep when it finishes, or indeterminate (no value) as a moving comet.

## 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 bar glides to it. `null` or leaving it out shows the indeterminate comet.   |
| `label`      | `string`         | `"Uploading"` | Text above the bar, and the bar's accessible name.                                                               |
| `doneLabel`  | `string`         | `"Complete"`  | Text shown once `value` reaches 100.                                                                             |
| `onComplete` | `() => void`     | —             | Called when `value` reaches 100, including on mount when it starts there, and again each time it returns to 100. |

### Ref

`ref` points at the root `HTMLDivElement`.

## Accessibility

* The bar is a canvas with `role="progressbar"`, `aria-valuemin="0"`, `aria-valuemax="100"`, `aria-valuenow` (left out when indeterminate) and `aria-label` from `label`.
* There is no live region; the completion text is not announced.
* Reduced motion: the bar jumps to each value, the finish sweep is skipped and the indeterminate comet holds still mid-track.

## 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 bar starts at the first `value` instead of gliding up from 0.
* The root carries `data-done` once the value reaches 100.


