Skip to content

Progress bar

In review
Live · web

Determinate: drag the slider; mark it complete or failed

Exporting session…64%
Progress64%

Indeterminate: work of unknown length (streaming). Reduced-motion freezes it

Connecting to gateway…

Buffer: a lighter secondary track (e.g. received) ahead of the primary (played)

Streaming response64%

Segmented: discrete steps (step 3 of 5)

Deploy pipeline3 / 5
Step

Sizes: thin (the app's 3 to 4dp) and thick

Thin72%
Thick72%

Status: complete and failed (icon + text, never color alone)

Snapshot uploaded100%
Done
Build artifacts80%
Failed at 80%: exited 1

Circular: determinate ring with a center % (indeterminate circular is the spinner)

25%Indexing
64%Export
100%Done

Track + fill are all var(--token). The track is a recessed well at radius-track (the app's 2dp squared shape), and the fill is the plain cyan accent with no threshold banding (that is the context gauge). The width grows; the fill never tweens. The island is live.

// Web: React, consuming @dizyx/nockerl-tokens. Progress ships as THREE primitives
// from @dizyx/nockerl-react, and the live demo above runs on all three. There is no
// single `ProgressBar` wrapper: the label and the % readout are the caller's row, so
// the accessible name rides in on `aria-label`.
import {
NockerlCircularProgress,
NockerlProgressSegments,
NockerlProgressTrack,
} from '@dizyx/nockerl-react';
// Determinate: a value 0 to 100 drives the cyan fill width. The track is the GENERAL
// progress primitive: plain cyan, NO threshold banding (that is the ContextGauge).
<NockerlProgressTrack
value={64}
size="thick"
role="progressbar"
aria-label="Exporting session"
aria-valuenow={64}
aria-valuemin={0}
aria-valuemax={100}
/>
// Indeterminate: omit `value` for work of unknown length (a sliding segment).
<NockerlProgressTrack indeterminate role="progressbar" aria-label="Connecting to gateway" aria-busy="true" />
// Buffer: a lighter secondary track behind the primary fill.
<NockerlProgressTrack value={48} buffer={70} role="progressbar" aria-label="Streaming response" />
// Segmented / stepped: discrete cells, `filled` of `total`.
<NockerlProgressSegments total={5} filled={3} role="progressbar" aria-label="Deploy pipeline" />
// Completion + failure. Color is never the only signal (icon + text follow).
<NockerlProgressTrack value={100} tone="success" role="progressbar" aria-label="Snapshot uploaded" />
<NockerlProgressTrack value={80} tone="error" role="progressbar" aria-label="Build artifacts" />
// Determinate ring with a center % (indeterminate circular = the NockerlSpinner).
// The ring owns its own accessible name, so `label` is required here.
<NockerlCircularProgress value={64} label="Export" showValue />

Three primitives cover the forms: NockerlProgressTrack is the linear bar (determinate, indeterminate, and buffer), NockerlProgressSegments is the stepped form, and NockerlCircularProgress is the determinate ring. The two linear primitives render no text of their own, so the label and the % readout live in the row you wrap them in and you pass role="progressbar" plus the aria-* values straight through. Both also forward any other div attribute. The ring is the exception: it draws its own centered figure and takes a required label.

NockerlProgressTrack, the linear bar:

PropTypeDefaultDescription
valuenumber0Determinate fill, 0-100 (clamped + rounded). Ignored when indeterminate.
sizeProgressSize'thin'Track height ramp: thin (the app's 3-4dp) or thick.
toneProgressTone'accent'The fill tone, either the plain cyan accent (default) or a warm status hue.
buffernumberA lighter secondary buffer (0-100) rendered BEHIND the primary fill.
indeterminatebooleanfalseWork of unknown length. A sliding segment travels the track (no value).
childrenReactNodeOverride the fill node's children (the demo never does; kept for parity).

Also accepts the native <div> attributes (e.g. onClick, disabled, id, aria-*, data-*), forwarded straight through, plus a forwarded ref.

NockerlProgressSegments, the stepped cells:

PropTypeDefaultDescription
total *numberTotal cells.
filled *numberHow many leading cells are filled (cyan).

NockerlCircularProgress, the determinate ring:

PropTypeDefaultDescription
value *numberDeterminate value, 0-100 (clamped + rounded).
sizenumber64Outer diameter in px. Default 64.
thicknessnumberRing stroke width in px. Defaults to ~1/8 of the size.
toneProgressTone'accent'Arc tone: the plain cyan accent (default) or a warm status hue.
label *stringAccessible name (role="progressbar"). REQUIRED.
showValuebooleanfalseRender the center % figure. Ignored when centerContent is supplied.
centerContentReactNodeOverride the center figure with custom content (e.g. a check glyph at 100%).
classNamestringExtra class names appended to the wrapper.

Also accepts the native <span> attributes (e.g. onClick, disabled, id, aria-*, data-*), forwarded straight through, plus a forwarded ref.

Cross-platform drift (tracked for reconciliation)

Use the progress bar for general determinate/indeterminate progress with the plain cyan fill. It is not the context gauge (the threshold-banded cyan → amber → red token-budget meter) and not the spinner (the indeterminate circular loader), so the ring here is determinate only.

  • Android shipped forms: only the determinate linear bar is real (LinearProgressIndicator in TodoWidget / AgentWidget / ClusterSheet). The indeterminate linear, buffer, segmented, and determinate ring forms are not shipped on Android; its CircularProgressIndicator is indeterminate-only (the spinner). (The determinate ring + indeterminate linear now ship on Swift via NockerlDesign; buffer + segmented remain design-only.)
  • Swift: NockerlDesign now publishes the determinate + indeterminate linear bar (NockerlProgressBar) and the determinate ring (NockerlProgressRing). The buffer and segmented forms are still Swift-unshipped (web/Android-first).
  • Track radius: both the real Android track and Swift’s NockerlProgressBar are squared (radius-track = 2dp/2pt), not a pill. The web demo matches this.
  • Height: Android uses 3 to 4dp; web exposes thin / thick as a convenience.