Files
suaveplan/packages/ui/react/react-ui/src/components/progress/progress.md
2026-05-27 23:47:53 +01:00

5.4 KiB

Progress

(no submodules)

- progress.variants — `progressVariants` is the cva c…
  • progress.variants — progressVariants is the cva config that powers the <Progress> chassis component (paired with `progressIndicatorVari…

  • progress.variants — progressVariants is the cva c…

  • progress.variants — progressVariants is the cva config that powers the <Progress> chassis component (paired with `progressIndicatorVari…

  • progress.variants — progressVariants is the cva c…

  • progress.variants — progressVariants is the cva config that powers the <Progress> chassis component (paired with `progressIndicatorVari…

  • progress.variants — progressVariants is the cva c…

  • progress.variants — progressVariants is the cva config that powers the <Progress> chassis component (paired with `progressIndicatorVari…

  • progress.variants — progressVariants is the cva config that powers the <Progress> chassis component (paired with `progressIndicatorVari…

Purpose

<Progress> is the chassis progress bar built on @radix-ui/react-progress. It supports both indeterminate (Radix's loading-state animation when value === undefined) and determinate (value is 0..max, default max=100) modes. The chassis layers a size axis on top — sm (4px), default (8px), lg (12px) — while leaving width consumer-driven via className (typically w-full inside a card or form row).

The accessible name comes from the consumer-supplied aria-label (typed as TranslationKey); when absent, the chassis falls back to the bundled react-ui.progress.fallback translation key so screen-readers always announce something meaningful. This avoids the common bug where developers ship a progress bar with no accessible name and screen-readers read out the percentage without any context.

Features

  • Indeterminate mode when value === undefined (Radix runs its loading animation).
  • Determinate mode when value is a number 0..max.
  • Three sizes via the size axis (sm / default / lg).
  • aria-label typed as TranslationKey — falls back to bundled react-ui.progress.fallback when undefined.
  • motion-safe:transition-transform motion-reduce:transition-none on the indicator so reduced-motion users see instant updates.
  • Forwards refs to the Radix Root.

Usage: Basic Example

import { Progress } from "@suaveplan/react-ui";

export function UploadProgress({ percent }: { percent: number }) {
    return <Progress value={percent} />;
}

Usage: Advanced Example

import { Progress } from "@suaveplan/react-ui";

export function MultiStageUpload({
    currentStage,
    totalStages,
    isLoading,
}: {
    currentStage: number;
    totalStages: number;
    isLoading: boolean;
}) {
    return (
        <Progress
            size="lg"
            value={isLoading ? undefined : (currentStage / totalStages) * 100}
            aria-label="react-ui.loading"
            className="w-64"
        />
    );
}

API Reference

<Progress>

forwardRef<…, ProgressProps> — Radix Root wrapper that renders an inner Indicator translated based on value.

Prop Type Default Description
value number | undefined undefined When defined, determinate mode; when undefined, indeterminate mode.
max number 100 Forwarded to Radix as the upper bound for value.
size "sm" | "default" | "lg" "default" Bar height.
aria-label TranslationKey | undefined react-ui.progress.fallback Accessible name. Falls back to bundled key.
Radix props Radix-typed — All ProgressPrimitive.Root props (except aria-label which the chassis overrides).

Errors

The Progress component calls useTranslation() to resolve the aria-label translation key. It MUST render inside a <TranslationProvider> / <ReactUiProvider>; otherwise useTranslation throws.

Implementation Notes

  • When value === undefined the chassis passes null to Radix (matching Radix's API for indeterminate mode). Radix toggles its data-state="indeterminate" on the root and runs its built-in loading animation; consumers wanting a custom indeterminate animation override the indicator's transform via a [&>*]: arbitrary descendant.
  • The accessible-name fallback uses the bundled react-ui.progress.fallback key which all five locales translate. This guarantees the screen-reader announcement is meaningful even when consumers forget to supply their own label.
  • motion-safe:transition-transform only runs when the user has not requested reduced motion. Reduced-motion users see the indicator snap to its new position instantly — this is intentional, animated progress with reduced-motion can cause vestibular discomfort.
  • The chassis does NOT expose a getValueLabel prop (Radix supports it) because the chassis-supplied aria-label is sufficient for the common case; consumers wanting a percentage-readable label compose their own <span>{percent}%</span> next to the bar.
  • The chassis supports both indeterminate (loading) and determinate (0..100) modes from a single component because Radix already handles the state-machine internally; splitting them into two components would force consumers to refactor when the loading-known transition lands.