Feedback feedbackprogressringcircularmeter
Progress Ring Counter
Circular SVG progress gauge with smooth stroke dashoffset transition, customizable track colors, and centered value readout.
Preview
75% COMPLETED
Usage
Copy the full block below to use this component with its imports.
astro
---
import { ProgressRingCounter } from 'astro-component-kit';
---
<ProgressRingCounter value={78} max={100} size={110} strokeWidth={8} color="var(--c-primary)" label="Score" /> ---
import { ProgressRingCounter } from 'astro-component-kit';
---
<ProgressRingCounter value={78} max={100} size={110} strokeWidth={8} color="var(--c-primary)" label="Score" /> Manual Installation
If you are not using the npm package, create a new file src/components/lib/ProgressRingCounter.astro and paste the following code:
astro
---
/**
* ProgressRingCounter — Circular SVG progress ring with centered countdown or metric value.
*
* @param {number} value - Current counter or progress value. Default is 75.
* @param {number} max - Maximum value threshold. Default is 100.
* @param {number} size - Pixel diameter of the ring. Default is 96.
* @param {number} strokeWidth - Thickness of the ring stroke. Default is 6.
* @param {string} color - Primary gauge fill color. Default is '#d4a017'.
* @param {string} label - Subtitle label beneath figure. Default is 'COMPLETED'.
*/
interface Props {
value?: number;
max?: number;
size?: number;
strokeWidth?: number;
color?: string;
label?: string;
}
const {
value = 75,
max = 100,
size = 96,
strokeWidth = 6,
color = '#d4a017',
label = 'COMPLETED',
} = Astro.props;
const radius = (size - strokeWidth) / 2;
const circumference = 2 * Math.PI * radius;
const percent = Math.min(100, Math.max(0, (value / max) * 100));
const strokeDashoffset = circumference - (percent / 100) * circumference;
---
<div class="ring-counter" style={`--ring-size: ${size}px; --ring-color: ${color};`}>
<svg width={size} height={size} class="ring-counter__svg">
<circle
class="ring-counter__bg"
cx={size / 2}
cy={size / 2}
r={radius}
stroke-width={strokeWidth}
/>
<circle
class="ring-counter__meter"
cx={size / 2}
cy={size / 2}
r={radius}
stroke-width={strokeWidth}
stroke-dasharray={circumference}
stroke-dashoffset={strokeDashoffset}
/>
</svg>
<div class="ring-counter__center">
<span class="val">{Math.round(percent)}%</span>
{label && <span class="lbl">{label}</span>}
</div>
</div>
<style>
.ring-counter {
position: relative;
width: var(--ring-size);
height: var(--ring-size);
display: inline-flex;
align-items: center;
justify-content: center;
font-family: var(--font-sans, system-ui, sans-serif);
user-select: none;
}
.ring-counter__svg {
transform: rotate(-90deg);
width: 100%;
height: 100%;
}
.ring-counter__bg {
fill: none;
stroke: rgba(255, 255, 255, 0.08);
}
.ring-counter__meter {
fill: none;
stroke: var(--ring-color);
stroke-linecap: round;
transition: stroke-dashoffset 0.8s cubic-bezier(0.16, 1, 0.3, 1);
filter: drop-shadow(0 0 6px rgba(212, 160, 23, 0.35));
}
.ring-counter__center {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.ring-counter__center .val {
font-size: 1.25rem;
font-weight: 800;
color: var(--c-text-1, #ffffff);
line-height: 1;
}
.ring-counter__center .lbl {
font-size: 0.55rem;
font-weight: 700;
letter-spacing: 0.08em;
color: var(--c-text-3, #737373);
margin-top: 4px;
text-transform: uppercase;
}
</style> ---
/**
* ProgressRingCounter — Circular SVG progress ring with centered countdown or metric value.
*
* @param {number} value - Current counter or progress value. Default is 75.
* @param {number} max - Maximum value threshold. Default is 100.
* @param {number} size - Pixel diameter of the ring. Default is 96.
* @param {number} strokeWidth - Thickness of the ring stroke. Default is 6.
* @param {string} color - Primary gauge fill color. Default is '#d4a017'.
* @param {string} label - Subtitle label beneath figure. Default is 'COMPLETED'.
*/
interface Props {
value?: number;
max?: number;
size?: number;
strokeWidth?: number;
color?: string;
label?: string;
}
const {
value = 75,
max = 100,
size = 96,
strokeWidth = 6,
color = '#d4a017',
label = 'COMPLETED',
} = Astro.props;
const radius = (size - strokeWidth) / 2;
const circumference = 2 * Math.PI * radius;
const percent = Math.min(100, Math.max(0, (value / max) * 100));
const strokeDashoffset = circumference - (percent / 100) * circumference;
---
<div class="ring-counter" style={`--ring-size: ${size}px; --ring-color: ${color};`}>
<svg width={size} height={size} class="ring-counter__svg">
<circle
class="ring-counter__bg"
cx={size / 2}
cy={size / 2}
r={radius}
stroke-width={strokeWidth}
/>
<circle
class="ring-counter__meter"
cx={size / 2}
cy={size / 2}
r={radius}
stroke-width={strokeWidth}
stroke-dasharray={circumference}
stroke-dashoffset={strokeDashoffset}
/>
</svg>
<div class="ring-counter__center">
<span class="val">{Math.round(percent)}%</span>
{label && <span class="lbl">{label}</span>}
</div>
</div>
<style>
.ring-counter {
position: relative;
width: var(--ring-size);
height: var(--ring-size);
display: inline-flex;
align-items: center;
justify-content: center;
font-family: var(--font-sans, system-ui, sans-serif);
user-select: none;
}
.ring-counter__svg {
transform: rotate(-90deg);
width: 100%;
height: 100%;
}
.ring-counter__bg {
fill: none;
stroke: rgba(255, 255, 255, 0.08);
}
.ring-counter__meter {
fill: none;
stroke: var(--ring-color);
stroke-linecap: round;
transition: stroke-dashoffset 0.8s cubic-bezier(0.16, 1, 0.3, 1);
filter: drop-shadow(0 0 6px rgba(212, 160, 23, 0.35));
}
.ring-counter__center {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.ring-counter__center .val {
font-size: 1.25rem;
font-weight: 800;
color: var(--c-text-1, #ffffff);
line-height: 1;
}
.ring-counter__center .lbl {
font-size: 0.55rem;
font-weight: 700;
letter-spacing: 0.08em;
color: var(--c-text-3, #737373);
margin-top: 4px;
text-transform: uppercase;
}
</style>
Quick Info
- Category
- Feedback
- Filename
ProgressRingCounter.astro- Dependencies
- None — pure Astro + CSS
- Tags
- feedbackprogressringcircularmeter