Progress
An accessible progress bar with determinate and indeterminate states.
import * as Progress from '@gentleduck/primitives/progress'import * as Progress from '@gentleduck/primitives/progress'Anatomy
<Progress.Root>
<Progress.Indicator />
</Progress.Root><Progress.Root>
<Progress.Indicator />
</Progress.Root>Example
<Progress.Root
className="relative h-4 w-full overflow-hidden rounded-full bg-gray-200"
value={65}
max={100}
>
<Progress.Indicator
className="h-full bg-blue-500 transition-all"
style={{ width: `${65}%` }}
/>
</Progress.Root><Progress.Root
className="relative h-4 w-full overflow-hidden rounded-full bg-gray-200"
value={65}
max={100}
>
<Progress.Indicator
className="h-full bg-blue-500 transition-all"
style={{ width: `${65}%` }}
/>
</Progress.Root>API
Progress.Root
Renders a <div> with role="progressbar" and appropriate ARIA attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | null | Current value. null = indeterminate. |
max | number | 100 | Maximum value |
getValueLabel | (value: number, max: number) => string | Percentage | Custom label for screen readers |
dir | 'ltr' | 'rtl' | -- | Reading direction |
Exposes data-state as indeterminate, loading, or complete, and data-value / data-max.
Progress.Indicator
Visual fill indicator. Inherits data-state, data-value, and data-max from context.
Indeterminate state
Pass value={null} for an indeterminate progress bar. The data-state will be set to indeterminate, which you can target in CSS for a loading animation.
<Progress.Root value={null} className="relative h-4 w-full overflow-hidden rounded-full bg-gray-200">
<Progress.Indicator className="h-full bg-blue-500 animate-indeterminate" />
</Progress.Root><Progress.Root value={null} className="relative h-4 w-full overflow-hidden rounded-full bg-gray-200">
<Progress.Indicator className="h-full bg-blue-500 animate-indeterminate" />
</Progress.Root>@keyframes indeterminate {
0% { transform: translateX(-100%); }
100% { transform: translateX(400%); }
}
.animate-indeterminate {
width: 30%;
animation: indeterminate 1.5s ease-in-out infinite;
}@keyframes indeterminate {
0% { transform: translateX(-100%); }
100% { transform: translateX(400%); }
}
.animate-indeterminate {
width: 30%;
animation: indeterminate 1.5s ease-in-out infinite;
}