FairGarden Design

Progress

A Base UI Progress for task completion: uploads, step position in a form, reading or quiz progress. It is drawn in ink only, and the value is always shown in text.

import { Progress } from '@fairgarden-private/design/components/Progress'

The label is a caps label, the value a data figure ("62%", "2 of 4"). Every part is the scope's step-12 ink on the ground, with no tinted track, so it reads on any ground. For a bounded measurement such as storage used, use a Meter instead.

Kinds and states

Kinds and states

bar is the default: an outlined 8 px track with a solid indicator. steps draws one outlined 12 px cell per step and reads "2 of 4". ring is a 2 px arc beside its value, and rail is the 2 px carousel position rail. At the maximum, the bar shows "Done" with the success glyph. value={null} is indeterminate: a dashed track with "Loading…", whose dashes move only when motion is allowed; show it only after one second. A failure or a limit is a status glyph plus words (status, statusText), never an indicator color swap; put a Retry button beside it.

Uploading trail map
x
Membership form
x
Quizx
Loading sightings
x
Photo upload
Upload failed. Retryx
Gallery position
x
ProgressKinds.tsx
'use client'

import * as React from 'react'
import { Button } from '@fairgarden-private/design/components/Button'
import { Progress } from '@fairgarden-private/design/components/Progress'
import styles from './kinds.module.css'

/** Bar, steps, ring and rail; indeterminate, complete and failed. */
export function ProgressKinds() {
  const [upload, setUpload] = React.useState(62)

  return (
    <div className={styles.stack}>
      <Progress label="Uploading trail map" value={upload} />
      <div className={styles.row}>
        <Button size="sm" onClick={() => setUpload((value) => Math.min(100, value + 19))}>
          Upload More
        </Button>
        <Button size="sm" variant="text" onClick={() => setUpload(0)}>
          Reset
        </Button>
      </div>
      <Progress kind="steps" label="Membership form" value={2} max={4} />
      <Progress kind="ring" label="Quiz" value={3} max={5} formatValue={(_formatted, value) => `${value} of 5`} />
      <Progress label="Loading sightings" value={null} />
      <Progress
        label="Photo upload"
        value={40}
        status="danger"
        statusText="Upload failed. Retry"
      />
      <Progress kind="rail" label="Gallery position" value={3} max={8} formatValue={(_formatted, value) => `${value} of 8`} />
    </div>
  )
}

Layout

The label and value sit above a full-width track. Inside a MeterPanel (from the Meter component), label, track and value share one row once the panel is at least 768 px wide. Without a panel, the row starts at the medium breakpoint.

In print

Progress is hidden in print, except steps, which prints its "2 of 4" text.

API Reference

A Base UI Progress drawn in ink only: every part is --primary12 on the ground, and the value is always shown in text, because the fill vanishes in print. value={null} is indeterminate: a dashed track with “Loading…"; show it only after 1 s. Progress is hidden in print, except steps, which prints its text.

PropTypeDescription
label
React.ReactNode | undefined

The label, in caps (type-label); author it in sentence case.

completeLabel
React.ReactNode | undefined

The word shown at completion. Default “Done”.

formatValue
| ((
    formattedValue: string,
    value: number,
  ) => React.ReactNode)
| undefined

Formats the visible value (type-data): “62%", “3 of 5″, “4.2 of 10 GB”. Default: the Base UI formatted value (a percentage), or “n of N” for steps.

indeterminateLabel
React.ReactNode | undefined

The words beside an indeterminate track (value={null}). Default “Loading…".

kind
'bar' | 'steps' | 'ring' | 'rail' | null | undefined

bar (default): an outlined 8 px track with a solid indicator; label left, value right. steps: one outlined 12 px cell per step (maxmin cells), “2 of 4″. ring: a 2 px arc beside its value. rail: the 2 px carousel position rail.

primary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined

Primary Radix scale: track, indicator, label and value. Never defaulted; omitted, it inherits the scope [D133].

secondary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

Secondary Radix scale: unused; status glyphs take their own scale. Never defaulted.

status
ProgressStatus | undefined

The status part beside the value. Complete progress shows success with completeLabel unless you pass another status. A threshold is always a glyph plus a word, never an indicator color swap.

statusText
React.ReactNode | undefined

The status word: “Upload failed. Retry”, “Near limit”. A Retry button goes beside it.

progress
ProgressProps

Props for Progress: Base UI Progress Root props plus the kind and color axes, the label and the status.

type ProgressProps = {
  /**
   * `bar` (default): an outlined 8 px track with a solid indicator; label
   * left, value right. `steps`: one outlined 12 px cell per step
   * (`max` − `min` cells), "2 of 4". `ring`: a 2 px arc beside its value.
   * `rail`: the 2 px carousel position rail.
   */
  kind?: 'bar' | 'steps' | 'ring' | 'rail' | null;
  /**
   * Primary Radix scale: track, indicator, label and value. Never defaulted;
   * omitted, it inherits the scope [D133].
   */
  primary?:
    | 'ruby'
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | null;
  /** Secondary Radix scale: unused; status glyphs take their own scale. Never defaulted. */
  secondary?:
    | 'ruby'
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | 'amber'
    | 'blue'
    | 'cyan'
    | 'grass'
    | 'green'
    | 'jade'
    | 'lime'
    | 'mint'
    | 'orange'
    | 'sky'
    | 'teal'
    | 'yellow'
    | null;
  /** The label, in caps (`type-label`); author it in sentence case. */
  label?: React.ReactNode;
  /**
   * Formats the visible value (`type-data`): "62%", "3 of 5", "4.2 of 10
   * GB". Default: the Base UI formatted value (a percentage), or "n of N"
   * for `steps`.
   */
  formatValue?: (formattedValue: string, value: number) => React.ReactNode;
  /**
   * The status part beside the value. Complete progress shows `success`
   * with `completeLabel` unless you pass another status. A threshold is
   * always a glyph plus a word, never an indicator color swap.
   */
  status?: ProgressStatus;
  /** The status word: "Upload failed. Retry", "Near limit". A Retry button goes beside it. */
  statusText?: React.ReactNode;
  /** The word shown at completion. Default "Done". */
  completeLabel?: React.ReactNode;
  /** The words beside an indeterminate track (`value={null}`). Default "Loading…". */
  indeterminateLabel?: React.ReactNode;
}
progressStatus

The status part: a §1.5.4 glyph plus a word, in the status scale [D129].

type progressStatus = progressStatus
ProgressStatus

A status beside the value: complete (success), near a limit (warning), failed or over (danger).

type ProgressStatus = 'success' | 'warning' | 'danger'

Specification: DESIGN-SYSTEM.md §10.18 (progress and meter) and §8.9 (meters, scales and data progress).