IndiaCN UI

Progress Circle

Circular and half-circular progress indicator with an optional caption.

Introduction

The Progress bar answers "how far along a line". The circle answers "how much of a whole" — a quota consumed, a document set completed, a verification part-done. UX4G defines both shapes, five diameters and an optional caption.

Installation

npx shadcn@latest add https://indiacn.in/r/progress-circle.json
import { ProgressCircle } from '@/components/ui/progress-circle';

Usage

<ProgressCircle value={40} size="xs" />

Examples

Default

40%
ProgressCircleDefault
import { ProgressCircle } from '@/components/ui/progress-circle';
import { Label3 } from '@/components/ui/typography';

export default function Component() {
  return <ProgressCircle value={40} />;
}

Sizes

size takes a number of pixels as well as a named size, because UX4G's ladder starts at 64px and runs to 280px — dashboard scale, with nothing for an inline ring. The stroke stays 10% of whatever diameter you give it.

32px
40%
48px
40%
64px
40%
96px
ProgressCircleSizes
import { ProgressCircle } from '@/components/ui/progress-circle';
import { Label3 } from '@/components/ui/typography';

export default function Component() {
  return (
    <div className='flex flex-wrap items-end justify-center gap-8'>
      {([32, 48, 64, 96] as const).map(size => (
        <div key={size} className='flex flex-col items-center gap-2'>
          <ProgressCircle value={40} size={size} showValue={size >= 48} />
          <Label3 className='text-neutral-600'>{size}px</Label3>
        </div>
      ))}
    </div>
  );
}

Half circle

40%
72%
ProgressCircleHalf
import { ProgressCircle } from '@/components/ui/progress-circle';
import { Label3 } from '@/components/ui/typography';

export default function Component() {
  return (
    <div className='flex flex-wrap items-end justify-center gap-8'>
      <ProgressCircle value={40} size={96} shape='half' />
      <ProgressCircle value={72} size={140} shape='half' />
    </div>
  );
}

With a caption

Below 120px the caption sits beneath the ring, because nothing legible fits inside a small circle beside the number. Above it, the caption goes above the value, inside.

40%

Users

Uploading

40%
ProgressCircleWithLabel
import { ProgressCircle } from '@/components/ui/progress-circle';
import { Label3 } from '@/components/ui/typography';

export default function Component() {
  return (
    <div className='flex flex-wrap items-end justify-center gap-8'>
      <ProgressCircle value={40} label='Users' />
      <ProgressCircle value={40} size='xs' label='Uploading' />
    </div>
  );
}

Measurements

Read off the Figma symbols:

sizediameterstrokevalue text
xxs64px6px14px
xs160px16px24px
sm200px20px30px
md240px24px36px
lg280px28px48px

The stroke is a flat 10% of the diameter at every size, which is why it is computed rather than tabulated. Track is neutral-50, the arc is primary with a round cap.

Note how large these are: UX4G's smallest is 64px and its xs is 160px. That is a dashboard scale, not an inline one, and the names are misleading if you expect sm to be small. The default is xxs, and size accepts a number so you can ask for the 32px ring the kit does not define.

The full circle starts at twelve o'clock and sweeps clockwise. The half circle spans the top 180°, starting at nine o'clock — sampling the Figma arc at 40% puts its end at 342°, which is exactly nine o'clock plus 40% of 180°.

Guidelines

  • Use it for a proportion of a known total. For an indeterminate wait, use Spinner.
  • Pass a number for anything inline. The named sizes are UX4G's and start at 64px.
  • Turn showValue off below about 40px — a percentage inside a ring that small cannot be read.
  • The value is announced through role="progressbar". Pass label so a screen reader hears what is progressing, not just a number.

API Reference

ProgressCircle

PropTypeDefaultDescription
valuenumber0Current progress
maxnumber100Value representing complete
sizexxs | xs | sm | md | lg | numberxxsNamed diameter, or pixels
shapecircle | halfcircleFull ring or top half
labelstringCaption, and the accessible name
showValuebooleantrueRenders the percentage

On this page