# Rate-limit hourglass
> Falling-sand hourglass that counts down to a usage reset; the remaining time is derived from the sand left in the top chamber.
- Element: `<px-hourglass>`
- React: `import { PxHourglass } from "@/components/lumesec/px-hourglass"`
- Collection: Pixel Lab (https://elements.lumesec.ai/components/pixel-lab)
- Registry item: https://elements.lumesec.ai/r/px-hourglass.json
- Page: https://elements.lumesec.ai/components/pixel-lab/hourglass



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

```html
<script type="module" src="https://elements.lumesec.ai/cdn/px-hourglass.js"></script>

<px-hourglass duration="30"></px-hourglass>
```

## Playground

Change a prop and the component updates. Props marked live animate to the new value; the others rebuild the element.

## Installation

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

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

## Usage

React:

```tsx
import { PxHourglass } from "@/components/lumesec/px-hourglass";

export function Example() {
  return (
    <PxHourglass duration={30} />
  );
}
```

HTML:

```html
<script type="module" src="https://elements.lumesec.ai/cdn/px-hourglass.js"></script>

<px-hourglass duration="30"></px-hourglass>
```

## Behaviour

A real falling-sand simulation counts down to the next usage reset. Grains pile up, slide off each other and squeeze through the neck one at a time; the timer is how much sand is left.

## API reference

### Attributes

| Attribute  | React prop | Type     | Default | Description                                                                                                                  |
| ---------- | ---------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `duration` | `duration` | `number` | `30`    | Countdown length in seconds for a full top chamber. Read once when the element first connects; later changes have no effect. |

### Events

Events bubble and cross the shadow boundary unless the description says otherwise.

| Event   | React prop | Detail | Description                                                              |
| ------- | ---------- | ------ | ------------------------------------------------------------------------ |
| `flip`  | `onFlip`   | `{}`   | Fires after the hourglass has turned over.                               |
| `reset` | `onReset`  | `{}`   | Fires once when the top chamber runs empty, meaning the limit has reset. |

### Methods

Call them on the element, for example through a React ref.

| Method   | Description                                                                        |
| -------- | ---------------------------------------------------------------------------------- |
| `flip()` | Turns the hourglass over so the sand runs back. Ignored while a flip is animating. |

### Properties

| Property               | Type     | Description                                                |
| ---------------------- | -------- | ---------------------------------------------------------- |
| `duration` (read-only) | `number` | Parsed `duration` attribute, `30` when missing or invalid. |

## Keyboard

| Keys                         | Action              |
| ---------------------------- | ------------------- |
| Enter / Space (on the glass) | Flip the hourglass. |

## Accessibility

* The glass is focusable (`tabindex="0"`) with `role="button"` and `aria-label="Flip the hourglass"`.
* A native "Flip" button performs the same action.
* The countdown and status text are plain text without a live region; the canvas itself has no role.
* Reduced motion: the flip happens instantly without the rotation, text swaps without the roll and the completion sparks are skipped; the sand simulation still runs.

## Theming

The element reads your shadcn theme tokens through its shadow root, so light and dark follow your theme. The accent comes from `--lumesec`. See [Theming](/docs/theming).

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

To restyle only LumeSec elements, set the matching `--ui-*` overrides: `--ui-accent`, `--ui-fg`, `--ui-muted` and `--ui-ok`.

## Notes

* The host gets a `data-done` attribute while the top chamber is empty, which turns the time green; it is cleared on the next flip.
* Flipping part-way through inverts the current sand, so the remaining time becomes the elapsed time.
* The glass canvas is a fixed 120 by 168px.


