# Spinner

An animated icon that shows an operation is in progress when its duration is unknown.

```tsx
import { Spinner } from "@zenoxai/ui/components/spinner"

function SpinnerDemo() {
  return <Spinner />
}
```

## Usage

```tsx
import { Spinner } from "@zenoxai/ui/components/spinner"

export function TimelineLoading() {
  return (
    <div className="flex h-40 items-center justify-center">
      <Spinner aria-label="Loading threat timeline" />
    </div>
  )
}
```

Use a spinner for a short wait with no measurable progress: a button action, a panel that is
fetching. Pick the alternative that tells the user more when there is one:

- The layout of the content is known in advance (a table, a card): use a
  [Skeleton](/components/skeleton), which avoids a jump when the content arrives.
- The operation reports how far along it is (an export, an upload): use a
  [Progress](/components/progress) bar.

`Spinner` is a `Loader2` icon from `lucide-react` that spins. It takes every SVG prop. It
inherits the text color and is 16px by default; change both with classes.

## Examples

### Sizes

Size the spinner to the text or the space around it: `size-3` next to small text, the default
16px in buttons and inputs, `size-6` or `size-8` for a panel or a page.

```tsx
import { Spinner } from "@zenoxai/ui/components/spinner"

function SpinnerSizes() {
  return (
    <>
      <Spinner className="size-3" />
      <Spinner />
      <Spinner className="size-6" />
      <Spinner className="size-8 text-primary" />
    </>
  )
}
```

### In a button

Put the spinner in place of the button's icon with `data-icon="inline-start"`, keep the label,
and disable the button until the action ends. Hide the spinner from screen readers with
`aria-hidden`: the label already says what is happening.

```tsx
import { Button } from "@zenoxai/ui/components/button"
import { Spinner } from "@zenoxai/ui/components/spinner"

function SpinnerInButton() {
  return (
    <>
      <Button disabled>
        <Spinner data-icon="inline-start" aria-hidden />
        Scanning
      </Button>
      <Button variant="outline" size="sm" disabled>
        <Spinner data-icon="inline-start" aria-hidden />
        Exporting report
      </Button>
    </>
  )
}
```

### With a label

A spinner next to text that names what is loading. The text carries the meaning, so the
spinner is hidden from screen readers. Keep the `role="status"` element mounted and change its
text when the wait ends ("Loaded 128 assets"), so screen readers announce the result.

```tsx
import { Spinner } from "@zenoxai/ui/components/spinner"

function SpinnerWithLabel() {
  return (
    <div className="flex items-center gap-2 text-sm text-text-secondary">
      <Spinner aria-hidden />
      <span role="status">Loading assets for Acme Corp</span>
    </div>
  )
}
```

### In a panel

Center a larger spinner in the space the content will take, with a fixed height so the layout
does not jump. Without visible text, set an `aria-label` that names what is loading.

```tsx
import { Spinner } from "@zenoxai/ui/components/spinner"

function SpinnerInPanel() {
  return (
    <div className="flex h-40 w-full max-w-sm items-center justify-center rounded-lg border border-border-subtle bg-surface-muted">
      <Spinner
        className="size-6 text-text-muted"
        aria-label="Loading threat timeline"
      />
    </div>
  )
}
```

## Accessibility

- The spinner renders `role="status"` with `aria-label="Loading"`. Replace the label with what
  is loading ("Loading threat timeline") when the spinner stands alone.
- Inside a button, set `aria-hidden`. Otherwise "Loading" becomes part of the button's
  accessible name ("Loading Scanning").
- A status region added to the page together with its content is often not announced. When
  the end of the wait matters, announce it from a `role="status"` element that is already on
  the page, or with a toast.
- The animation stops for users who ask for reduced motion; the icon stays visible.

## API reference

`import { ... } from "@zenoxai/ui/components/spinner"`

### Spinner

No props of its own.

