# Focus timer
> Focus timer drawn as a ring of 60 pixels; one breaks loose each interval and drops into a sand pile, and reset flies them back.
- Element: `<px-focus>`
- React: `import { PxFocus } from "@/components/lumesec/px-focus"`
- Collection: Pixel Lab (https://elements.lumesec.ai/components/pixel-lab)
- Registry item: https://elements.lumesec.ai/r/px-focus.json
- Page: https://elements.lumesec.ai/components/pixel-lab/focus



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

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

<px-focus duration="60"></px-focus>
```

## 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-focus
```

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

## Usage

React:

```tsx
import { PxFocus } from "@/components/lumesec/px-focus";

export function Example() {
  return (
    <PxFocus duration={60} />
  );
}
```

HTML:

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

<px-focus duration="60"></px-focus>
```

## Behaviour

Sixty pixels make the ring. Each second the next one trembles, breaks loose and drops into a sand pile below. Reset and the whole pile flies back up into place.

## API reference

### Attributes

| Attribute  | React prop | Type     | Default | Description                                                                                                           |
| ---------- | ---------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------- |
| `duration` | `duration` | `number` | `60`    | Session length in seconds, split evenly across the 60 ring pixels. Read every frame, so a change applies immediately. |

### Events

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

| Event   | React prop | Detail | Description                                                  |
| ------- | ---------- | ------ | ------------------------------------------------------------ |
| `start` | `onStart`  | `{}`   | Fires when the timer starts or resumes.                      |
| `pause` | `onPause`  | `{}`   | Fires when the timer is paused.                              |
| `reset` | `onReset`  | `{}`   | Fires when the timer is reset.                               |
| `done`  | `onDone`   | `{}`   | Fires when the last pixel falls and the session is complete. |

### Methods

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

| Method     | Description                                                              |
| ---------- | ------------------------------------------------------------------------ |
| `toggle()` | Starts, pauses or resumes the timer; after completion it resets instead. |
| `reset()`  | Stops the timer and flies the fallen pixels back into the ring.          |

### Properties

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

## Accessibility

* The remaining time and the status (Ready, Focus, Paused, Session complete) are text in a `role="timer"` element with `aria-live="off"`, so the countdown is not announced on every tick.
* The ring canvas is decorative and hidden from assistive technology.
* Start/Pause/Resume and Reset are native buttons.
* Reduced motion: the next pixel no longer trembles before it drops and the completion sparks are skipped; falling and returning pixels still animate.

## 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-font`, `--ui-mono`, `--ui-muted` and `--ui-ok`.

## Notes

* The canvas is a fixed 224 by 232px; the time sits in the centre of the ring in the monospace font.


