1 estrella en GitHub = 1 componente nuevo.

Dar una estrella
Gráficos y Estadísticaschartsvgzero-dependencykpistatmetricdashboardsparkline

KPI Stat Tile

Tarjeta de estadística con número destacado, variación con icono y texto, periodo y sparkline.

Vista Previa en Vivo

Monthly recurring revenue

+12.4%

$84,210

vs last month

Uso

Copia y pega este código directamente en tu componente React:

tsx
import { KpiStatTile } from 'react-component-kit-ui/charts';

<KpiStatTile label="MRR" value="$84,210" change={12.4} period="vs last month" trend={[52, 60, 63, 72, 84]} />
<KpiStatTile label="Churn" value="2.3%" change={-0.6} invert variant="solid" size="sm" />

Instalación Manual

Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/KpiStatTile.tsx + KpiStatTile.css:

KpiStatTile.tsx
'use client';

import SparklineChart from './SparklineChart';
import './KpiStatTile.css';

/**
 * KpiStatTile — Hero-number statistic tile: label, big value, change vs previous
 * period (icon + text, never color alone) and an optional sparkline.
 *
 * @param {string} label - Metric name.
 * @param {string} value - Formatted value.
 * @param {number} change - Percentage change vs comparison period.
 * @param {string} period - Comparison period label (e.g. 'vs last month').
 * @param {boolean} invert - Treat a decrease as good (e.g. churn, latency).
 * @param {number[]} trend - Optional sparkline data.
 * @param {string} variant - Visual style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 * @param {string} className - Extra CSS class.
 */
export interface KpiStatTileProps {
  label?: string;
  value?: string;
  change?: number;
  period?: string;
  invert?: boolean;
  trend?: number[];
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

export function KpiStatTile({
  label = 'Monthly recurring revenue',
  value = '$84,210',
  change = 12.4,
  period = 'vs last month',
  invert = false,
  trend = [52, 55, 54, 60, 63, 61, 68, 72, 70, 76, 80, 84],
  variant = 'glass',
  size = 'md',
  className = '',
}: KpiStatTileProps) {
  const up = change >= 0;
  const good = invert ? !up : up;
  return (
    <>
      <article className={`kpi kpi--${variant} kpi--${size} ${className}`} aria-label={`${label}: ${value}, ${up ? 'up' : 'down'} ${Math.abs(change)}% ${period}`} data-rk-727d83="">
        <header className="kpi__head" data-rk-727d83="">
          <h3 className="kpi__label" data-rk-727d83="">{label}</h3>
          <span className={`kpi__chip ${good ? 'kpi__chip--good' : 'kpi__chip--bad'}`} data-rk-727d83="">
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" data-rk-727d83="">
              {up ? <path d="M7 17 17 7M9 7h8v8" data-rk-727d83="" /> : <path d="M7 7l10 10M17 9v8H9" data-rk-727d83="" />}
            </svg>
            {up ? '+' : '−'}{Math.abs(change)}%
          </span>
        </header>
        <p className="kpi__value" data-rk-727d83="">{value}</p>
        <div className="kpi__foot" data-rk-727d83="">
          <span className="kpi__period" data-rk-727d83="">{period}</span>
          {trend.length > 1 && (
            <SparklineChart data={trend} size={size === 'lg' ? 'lg' : 'md'} color={good ? 'var(--chart-pos, #34c38f)' : 'var(--chart-neg, #f06a6a)'} label={`${label} trend`} />
          )}
        </div>
      </article>
    </>
  );
}

export default KpiStatTile;
KpiStatTile.css
/* KpiStatTile — ported from astro-component-kit (charts/KpiStatTile.astro) */

.kpi:where([data-rk-727d83]) {
  --kpi-pad: var(--sp-6, 1.5rem);
  --kpi-val: 2rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: var(--kpi-pad);
  border-radius: var(--r-lg, 20px);
  min-width: 0;
  transition: border-color 0.25s ease, transform 0.25s ease;
}
.kpi--glass:where([data-rk-727d83]) {
  background: linear-gradient(160deg, rgb(var(--ink-rgb, 255 255 255) / 0.05), rgb(var(--ink-rgb, 255 255 255) / 0.015));
  border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.07));
  backdrop-filter: blur(14px);
}
.kpi--solid:where([data-rk-727d83]) { background: var(--chart-surface, #0d0d0f); border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); }
.kpi--ghost:where([data-rk-727d83]) { background: transparent; border: 1px solid transparent; padding-inline: 0; }
.kpi--glass:hover:where([data-rk-727d83]), .kpi--solid:hover:where([data-rk-727d83]) { border-color: rgb(var(--ink-rgb, 255 255 255) / 0.16); transform: translateY(-2px); }
.kpi--sm:where([data-rk-727d83]) { --kpi-pad: var(--sp-4, 1rem); --kpi-val: 1.5rem; }
.kpi--lg:where([data-rk-727d83]) { --kpi-pad: var(--sp-8, 2rem); --kpi-val: 2.5rem; }
.kpi__head:where([data-rk-727d83]) { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; }
.kpi__label:where([data-rk-727d83]) { margin: 0; font-size: 0.8125rem; font-weight: 500; color: var(--c-text-2, #a3a3a3); letter-spacing: 0; }
.kpi__chip:where([data-rk-727d83]) {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.2rem 0.5rem;
  border-radius: var(--r-full, 999px);
  font-size: 0.75rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--c-text-1, #fff);
}
.kpi__chip--good:where([data-rk-727d83]) { background: color-mix(in srgb, var(--chart-pos, #34c38f) 18%, transparent); }
.kpi__chip--good:where([data-rk-727d83]) svg:where([data-rk-727d83]) { color: var(--chart-pos, #34c38f); }
.kpi__chip--bad:where([data-rk-727d83]) { background: color-mix(in srgb, var(--chart-neg, #f06a6a) 18%, transparent); }
.kpi__chip--bad:where([data-rk-727d83]) svg:where([data-rk-727d83]) { color: var(--chart-neg, #f06a6a); }
.kpi__value:where([data-rk-727d83]) {
  margin: 0;
  font-size: var(--kpi-val);
  font-weight: 750;
  color: var(--c-text-1, #fff);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.kpi__foot:where([data-rk-727d83]) { display: flex; justify-content: space-between; align-items: flex-end; gap: 1rem; }
.kpi__period:where([data-rk-727d83]) { font-size: 0.75rem; color: var(--c-text-3, #8a8a8a); }
@media (prefers-reduced-motion: reduce) { .kpi:where([data-rk-727d83]) { transition: none; } }

Información Rápida

Categoría
Gráficos y Estadísticas
Nombre de archivo
KpiStatTile.tsx
Dependencias
Ninguna — React + CSS puro
Etiquetas
chartsvgzero-dependencykpistatmetricdashboardsparkline