Barras de vida y experiencia 8 bits
Bloques de píxeles que se llenan, marco blanco y parpadeo en estado crítico, como un videojuego retro.
HéroeNv 7
HP72
XP40
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";
/** Barras de vida y experiencia estilo videojuego de 8 bits, con bloques que se llenan, marco de píxeles y parpadeo en estado crítico. React + TypeScript, sin dependencias. */
export type ProgresoPixelProps = {
hp?: number;
xp?: number;
name?: string;
level?: number;
simulate?: boolean;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcprx-root { box-sizing: border-box; display: grid; gap: 14px; width: 100%; max-width: 380px; padding: 18px; background: #1b1034; color: #fff; font: 700 13px/1 "Press Start 2P", ui-monospace, "SF Mono", Menlo, Consolas, monospace; letter-spacing: .04em; text-transform: uppercase; image-rendering: pixelated; box-shadow: 0 -4px 0 0 #fff, 0 4px 0 0 #fff, -4px 0 0 0 #fff, 4px 0 0 0 #fff, 0 -8px 0 -2px #14081f, 0 8px 0 -2px #14081f, -8px 0 0 -2px #14081f, 8px 0 0 -2px #14081f; }
.gcprx-root *, .gcprx-root *::before, .gcprx-root *::after { box-sizing: border-box; }
@media (prefers-reduced-motion: reduce) { .gcprx-root * { animation: none !important; } }
.gcprx-top { display: flex; justify-content: space-between; align-items: center; }
.gcprx-row { display: grid; grid-template-columns: 34px 1fr 48px; align-items: center; gap: 10px; }
.gcprx-lab { color: #ffd23f; }
.gcprx-bar { display: grid; grid-template-columns: repeat(20, 1fr); gap: 2px; height: 20px; padding: 3px; background: #0b0517; box-shadow: 0 -2px 0 0 #fff, 0 2px 0 0 #fff, -2px 0 0 0 #fff, 2px 0 0 0 #fff; }
.gcprx-bar i { background: #2a1950; }
.gcprx-bar i[data-on="true"] { background: var(--c); box-shadow: inset 0 -3px 0 rgba(0, 0, 0, .3), inset 0 3px 0 rgba(255, 255, 255, .3); }
.gcprx-row[data-low="true"] .gcprx-bar i[data-on="true"] { animation: gcprx-blink .5s steps(2, jump-none) infinite; }
.gcprx-num { text-align: right; font-variant-numeric: tabular-nums; }
@keyframes gcprx-blink { 50% { opacity: .35; } }`;
export function ProgresoPixel({ hp, xp, name = "Héroe", level = 7, simulate = true, className = "", style }: ProgresoPixelProps) {
const [aHp, setHp] = useState(72);
const [aXp, setXp] = useState(40);
useEffect(() => {
if ((hp !== undefined && xp !== undefined) || !simulate || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
const id = window.setInterval(() => {
if (document.hidden) return;
setHp((v) => (v <= 8 ? 100 : Math.max(5, v - 6 - Math.random() * 10)));
setXp((v) => (v >= 100 ? 0 : Math.min(100, v + 5 + Math.random() * 8)));
}, 900);
return () => window.clearInterval(id);
}, [hp, xp, simulate]);
const h = Math.round(Math.max(0, Math.min(100, hp ?? aHp)));
const x = Math.round(Math.max(0, Math.min(100, xp ?? aXp)));
const bar = (v: number, c: string, label: string) => (
<div className="gcprx-bar" role="progressbar" aria-valuemin={0} aria-valuemax={100} aria-valuenow={v} aria-label={label} style={{ "--c": c } as CSSProperties}>
{Array.from({ length: 20 }, (_, i) => (
<i key={i} data-on={i < Math.round(v / 5)} />
))}
</div>
);
return (
<div className={`gcprx-root ${className}`} style={style}>
<style>{CSS}</style>
<div className="gcprx-top">
<span>{name}</span>
<span className="gcprx-lab">Nv {level}</span>
</div>
<div className="gcprx-row" data-low={h < 25}>
<span className="gcprx-lab">HP</span>
{bar(h, h < 25 ? "#ff4d4d" : "#4ddc6a", "Vida")}
<span className="gcprx-num">{h}</span>
</div>
<div className="gcprx-row">
<span className="gcprx-lab">XP</span>
{bar(x, "#5aa8ff", "Experiencia")}
<span className="gcprx-num">{x}</span>
</div>
</div>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/progreso-pixelEjemplo de uso de Barras de vida y experiencia 8 bits
Después de copiar progreso-pixel.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { ProgresoPixel } from "./progreso-pixel";
export default function Ejemplo() {
return <ProgresoPixel simulate />;
}Propiedades de ProgresoPixel
ProgresoPixel acepta 7 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
hp? | number | Opcional. |
xp? | number | Opcional. |
name? | string | Opcional. |
level? | number | Opcional. |
simulate? | boolean | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Accesibilidad y rendimiento
El archivo tiene 71 líneas (4 KB) y define 1 animación con @keyframes. Esto es lo que hace Barras de vida y experiencia 8 bits 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.
- Es un componente de cliente de Next.js: incluye la directiva "use client".