Glin UI
Primitive / Atom

Progress

Determinate linear and circular progress indicators with tokenized variants and sizing.

Edit on GitHub

Uploading 3 files

About 40 seconds left

brand-guidelines.pdf100%
homepage-v3.fig64%
launch-assets.zip23%

Installation

Install from
pnpm dlx @glinui/cli@latest add progress

Prefer the package?

Usage

Onboarding steps

A circular summary, a bar and a checklist share one value.

Get started

2 of 4 steps complete

50%
  • Create workspace
  • Invite your team
  • Connect a repository
  • Ship your first deploy
import { CheckCircle } from "@phosphor-icons/react"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Progress,
ProgressCircle
} from "@glinui/ui"
export function Demo() {
const steps = [
{ label: "Create workspace", done: true },
{ label: "Invite your team", done: true },
{ label: "Connect a repository", done: false },
{ label: "Ship your first deploy", done: false }
]
const done = steps.filter((step) => step.done).length
return (
<Card className="w-full max-w-sm">
<CardHeader>
<div className="flex items-center justify-between gap-4">
<div>
<CardTitle>Get started</CardTitle>
<CardDescription>{done} of {steps.length} steps complete</CardDescription>
</div>
<ProgressCircle value={(done / steps.length) * 100} size="lg" showValue aria-label="Onboarding progress" />
</div>
</CardHeader>
<CardContent className="flex flex-col gap-3">
<Progress aria-label="Onboarding progress" value={(done / steps.length) * 100} />
<ul className="flex flex-col gap-2 text-sm">
{steps.map((step) => (
<li key={step.label} className="flex items-center gap-2">
<CheckCircle
aria-hidden
weight={step.done ? "fill" : "regular"}
className={step.done ? "size-4 text-[var(--tone-success)]" : "size-4 text-[var(--color-muted)]"}
/>
<span className={step.done ? "text-[var(--color-muted)] line-through" : "font-medium"}>{step.label}</span>
</li>
))}
</ul>
</CardContent>
</Card>
)
}

Variants

import { Progress } from "@glinui/ui"
export function Demo() {
return (
<div className="space-y-3">
<Progress variant="glinr" value={30} aria-label="glinr" />
<Progress variant="solid" value={38} aria-label="solid" />
<Progress variant="plain" value={46} aria-label="plain" />
<Progress variant="soft" value={54} aria-label="soft" />
<Progress variant="outline" value={62} aria-label="outline" />
<Progress variant="ghost" value={70} aria-label="ghost" />
<Progress variant="gradient" value={78} aria-label="gradient" />
</div>
)
}

Sizes and indeterminate

Three sizes animate in sync. Use indeterminate when the duration is unknown.

size sm
size md
size lg
indeterminate
import { useEffect, useState } from "react"
import { Progress } from "@glinui/ui"
export function Demo() {
const [value, setValue] = useState(35)
useEffect(() => {
const id = window.setInterval(() => setValue((current) => (current >= 100 ? 8 : current + 9)), 900)
return () => window.clearInterval(id)
}, [])
return (
<div className="flex w-full max-w-md flex-col gap-5">
{(["sm", "md", "lg"] as const).map((size) => (
<div key={size} className="flex flex-col gap-1.5">
<span className="font-mono text-xs text-[var(--color-muted)]">size {size}</span>
<Progress aria-label={`Progress ${size}`} size={size} value={value} />
</div>
))}
<div className="flex flex-col gap-1.5">
<span className="font-mono text-xs text-[var(--color-muted)]">indeterminate</span>
<Progress aria-label="Waiting for server" indeterminate />
</div>
</div>
)
}

Variants

import { Progress } from "@glinui/ui"

export function ProgressVariantDemo() {
  return (
    <div className="space-y-3">
      <Progress value={52} />
      <Progress variant="glass" value={68} />
      <Progress variant="liquid" value={82} />
      <Progress variant="matte" value={38} />
    </div>
  )
}

Sizes

import { Progress } from "@glinui/ui"

export function ProgressSizeDemo() {
  return (
    <div className="space-y-3">
      <Progress size="sm" value={30} />
      <Progress size="md" value={50} />
      <Progress size="lg" value={80} />
    </div>
  )
}

Circular Progress

import { ProgressCircle } from "@glinui/ui"

export function ProgressCircleDemo() {
  return (
    <div className="flex flex-wrap items-center gap-5">
      <ProgressCircle size="sm" value={28} aria-label="Small progress" />
      <ProgressCircle size="md" variant="glass" value={64} aria-label="Glass progress" />
      <ProgressCircle size="lg" variant="liquid" value={86} aria-label="Liquid progress" />
    </div>
  )
}

Accessibility

  • Built on Radix Progress with `role="progressbar"`.
  • Provide `aria-label` for screen readers.
  • Root uses Radix Progress semantics with `role="progressbar"`.
  • Circular variant also exposes `role="progressbar"` with proper value attrs.
  • Provide `aria-label` or external label for non-visual users.
  • Values are clamped between `0` and `100`.

ARIA Attributes

  • `role="progressbar"`
  • `aria-valuenow`
  • `aria-valuemin`
  • `aria-valuemax`

Reduced Motion

Width transition respects `prefers-reduced-motion` via `motion-reduce:transition-none`.

Width transitions are disabled when `prefers-reduced-motion` is enabled.

Affected properties

width

API Reference

PropTypeRequiredDefaultDescription
variantControlVariantPropNoambient (glinr)Look. Omit to follow the ambient design style: glinr is an inset track with an accent fill. Glass is opt-in.
size"sm" | "md" | "lg"NomdTrack height (h-2/h-3/h-4).
valuenumberNo0Progress value (0-100, clamped).
indeterminatebooleanNofalseLoading state without a specific value.

Source

Import directly from the package or browse the source on GitHub. Click any file to view it.

Import

import { Progress } from "@glinui/ui"

Source Files

Dependencies

@glinui/ui

Generated API Snapshot

Beta

Auto-extracted from TypeScript source in packages/ui/src/components/progress.tsx. This section is in beta and may lag behind hand-curated docs. Regenerate with pnpm --filter @glinui/docs api:generate.

Generated: 2026-10-08T01:41:21.546Z · Full index: /docs/api-metadata

Primary Props Type

ProgressProps

ProgressCircleProps

PropTypeRequiredDefaultDescription
cap"round" | "square" | "butt"No"round"-
formatValue(value: number) => React.ReactNodeNo--
indeterminatebooleanNofalse-
showValuebooleanNotrue-
size"sm" | "md" | "lg"No"md"Variant option from progressCircleVariants.
strokeWidthnumberNo4-
valuenumberNo--
variantControlVariantPropNo--

ProgressProps

PropTypeRequiredDefaultDescription
indeterminatebooleanNofalseRender loading state without aria-valuenow and fixed width indicator.
size"sm" | "md" | "lg"No"md"Variant option from progressVariants.
valuenumberNo-Current progress value from 0 to 100.
variantControlVariantPropNo-Look. Omit to follow the ambient design style (glinr = inset track, accent fill). Also: solid, plain, soft, outline, ghost, gradient, glass (opt-in), liquid, matte.