Esqueleto oscuro con pulso
Panel de datos con título, tres indicadores y tabla en bloques que respiran en cascada.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import type { CSSProperties } from "react";
/** Esqueleto oscuro con pulso para un panel de datos: título, indicadores y tabla ocupan su lugar con bloques que respiran en cascada. Minimalista. React + TypeScript, sin dependencias. */
export type EsqueletoPulsoProps = {
rows?: number;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcesp-root { box-sizing: border-box; display: grid; gap: 18px; width: 100%; max-width: 520px; padding: 20px; border: 1px solid #23262f; border-radius: 16px; background: #0f1115; }
.gcesp-root *, .gcesp-root *::before, .gcesp-root *::after { box-sizing: border-box; }
@media (prefers-reduced-motion: reduce) { .gcesp-b { animation: none !important; opacity: .7; } }
.gcesp-b { border-radius: 8px; background: #1b1e26; animation: gcesp-pulse 1.8s ease-in-out infinite; animation-delay: calc(var(--i, 0) * 90ms); }
.gcesp-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.gcesp-kpi { display: grid; gap: 10px; padding: 14px; border: 1px solid #1d2028; border-radius: 12px; }
.gcesp-table { display: grid; gap: 10px; }
.gcesp-tr { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 14px; }
@keyframes gcesp-pulse { 50% { opacity: .45; } }`;
const B = ({ w, h, i = 0 }: { w?: string | number; h: number; i?: number }) => <div className="gcesp-b" aria-hidden="true" style={{ width: w ?? "100%", height: h, "--i": i } as CSSProperties} />;
export function EsqueletoPulso({ rows = 4, className = "", style }: EsqueletoPulsoProps) {
return (
<div className={`gcesp-root ${className}`} style={style} role="status" aria-busy="true" aria-label="Cargando panel">
<style>{CSS}</style>
<B w="40%" h={22} />
<div className="gcesp-kpis">
{[0, 1, 2].map((i) => (
<div key={i} className="gcesp-kpi">
<B w="60%" h={10} i={i} />
<B w="80%" h={26} i={i + 1} />
</div>
))}
</div>
<div className="gcesp-table">
{Array.from({ length: rows }, (_, r) => (
<div key={r} className="gcesp-tr">
<B h={14} i={r} />
<B h={14} i={r + 1} />
<B h={14} i={r + 2} />
</div>
))}
</div>
</div>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/esqueleto-pulsoEjemplo de uso de Esqueleto oscuro con pulso
Después de copiar esqueleto-pulso.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { EsqueletoPulso } from "./esqueleto-pulso";
export default function Ejemplo() {
return <EsqueletoPulso />;
}Propiedades de EsqueletoPulso
EsqueletoPulso acepta 3 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
rows? | number | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Personaliza Esqueleto oscuro con pulso con variables CSS
Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.
| Variable | Valor por defecto |
|---|---|
--i | 0 |
Accesibilidad y rendimiento
El archivo tiene 50 líneas (2 KB) y define 1 animación con @keyframes. Esto es lo que hace Esqueleto oscuro con pulso 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: status.
- Es un componente de cliente de Next.js: incluye la directiva "use client".