# ProgressIndicator

**Maturity:** experimental

A read-only status indicator that conveys either ongoing progress of unknown duration (indeterminate, circular spinner) or a known fraction of completion (determinate, linear bar).

## Import

```tsx
import { ProgressIndicator } from "@liquidify/react/progress-indicator"
```

## Props

- `children`: `ReactNode`. Optional visible label rendered as a sibling `<span>` to the spinner or bar, styled with the secondary-label token. The SwiftUI `ProgressView("Loading")` / `{ Text("…") }` analogue — the indicator's text content.
- `className`: `string | undefined`. Optional additional CSS class merged into the glass surface class. The supported per-instance customisation seam: override any `--lq-*` custom property from this class's CSS — the token cascade the engine reads (ADR-0004). No raw inline style is involved, so this stays ADR-0003 clean.
- `labelProps`: `LabelProps | undefined`. Per-instance typography override for the visible label, forwarded to the internal {@link Text} (ADR-0028). Token-backed axes only; the HIG default is `variant="body"` with `color="secondary"` (circular) / `"primary"` (linear). Applies only when {@link ProgressIndicatorProps.children} is a string/number.
- `ref`: `Ref<HTMLDivElement> | undefined`. Forwarded ref to the rendered element — the component's native root (or the merged child element on a component that opts into {@link SlottableProps}). Generic over `E` (charter `docs/api-conventions.md` §4 rule 1, default `HTMLElement`) so a component with a polymorphic root (Link's navigation / action mode union, Text's `as`) can instantiate the concrete element per variant instead of leaving every consumer with a widened `Ref<HTMLElement>`. A component that does not narrow simply inherits the `HTMLElement` default — source-compatible with every pre-existing non-generic `extends ControlProps`.
- `size`: `ProgressIndicatorSize | undefined`; default ``"regular"``. Size of the circular spinner, drawn from the subset `"small" | "regular" | "large"`. The linear bar has a fixed geometry (Apple ships a single bar height) — the `size` prop applies **only to the circular variant**.
- `tint`: `LiquidTintValue | undefined`; default ``"blue"``. System-accent of the determinate fill and the spinner arc — drawn from the restricted {@link LIQUID_COLORS } palette and applied via the `data-tint` attribute selector in CSS (ADR-0024). Per Apple HIG the linear bar fill is system blue by default.
- `value`: `number | undefined`. Fractional progress value in `[0, 1]`. Applies **only when the resolved `variant` is `"linear"`** — on `variant="circular"` the value is ignored for ARIA/state purposes and the spinner stays indeterminate. When omitted (`undefined`) the linear indicator is **indeterminate** — the bar shimmers with no fill fraction. When set on a linear indicator, it is **determinate** — the fill encodes the fraction. Out-of-range values are clamped silently. This is a **one-way display prop**: `ProgressIndicator` reads it but never calls back with a new value.
- `variant`: `"circular" | "linear" | undefined`. Display variant — `"circular"` renders the spoked spinner; `"linear"` renders the horizontal pill track + fill. When omitted, the variant resolves by `value` presence: `undefined` → `"circular"`, number → `"linear"` (the SwiftUI `.progressViewStyle(.automatic)` analogue). An explicit `variant` overrides which shape renders, but **does not** grant a circular indicator determinacy: `variant="circular"` is always indeterminate, regardless of `value` (audit §7 P2 — a determinate ARIA state over a perpetually-spinning visual disagreed with the eyes). Determinacy is scoped to `variant="linear"` only.

## SwiftUI mapping

- ``ProgressView()` (no value)` → ``value` omitted / `undefined`` (direct): Indeterminate — mirrors the no-arg overload; renders the circular spinner by default.
- ``ProgressView(value: Double)`` → ``value?: number` in `[0, 1]`` (direct): Determinate fractional; `undefined` ⇒ indeterminate. SwiftUI default `total = 1.0`; React fixes range to `0…1`.
- ``ProgressView(value:total:)`` → ``value` only — `total`/`max` not exposed` (direct): SwiftUI allows a custom `total`; React fixes to `0…1` fractional. Deferred.
- ``.progressViewStyle(.circular)`` → ``variant="circular"`` (direct): The spoked spinner.
- ``.progressViewStyle(.linear)`` → ``variant="linear"`` (direct): The horizontal bar.
- ``.progressViewStyle(.automatic)`` → `default `variant` resolution` (direct): SwiftUI picks by context; React picks by `value` presence — circular for undefined, linear for a number.
- ``ProgressView("Loading")` / `{ Text("…") }`` → ``children?: ReactNode`` (renamed): Title or current-value text slot; circular inline + linear trailing collapse to one `children` slot. A string/number renders through the `Text` primitive (`variant="body"`, `color="secondary"` circular / `"primary"` linear); a label *element* passes through unwrapped (ADR-0028).
- ``.font(_:)` / `.foregroundStyle(_:)` on the label` → ``labelProps` (`Pick<TextProps, …>`)` (direct): Per-instance typography override forwarded to the internal `Text` (token-backed axes only; ADR-0028). Spread last, so an explicit value wins over the computed default. Applies only to string/number `children`.
- ``.controlSize(.small/.regular/.large)`` → ``size?: Extract<Size, "small" | "regular" | "large">`` (direct): Applies to the circular spinner only; demo uses only these three sizes (ADR-0023, ADR-0018).
- ``.tint(_:)`` → ``tint?: LiquidColor`` (direct): 13 system colours (ADR-0024); colours the determinate fill and the spinner arc.
- `(no `.disabled`)` → `—` (direct): Not demonstrated; progress is a status display, not an input.
