Progress
Progress bar for work under way (with an indeterminate state and tones) and Meter for a quantity against a limit that turns warning and danger past thresholds. Wraps Base UI Progress and Meter.
Code
import { Progress } from "@fadymondy/nasaq/web";export function Upload({ percent }: { percent: number }) { return <Progress value={percent} label="Uploading files" />;}Loading & States · stable
Live examples and controls: Progress in the lab.
Install
npx shadcn@latest add https://docs.nasaqui.com/r/progress.jsonTwo thin bars that look alike and mean different things. Progress shows a task moving toward done: an
upload, an import, a setup. Meter shows an amount against a limit: seats used, storage, a monthly budget.
The fill starts at the inline start, so it grows from the right in Arabic.
When to use
Progress: something with a start and an end that takes long enough to see. Passvalue={null}when the length is unknown.Meter: a quota or budget where crossing a threshold matters. It changes tone for you.
When not to use
- A short wait with no length: use
Spinneror aLoadingState. - A rating or score: use
Rating. - A single status word: use
StatusorBadge. - A written warning about a limit: pair a
Meterwith anAlert.
Import
import { Meter, Progress } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"Quick start
import { Progress } from "@fadymondy/nasaq/web";
export function Upload({ percent }: { percent: number }) {
return <Progress value={percent} label="Uploading files" />;
}Anatomy
Progress / Meter Base UI Root (role="progressbar" / role="meter") data-slot="progress" | "meter"
├─ head label + value row (when label or showValue) data-slot="progress-head"
└─ track data-slot="progress-track" | "meter-track"
└─ indicator the fill data-slot="progress-indicator" | "meter-indicator"Both roots carry data-tone with the tone in use.
API
Progress
ProgressProps extends ComponentProps<"div"> (without children).
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | required | Current value. null is indeterminate. |
tone? | "default" | "info" | "success" | "warning" | "danger" | "default" | Fill colour. Set success when it completes. |
label? | ReactNode | none | Visible name above the bar. Without one, pass aria-label. |
showValue? | boolean | true when label is set | Shows the formatted value at the inline end. Hidden while indeterminate. |
valueText? | ReactNode | formatted value | Replaces the visible value text ("45 of 50 seats"). |
format? | Intl.NumberFormatOptions | percentage of the range | How the value is formatted. |
locale? | Intl.LocalesArgument | runtime locale | Locale for numbers, e.g. "ar-SA". |
size? | "sm" | "md" | "md" | Track height: 4px or 8px. |
min? / max? | number | 0 / 100 | Range. |
Meter
MeterProps shares the props above (label, showValue, valueText, format, locale, size, min, max) and has:
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | required | Current amount. Never null. |
warnAt? | number | 0.8 | Fraction of the range at which the fill turns warning. |
dangerAt? | number | 0.95 | Fraction of the range at which the fill turns danger. |
tone? | ProgressTone | derived | Forces a tone and skips the thresholds. |
Examples
Unknown length, then determinate
import { Progress } from "@fadymondy/nasaq/web";
export function Import({ percent }: { percent: number | null }) {
return (
<Progress
value={percent}
tone={percent === 100 ? "success" : "default"}
label={percent === null ? "Preparing…" : "Importing"}
/>
);
}Quota with visible text
import { Meter } from "@fadymondy/nasaq/web";
export function Seats() {
return <Meter value={42} max={50} label="Seats" valueText="42 of 50" />;
}Budget in Arabic, custom thresholds
import { Meter } from "@fadymondy/nasaq/web";
export function BudgetAr() {
return (
<Meter
value={7200}
max={10000}
warnAt={0.7}
dangerAt={0.9}
locale="ar-SA"
format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }}
label="ميزانية الذكاء الاصطناعي"
/>
);
}Accessibility
Progressisrole="progressbar"witharia-valuenow,aria-valuemin,aria-valuemax(noaria-valuenowwhen indeterminate).Meterisrole="meter".- The
labelnames the bar. If you omitlabel, passaria-labeloraria-labelledby. - Colour is never the only carrier: the value text stays visible, and a threshold crossing should be backed by text (an
AlertorvalueText). - Nothing is focusable and no keys apply.
- Indeterminate uses a pulse that stops under
prefers-reduced-motion; width changes do not animate then either.
RTL & i18n
- The fill is positioned with
inset-inline-start, so it grows from the right in RTL. The head row (label, value) mirrors with the document. - Numbers format with
locale(pass"ar-SA"for Arabic-Indic digits). The component has no built-in strings; localiselabelandvalueText.
Styling & tokens
- Track
bg-nq-surface-soft, fillbg-primary(default) orbg-nq-info/-success/-warning/-danger;rounded-full; 300ms width transition onease-nq. - Target
[data-slot=progress],[data-slot=meter],[data-tone=warning],progress-track,progress-indicator. Extend withclassName; set the width with a wrapper orclassName(the bar isw-full).
Do / Don't
- Do use
Meterfor limits andProgressfor tasks; the semantics differ for assistive tech. - Do show the numbers ("42 of 50"), not just the bar.
- Don't use
dangeron aProgressunless the task failed; stop the bar and show anAlert. - Don't fake a percentage for unknown work: use
value={null}.
Related
Lab
https://docs.nasaqui.com/?path=/docs/components-loading-states-progress--docs
ErrorPage
Full-page states for 404, 500, offline, maintenance, no access, unknown workspace and a module that is coming soon, with sensible actions, an error ID to quote and English and Arabic copy.
RouteProgress
A thin progress bar along the top edge for navigation and background jobs. It creeps while work runs, jumps to full when it ends, and a hook counts overlapping jobs.