Ir al contenido
Evaluar mi proyecto
Componentes · Datos y visualizaciónDisponible

Gráfico de barras seleccionable

Barras con escala numérica, meses y selección por teclado o toque; valores anunciados y callback del dato elegido.

Actividad por mes

Datos de muestra · proyectos

Abr56 proyectos

Código completo

Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.

datos-barras.tsx109 líneas
Descargar
"use client";

import { useState, type CSSProperties } from "react";

/** Gráfico de barras seleccionable. React + TypeScript, sin dependencias. Personaliza con variables --gczb-*. */
export type BarDatum = { label: string; value: number };
export type DatosBarrasProps = {
  title?: string;
  data?: BarDatum[];
  unit?: string;
  onSelect?: (datum: BarDatum) => void;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gczb-root { box-sizing: border-box; min-width: 0; max-width: 100%; color: var(--gczb-fg, #202421); font: 400 15px/1.5 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.gczb-root *, .gczb-root *::before, .gczb-root *::after { box-sizing: border-box; }
.gczb-root button, .gczb-root input, .gczb-root textarea, .gczb-root select { font: inherit; }
.gczb-root button, .gczb-root a { -webkit-tap-highlight-color: transparent; }
.gczb-root :focus-visible { outline: 2px solid var(--gczb-accent, #25724b); outline-offset: 4px; }
.gczb-root ::selection { background: var(--gczb-accent, #25724b); color: #fff; }
.gczb-root button:disabled { opacity: .5; cursor: not-allowed; }
.gczb-root svg { flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) { .gczb-root *, .gczb-root *::before, .gczb-root *::after { animation: none !important; transition-duration: .01ms !important; scroll-behavior: auto !important; } }
.gczb-root { width:520px; background:var(--gczb-bg,#fff); padding:26px; border-radius:12px; }
.gczb-root h3 { font-size:18px; font-weight:600; margin:0 0 6px; }
.gczb-root>p { color:#61705f; font-size:11px; margin:0 0 26px; }
.gczb-chart { display:flex; gap:14px; height:200px; }
.gczb-axis { width:30px; height:170px; display:flex; flex-direction:column; justify-content:space-between; font-size:10px; color:#61705f; font-variant-numeric:tabular-nums; }
.gczb-bars { display:flex; flex:1; gap:10px; min-width:0; border-bottom:1px solid #ced8c9; }
.gczb-bars button { flex:1; min-width:0; display:flex; flex-direction:column; gap:10px; align-items:center; padding:0 0 8px; border:0; background:transparent; color:#52664e; cursor:pointer; font-size:11px; }
.gczb-track { display:flex; align-items:flex-end; height:170px; width:100%; position:relative; }
.gczb-track i { display:block; height:100%; width:100%; background:#b0c4a7; border-radius:4px 4px 0 0; transform-origin:bottom; transition:background .2s,transform .4s cubic-bezier(.16,1,.3,1); }
.gczb-bars button[aria-pressed="true"] i { background:var(--gczb-accent,#345d3c); }
.gczb-bars button:hover i { background:#678b5b; }
.gczb-root .gczb-detail { margin:20px 0 0; display:flex; justify-content:space-between; gap:15px; padding-top:16px; font-size:12px; color:#34513b; }
.gczb-detail span { font-variant-numeric:tabular-nums; }`;

export function DatosBarras({
  title = "Actividad por mes",
  data = [
    { label: "Ene", value: 24 },
    { label: "Feb", value: 38 },
    { label: "Mar", value: 31 },
    { label: "Abr", value: 56 },
    { label: "May", value: 43 },
    { label: "Jun", value: 68 },
  ],
  unit = "proyectos",
  onSelect,
  className = "",
  style,
}: DatosBarrasProps) {
  const [selected, setSelected] = useState(3);
  const max = Math.max(1, ...data.map((d) => d.value));
  const index = Math.min(selected, Math.max(0, data.length - 1));
  const current = data[index];
  return (
    <section className={`gczb-root ${className}`} style={style}>
      <style>{CSS}</style>
      <h3>{title}</h3>
      <p>Datos de muestra · {unit}</p>
      {data.length ? (
        <>
          <div className="gczb-chart">
            <div className="gczb-axis" aria-hidden="true">
              <span>{max}</span>
              <span>{Math.round(max / 2)}</span>
              <span>0</span>
            </div>
            <div className="gczb-bars">
              {data.map((datum, i) => (
                <button
                  type="button"
                  key={i}
                  aria-label={`${datum.label}: ${datum.value} ${unit}`}
                  aria-pressed={index === i}
                  onClick={() => {
                    setSelected(i);
                    onSelect?.(datum);
                  }}
                >
                  <span className="gczb-track" aria-hidden="true">
                    <i
                      style={{
                        transform: `scaleY(${Math.max(0, datum.value) / max})`,
                      }}
                    />
                  </span>
                  <span>{datum.label}</span>
                </button>
              ))}
            </div>
          </div>
          <p className="gczb-detail" role="status">
            <strong>{current.label}</strong>
            <span>
              {current.value.toLocaleString("es-MX")} {unit}
            </span>
          </p>
        </>
      ) : (
        <p>No hay datos para mostrar.</p>
      )}
    </section>
  );
}

Úsalo en tu proyecto

Instálalo con la CLI de shadcn.

terminal.sh1 líneas
npx shadcn@latest add https://www.gassierconsulting.com/r/datos-barras

Ejemplo de uso de Gráfico de barras seleccionable

Después de copiar datos-barras.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.

ejemplo.tsx5 líneas
import { DatosBarras } from "./datos-barras";

export default function Ejemplo() {
  return <DatosBarras title="Ejemplo" />;
}

Propiedades de DatosBarras

DatosBarras acepta 6 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.

PropiedadTipoDescripción
title?stringOpcional.
data?BarDatum[]Opcional.
unit?stringOpcional.
onSelect?(datum: BarDatum) => voidOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Personaliza Gráfico de barras seleccionable 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.

VariableValor por defecto
--gczb-fg#202421
--gczb-accent#25724b
--gczb-bg#fff

Accesibilidad y rendimiento

El archivo tiene 109 líneas (5 KB) y no necesita @keyframes. Esto es lo que hace Gráfico de barras seleccionable por quien lo usa:

  • Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
  • Muestra un anillo de foco visible al navegar con teclado (:focus-visible).
  • Declara roles ARIA para lectores de pantalla: status.
  • Es un componente de cliente de Next.js: incluye la directiva "use client".
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React 18 y 19 · Next.js (componente de cliente)