Anillo de progreso claymorfismo
Anillo grueso de plastilina con una bolita que recorre el arco y la cifra al centro.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import { useEffect, useState, type CSSProperties } from "react";
/** Progreso claymorfismo: anillo grueso de plastilina con una bolita que recorre el arco y cifra central. Colores configurables. React + TypeScript, sin dependencias. */
export type ProgresoArcillaProps = {
value?: number;
label?: string;
color?: string;
simulate?: boolean;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcpra-root { box-sizing: border-box; display: inline-grid; justify-items: center; gap: 14px; padding: 26px 30px 22px; border-radius: 36px; background: #fff0e6; color: #40284d; font: 800 15px/1.2 ui-rounded, "SF Pro Rounded", "Nunito", ui-sans-serif, system-ui, sans-serif; box-shadow: inset -8px -10px 18px rgba(214, 150, 100, .3), inset 8px 10px 18px rgba(255, 255, 255, .9), 14px 18px 28px -10px rgba(190, 110, 60, .4); }
.gcpra-root *, .gcpra-root *::before, .gcpra-root *::after { box-sizing: border-box; }
@media (prefers-reduced-motion: reduce) { .gcpra-root * { transition-duration: .01ms !important; } }
.gcpra-ring { position: relative; width: 180px; height: 180px; }
.gcpra-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); overflow: visible; }
.gcpra-ring circle { fill: none; stroke-width: 24; stroke-linecap: round; }
.gcpra-ring .t { stroke: #f3d7c4; filter: drop-shadow(inset 0 0 0 #000); }
.gcpra-ring .v { stroke: var(--c); transition: stroke-dashoffset .6s cubic-bezier(.34, 1.3, .64, 1); filter: drop-shadow(4px 6px 5px color-mix(in srgb, var(--c) 55%, #4a1a30)); }
.gcpra-ball { position: absolute; left: 50%; top: 50%; width: 34px; height: 34px; margin: -17px; border-radius: 50%; background: #fff; box-shadow: inset -4px -5px 8px rgba(190, 120, 80, .45), inset 4px 5px 8px #fff, 3px 6px 10px rgba(60, 30, 80, .5), 0 0 0 3px color-mix(in srgb, var(--c) 70%, #fff); transition: transform .6s cubic-bezier(.34, 1.3, .64, 1); }
.gcpra-num { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; font: 900 40px/1 ui-rounded, "SF Pro Rounded", "Nunito", ui-sans-serif, sans-serif; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.gcpra-num small { font-size: 13px; font-weight: 700; letter-spacing: 0; opacity: .7; }`;
export function ProgresoArcilla({ value, label = "Meta del mes", color = "#ff9a7a", simulate = true, className = "", style }: ProgresoArcillaProps) {
const [auto, setAuto] = useState(64);
useEffect(() => {
if (value !== undefined || !simulate || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
const id = window.setInterval(() => !document.hidden && setAuto((v) => (v >= 100 ? 12 : Math.min(100, v + 7 + Math.random() * 9))), 1100);
return () => window.clearInterval(id);
}, [value, simulate]);
const v = Math.round(Math.max(0, Math.min(100, value ?? auto)));
const R = 70;
const C = 2 * Math.PI * R;
const angle = (v / 100) * 360 - 90;
return (
<div className={`gcpra-root ${className}`} style={{ "--c": color, ...style } as CSSProperties}>
<style>{CSS}</style>
<div className="gcpra-ring" role="progressbar" aria-valuemin={0} aria-valuemax={100} aria-valuenow={v} aria-label={label}>
<svg viewBox="0 0 180 180" aria-hidden="true">
<circle className="t" cx="90" cy="90" r={R} />
<circle className="v" cx="90" cy="90" r={R} strokeDasharray={C} strokeDashoffset={C * (1 - v / 100)} />
</svg>
<span className="gcpra-ball" aria-hidden="true" style={{ transform: `translate(${Math.cos((angle * Math.PI) / 180) * R}px, ${Math.sin((angle * Math.PI) / 180) * R}px) rotate(0deg)` }} />
<span className="gcpra-num" aria-hidden="true">
{v}%
<small>{label}</small>
</span>
</div>
</div>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/progreso-arcillaEjemplo de uso de Anillo de progreso claymorfismo
Después de copiar progreso-arcilla.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { ProgresoArcilla } from "./progreso-arcilla";
export default function Ejemplo() {
return <ProgresoArcilla label="Ejemplo" simulate />;
}Propiedades de ProgresoArcilla
ProgresoArcilla acepta 6 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
value? | number | Opcional. |
label? | string | Opcional. |
color? | string | Opcional. |
simulate? | boolean | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Accesibilidad y rendimiento
El archivo tiene 56 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Anillo de progreso claymorfismo por quien lo usa:
- Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
- Declara roles ARIA para lectores de pantalla: progressbar.
- Calcula tonos con color-mix(), así que un solo color base alcanza.
- Es un componente de cliente de Next.js: incluye la directiva "use client".