A stateless progress indicator that displays the completion of a task. Renders as a native <progress> element for linear bars — giving you built-in browser semantics — and as an SVG ring with role="progressbar" for the circular variant. Use it for uploads, loading states, step flows, and any operation where showing progress reduces perceived wait time.
The variant prop switches between linear (a horizontal bar) and circular (a ring). Both support determinate states (via value and max) and indeterminate states (omit value or pass indeterminate). Indeterminate linear bars animate with a sliding shimmer; indeterminate rings spin continuously.
---
// GENERATED from packages/demos/progress/variants.demo.tsx — edit the source, not this file.
import Progress from "@pindoba/astro-progress";
import { stack } from "@pindoba/styled-system/patterns";
---
<div
class={stack({
gap: "xl",
direction: "column",
})}
>
<div>
<h3>Linear</h3>
<div class={stack({ gap: "md", direction: "column" })}>
<Progress value={30} label="Task progress" />
<Progress value={65} label="Upload progress" />
<Progress value={100} label="Complete" />
</div>
</div>
<div>
<h3>Linear Indeterminate</h3>
<Progress indeterminate label="Loading…" />
</div>
<div>
<h3>Circular</h3>
<div class={stack({ gap: "md", direction: "row", align: "center" })}>
<Progress variant="circular" value={30} label="30%" />
<Progress variant="circular" value={65} label="65%" />
<Progress variant="circular" value={100} label="Complete" />
</div>
</div>
<div>
<h3>Circular Indeterminate</h3>
<Progress variant="circular" indeterminate label="Loading…" />
</div>
</div>Use tone to convey meaning — success for a completed upload, danger for a failed operation, warning for a degraded state, primary for general brand progress. Both linear and circular variants respond to the same tone tokens.
---
// GENERATED from packages/demos/progress/tone.demo.tsx — edit the source, not this file.
import Progress from "@pindoba/astro-progress";
import { stack } from "@pindoba/styled-system/patterns";
---
<div
class={stack({
gap: "xl",
direction: "column",
})}
>
<div>
<h3>Neutral</h3>
<div class={stack({ gap: "sm", direction: "column" })}>
<Progress value={60} tone="neutral" label="Neutral progress" />
<Progress variant="circular" value={60} tone="neutral" label="60%" />
</div>
</div>
<div>
<h3>Primary</h3>
<div class={stack({ gap: "sm", direction: "column" })}>
<Progress value={60} tone="primary" label="Primary progress" />
<Progress variant="circular" value={60} tone="primary" label="60%" />
</div>
</div>
<div>
<h3>Success</h3>
<div class={stack({ gap: "sm", direction: "column" })}>
<Progress value={60} tone="success" label="Success progress" />
<Progress variant="circular" value={60} tone="success" label="60%" />
</div>
</div>
<div>
<h3>Warning</h3>
<div class={stack({ gap: "sm", direction: "column" })}>
<Progress value={60} tone="warning" label="Warning progress" />
<Progress variant="circular" value={60} tone="warning" label="60%" />
</div>
</div>
<div>
<h3>Danger</h3>
<div class={stack({ gap: "sm", direction: "column" })}>
<Progress value={60} tone="danger" label="Danger progress" />
<Progress variant="circular" value={60} tone="danger" label="60%" />
</div>
</div>
</div>Three sizes — sm, md (default), lg — scale the bar height and ring diameter proportionally. The label slot font size scales with the chosen size.
---
// GENERATED from packages/demos/progress/size.demo.tsx — edit the source, not this file.
import Progress from "@pindoba/astro-progress";
import { stack } from "@pindoba/styled-system/patterns";
---
<div
class={stack({
gap: "xl",
direction: "column",
})}
>
<div>
<h3>Linear Sizes</h3>
<div class={stack({ gap: "md", direction: "column" })}>
<Progress size="sm" value={50} label="Small" />
<Progress size="md" value={50} label="Medium (default)" />
<Progress size="lg" value={50} label="Large" />
</div>
</div>
<div>
<h3>Circular Sizes</h3>
<div class={stack({ gap: "md", direction: "row", align: "center" })}>
<Progress variant="circular" size="sm" value={50} label="Small" />
<Progress
variant="circular"
size="md"
value={50}
label="Medium (default)"
/>
<Progress variant="circular" size="lg" value={50} label="Large" />
</div>
</div>
</div>Use showLabel to render a visible value next to the indicator. By default it shows the percentage (65%). Pass formatValue to replace that with any string — step counts, file sizes, or any unit that fits the context.
---
// GENERATED from packages/demos/progress/label.demo.tsx — edit the source, not this file.
import Progress from "@pindoba/astro-progress";
import { stack } from "@pindoba/styled-system/patterns";
---
<div
class={stack({
gap: "xl",
direction: "column",
})}
>
<div>
<h3>Default percentage</h3>
<div class={stack({ gap: "sm", direction: "column" })}>
<Progress value={65} showLabel label="Upload progress" />
<Progress
variant="circular"
value={65}
showLabel
label="Upload progress"
/>
</div>
</div>
<div>
<h3>Step count</h3>
<div class={stack({ gap: "sm", direction: "column" })}>
<Progress
value={3}
max={5}
showLabel
formatValue={(v: number, m: number) => `Step ${v} of ${m}`}
label="Onboarding progress"
/>
<Progress
variant="circular"
value={3}
max={5}
showLabel
formatValue={(v: number, m: number) => `${v}/${m}`}
label="Onboarding progress"
/>
</div>
</div>
<div>
<h3>File size</h3>
<div class={stack({ gap: "sm", direction: "column" })}>
<Progress
value={42}
max={100}
showLabel
formatValue={(v: number, m: number) => `${v} MB of ${m} MB`}
tone="primary"
label="Download progress"
/>
<Progress
variant="circular"
value={42}
max={100}
showLabel
formatValue={(v: number, _m: number) => `${v} MB`}
tone="primary"
label="Download progress"
/>
</div>
</div>
</div>Use formatValue to replace the default percentage label with any string (e.g. step counts). Use passThrough.root.style for Panda CSS layout overrides on the wrapper, and passThrough.root.props to forward ARIA attributes or data attributes to the outermost element.
Use passThrough.root.props to attach ARIA attributes, event handlers, or data attributes to the outer wrapper element.
---
// GENERATED from packages/demos/progress/custom.demo.tsx — edit the source, not this file.
import Progress from "@pindoba/astro-progress";
import { stack } from "@pindoba/styled-system/patterns";
import { css } from "@pindoba/styled-system/css";
---
<div
class={stack({
gap: "xl",
direction: "column",
})}
>
<div>
<h3>Custom Max and Label</h3>
<Progress
value={3}
max={10}
showLabel
formatValue={(v: number, m: number) => `${v} of ${m} steps`}
label="Step progress"
tone="primary"
/>
</div>
<div>
<h3>Custom Track Styling</h3>
<Progress
value={70}
tone="success"
passThrough={{
root: {
style: css.raw({
maxWidth: "400px",
}),
},
track: {
style: css.raw({
borderRadius: "none",
}),
},
}}
label="Custom radius progress"
/>
</div>
<div>
<h3>Accessible Label via Props</h3>
<Progress
variant="circular"
value={42}
tone="warning"
passThrough={{
root: {
props: {
"aria-describedby": "upload-status",
},
},
}}
label="File upload"
/>
<p
id="upload-status"
class={css({ fontSize: "sm", color: "fg.subtle", mt: "xs" })}
>
Use <code>passThrough.root.props</code> to attach ARIA attributes, event
handlers, or data attributes to the outer wrapper element.
</p>
</div>
</div>