feedbackclient

progress

a track that fills up as something gets done.

uploading cover image

installation

new to blurrow? this sets up your project in one go: the package, the styles and the font. already done it? skip straight to usage. the installation page has the details.

bash
npx @blurrow/ui init
›rather own the code? copy the source instead

let the cli copy it, along with anything it needs:

bash
npx @blurrow/ui add progress

or by hand: drop this file into components/ui and import from there.

components/ui/Progress.tsxtsx
"use client";
import React, { useEffect, useRef, useState } from "react";
interface ProgressProps {
value: number;
max?: number;
label?: string;
showValue?: boolean;
className?: string;
}
export function Progress({
value,
max = 100,
label,
showValue = false,
className = "",
}: ProgressProps) {
const pct = Math.min(Math.max((value / max) * 100, 0), 100);
const [displayed, setDisplayed] = useState(0);
const t = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
t.current = setTimeout(() => setDisplayed(pct), 80);
return () => { if (t.current) clearTimeout(t.current); };
}, [pct]);
return (
<div className={["flex flex-col gap-2", className].join(" ")}>
{(label || showValue) && (
<div className="flex items-center justify-between gap-4">
{label && (
<span className="text-[13px] font-medium text-neutral-700 truncate">{label}</span>
)}
{showValue && (
<span aria-hidden className="text-[12px] tabular-nums text-neutral-500 shrink-0">
{Math.round(pct)}%
</span>
)}
</div>
)}
<div
className="w-full h-2 rounded-full p-px overflow-hidden"
role="progressbar"
aria-label={label}
aria-valuenow={value}
aria-valuemin={0}
aria-valuemax={max}
style={{ background: "var(--well)", boxShadow: "var(--well-shadow)" }}
>
<div
className="h-full rounded-full relative overflow-hidden"
style={{
width: `${displayed}%`,
/* Never collapses below a pill, so 1% still reads as started */
minWidth: displayed > 0 ? 6 : 0,
background: "var(--face-dark)",
boxShadow: "inset 0 2px 3px -2px rgba(255,255,255,0.135), 0 0 2px rgba(0,0,0,0.2)",
transition: "width 700ms var(--ease-out-soft)",
}}
/>
</div>
</div>
);
}

usage

tsx
import { Progress } from "@blurrow/ui";
tsx
<Progress label="uploading" value={64} showValue />

examples

stacked

articles written
subscriber goal
storage used
draft completion

props

straight from the source, so they're always up to date.

propsts
interface ProgressProps {
value: number;
max?: number;
label?: string;
showValue?: boolean;
className?: string;
}