Circular Progress
Circular progress indicators with conic gradient glow effects
Circular Progress
Circular progress indicators featuring conic gradient glow effects and radial masking. Perfect for displaying loading states, completion percentages, and decorative background elements.
Usage
import { CircularProgress, CircularRing, ProgressRing, DualRing } from "@/components/ui/circular-progress";
export default function Example() {
return (
<CircularProgress>
<CircularRing animated />
</CircularProgress>
);
}
Components
CircularProgress
The container component that provides radial masking and positioning for circular rings.
CircularRing
A decorative circular ring with conic gradient glow effect. Can be positioned and animated.
ProgressRing
An interactive progress indicator showing percentage completion with a conic gradient arc.
DualRing
Convenience component that renders two overlapping rings (Bento11 pattern).
Props
CircularProgress Props
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "default" | "lg" | "default" | Container size |
centered | boolean | true | Center container horizontally |
CircularRing Props
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "default" | "lg" | "default" | Ring diameter |
position | "top" | "bottom" | "center" | "center" | Vertical position |
animated | boolean | false | Enable rotation animation |
opacity | "full" | "medium" | "low" | "full" | Glow opacity |
rotation | number | 0 | Initial rotation angle (degrees) |
ProgressRing Props
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "default" | "lg" | "default" | Ring size |
value | number | - | Current progress value |
max | number | 100 | Maximum value |
showValue | boolean | true | Display percentage text |
DualRing Props
| Prop | Type | Default | Description |
|---|---|---|---|
topRotation | number | 135 | Top ring rotation (degrees) |
bottomRotation | number | 0 | Bottom ring rotation (degrees) |
topOpacity | "full" | "medium" | "low" | "full" | Top ring glow opacity |
bottomOpacity | "full" | "medium" | "low" | "low" | Bottom ring glow opacity |
Variants
Size Variants
// Small - 280px
<CircularProgress size="sm">
<CircularRing />
</CircularProgress>
// Default - 336px
<CircularProgress size="default">
<CircularRing />
</CircularProgress>
// Large - 400px
<CircularProgress size="lg">
<CircularRing />
</CircularProgress>
Position Variants
// Top half
<CircularRing position="top" />
// Bottom half
<CircularRing position="bottom" />
// Centered
<CircularRing position="center" />
Examples
Animated Ring
<CircularProgress>
<CircularRing animated />
</CircularProgress>
The ring rotates continuously with a 3.6s cycle using the button-glow animation.
Progress Indicator
import { useState } from "react";
export default function ProgressExample() {
const [progress, setProgress] = useState(68);
return (
<ProgressRing value={progress} max={100} />
);
}
Dual Ring Pattern (Bento11)
<DualRing
topRotation={135}
bottomRotation={0}
topOpacity="full"
bottomOpacity="low"
/>
This recreates the exact Bento11 pattern with two overlapping rings.
Multiple Rings
<CircularProgress>
<CircularRing position="top" rotation={135} opacity="full" />
<CircularRing position="bottom" rotation={0} opacity="low" />
<CircularRing position="center" rotation={270} opacity="medium" />
</CircularProgress>
Custom Rotation
<CircularRing rotation={45} />
<CircularRing rotation={90} />
<CircularRing rotation={180} />
With Content
<CircularProgress>
<CircularRing animated />
<div className="absolute inset-0 flex items-center justify-center z-10">
<div className="text-center">
<p className="text-4xl font-medium">68%</p>
<p className="text-sm text-[var(--ui-text-secondary)]">Complete</p>
</div>
</div>
</CircularProgress>
Design Tokens
Dimensions
| Size | Diameter |
|---|---|
sm | 280px |
default | 336px |
lg | 400px |
Effects
| Token | Value | Description |
|---|---|---|
| Border | rgba(255, 255, 255, 0.15) | Ring border |
| Glow | var(--gradient-conic-glow) | Conic gradient from 180deg |
| Mask | var(--gradient-radial-center) | Radial fade to transparent |
| Animation | button-glow 3.6s linear infinite | Rotation animation |
Opacity Levels
| Level | Opacity | Use Case |
|---|---|---|
full | 100% | Primary ring |
medium | 50% | Secondary ring |
low | 30% | Background ring |
Animation Details
The animated ring uses the button-glow keyframe animation defined in globals.css:
@keyframes button-glow {
0% { transform: translateX(0) rotate(0); }
25% { transform: translateX(-30px) rotate(90deg); }
50% { transform: translateX(0) rotate(180deg); }
75% { transform: translateX(30px) rotate(270deg); }
100% { transform: translateX(0) rotate(360deg); }
}
Conic Gradient
The glow effect uses a conic gradient:
conic-gradient(
from 180deg at 50% 50%,
rgba(255, 255, 255, 0.01) 180deg,
rgba(255, 255, 255, 0.05) 360deg
)
This creates a subtle rotating glow that appears to sweep around the ring.
Found In
- Bento11 - Dual ring background with social icons
- Bento14 - Single ring with avatar
- Bento23 - Progress indicator
- Bento24 - Loading state
- Bento25 - Completion percentage
Accessibility
ProgressRing
- Uses
role="progressbar"for semantic meaning - Includes
aria-valuenow,aria-valuemin,aria-valuemax - Percentage text is readable by screen readers
- Visual progress is supplemented with text
CircularRing (Decorative)
- Purely decorative background element
- Does not interfere with content accessibility
- Radial mask ensures content remains readable
Best Practices
// For loading states
<ProgressRing
value={progress}
aria-label="Upload progress"
/>
// For decorative backgrounds
<CircularProgress aria-hidden="true">
<CircularRing animated />
</CircularProgress>
Performance
- Pure CSS implementation for glow effect
- Hardware-accelerated transforms
- Single animation keyframe shared across all rings
- Minimal DOM nodes (container + rings)
- No JavaScript required for animation