# `progressBar` — behaviour

*Open when placing a progress or percentage indicator, or when one renders at 0% on a form that saves clean.*

A display element: it computes `(current − start) / (end − start) × 100`, rounds to a whole number, and draws it. It collects no value and offers no control. Nothing renders decimals or a "3 of 10" caption — `numeric` is `NN%` alone; its only text is the base label/description/hint.

**Every bad input renders as a plausible bar, never as an error.** A point that resolves to anything non-finite falls back per point: `start` 0, `current` 0, `end` 100. `end === start` returns 0%, so an `end` bound to a field that genuinely holds 0 shows an empty bar, not a full one.

`{ source: 'field' }` is NOT a binding. A record's load query carries `id`, `updatedAt`, fields bound through `dataSource.fieldPath`, `{{…}}` template paths, condition paths and the variable's `select` — progressBar contributes to none of them. A record field named only here is never fetched, and the bar sits at 0% forever.

```jsonc
"current": { "source": "field", "field": "task.doneCount" }
// only works if another element binds task.doneCount, or the variable's `select` lists it
```

Reading a sibling element needs the accessor: `$components.<name>.value`. A bare `$components.<name>` resolves to the wrapper object `{ value: … }`, so `Number(…)` is NaN and the bar reads 0%.

`allowOver100` / `allowNegative` change only the NUMBER. The bar fill is clamped 0–100 either way, and the circle arc draws `|percent|` capped at 100 — 150% is a full ring labelled "150%".

`colorStops` is scanned in array order; the first range containing the percent wins (`from`/`to` inclusive, `null` = open end). A stop with no `color`, or no match, leaves the default `#2563eb`.

`circleSize` is read only by `variant: 'circle'`, and its CSS length is parsed to a px number — `'50%'` becomes 50px.
