npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@juanarenas31/metrik-ui

v0.6.0

Published

Librería de componentes React para dashboards analíticos · Unidad de Ciencia de los Datos · Universidad Simón Bolívar.

Readme

@juanarenas31/metrik-ui

Librería de componentes React para construir dashboards analíticos de la Unidad de Ciencia de los Datos · Universidad Simón Bolívar.

npm license types

Inspirada en shadcn/ui · Radix · Mantine. Construida sobre React 18+ · TypeScript · TailwindCSS · Radix Primitives · CVA.


✨ Características

  • 22+ componentes accesibles (ARIA · keyboard · focus visible)
  • Tokens semánticos para light + dark mode con un sólo .dark en <html>
  • Tree-shakeable · cada componente se importa de su sub-path
  • forwardRef + asChild en todo lo interactivo
  • Sistema de variantes tipadas con class-variance-authority
  • TypeScript 5 estricto · types incluidos
  • Preset de Tailwind plug-and-play · mapea tokens a clases utility
  • Hooks reutilizables · useTheme, useDisclosure, useMediaQuery, useDebounce, useLocalStorage

📦 Instalación

# pnpm
pnpm add @juanarenas31/metrik-ui

# npm
npm install @juanarenas31/metrik-ui

# yarn
yarn add @juanarenas31/metrik-ui

peerDependencies obligatorias:

pnpm add react react-dom tailwindcss

peerDependencies opcionales — solo si usas los módulos que las requieren (metrik-ui no las instala por ti, así no engordan tu bundle si no las usas):

pnpm add recharts                 # solo para @juanarenas31/metrik-ui/charts
pnpm add @tanstack/react-table    # solo para DataTable (@juanarenas31/metrik-ui/table)
pnpm add react-day-picker         # solo para Calendar/DateRangePill (@juanarenas31/metrik-ui/calendar)

Si importas un componente de un módulo pesado sin tener su peer instalada, tu gestor de paquetes te avisará. Los componentes del core no requieren ninguna.


🚀 Setup en 3 pasos

1 · Importa los estilos base

En tu archivo de estilos global (globals.css o index.css):

@import "@juanarenas31/metrik-ui/styles.css";

@tailwind base;
@tailwind components;
@tailwind utilities;

Solo necesitas el preset si quieres usar las clases utility (bg-primary, text-fg-muted, etc.). Si prefieres usar las CSS variables directamente, importa únicamente tokens.css.

2 · Agrega el preset de Tailwind

En tailwind.config.ts:

import metrikPreset from "@juanarenas31/metrik-ui/tailwind-preset";
import type { Config } from "tailwindcss";

export default {
  presets: [metrikPreset],
  content: [
    "./src/**/*.{ts,tsx}",
    "./node_modules/@juanarenas31/metrik-ui/dist/**/*.js",
  ],
} satisfies Config;

3 · Empieza a usar componentes

import { Button, Card, CardHeader, CardTitle, CardContent, Badge } from "@juanarenas31/metrik-ui";
import { TrendingUp } from "lucide-react";

export function KPI() {
  return (
    <Card className="max-w-sm">
      <CardHeader>
        <div className="flex items-center justify-between">
          <CardTitle>Estudiantes activos</CardTitle>
          <Badge tone="primary" dot>+4.2%</Badge>
        </div>
      </CardHeader>
      <CardContent>
        <div className="text-3xl font-semibold tabular-nums">12 488</div>
        <Button variant="outline" size="sm" className="mt-4" leftIcon={<TrendingUp />}>
          Ver tendencia
        </Button>
      </CardContent>
    </Card>
  );
}

Selector de fecha con dropdowns de año/mes

import { FloatingDatePicker } from "@juanarenas31/metrik-ui";
import { useState } from "react";

export function FechaNacimiento() {
  const [date, setDate] = useState<Date | undefined>();
  return (
    <FloatingDatePicker
      label="Fecha de nacimiento"
      value={date}
      onValueChange={setDate}
      // azúcar: limita el rango del dropdown de años
      fromYear={1950}
      toYear={new Date().getFullYear()}
      // captionLayout="dropdown"  ← default; "label" vuelve al modo flechas
    />
  );
}

Por defecto el calendario abre con dropdowns de mes y año, así puedes saltar varios años de una. Pasa captionLayout="label" si prefieres el modo clásico con flechas.


🌗 Dark mode

import { useTheme, Switch } from "@juanarenas31/metrik-ui";

export function ThemeToggle() {
  const { resolvedTheme, toggleTheme } = useTheme();
  return (
    <Switch
      checked={resolvedTheme === "dark"}
      onCheckedChange={toggleTheme}
      aria-label="Cambiar tema"
    />
  );
}

Para evitar el "flash of unstyled content" en SSR, inyecta este script en tu <head>:

<script>
  (function () {
    var t = localStorage.getItem("metrik-theme") || "system";
    var d = t === "dark" || (t === "system" && matchMedia("(prefers-color-scheme: dark)").matches);
    document.documentElement.classList.toggle("dark", d);
    document.documentElement.classList.toggle("light", !d);
  })();
</script>

📚 Componentes

| Componente | Sub-path | Descripción | |-------------------|---------------------------------------|----------------------------------------------| | Alert | @juanarenas31/metrik-ui | Avisos · info · success · warning · danger | | Avatar | @juanarenas31/metrik-ui | Imagen de usuario con fallback | | Badge | @juanarenas31/metrik-ui | Etiquetas pequeñas · 7 tonos · 3 tamaños | | Button | @juanarenas31/metrik-ui | 6 variantes · 4 tamaños · loading · iconos | | Card | @juanarenas31/metrik-ui | Compound · Header/Title/Description/Content/Footer | | Checkbox | @juanarenas31/metrik-ui | Checkbox accesible · indeterminate | | Dialog | @juanarenas31/metrik-ui | Modal · focus trap · scroll-lock · portal | | DropdownMenu | @juanarenas31/metrik-ui | Menú con checkbox · radio · sub-menús | | Input | @juanarenas31/metrik-ui | Input texto · 3 tamaños · estados error/success | | Label | @juanarenas31/metrik-ui | Label semántico · required | | Popover | @juanarenas31/metrik-ui | Popover con anchor opcional | | Select | @juanarenas31/metrik-ui | Select accesible · grupos · keyboard nav | | Separator | @juanarenas31/metrik-ui | Línea divisoria · horizontal/vertical | | Skeleton | @juanarenas31/metrik-ui | Loading placeholder con shimmer | | Spinner | @juanarenas31/metrik-ui | Indicador de carga · 4 tamaños | | Switch | @juanarenas31/metrik-ui | Toggle on/off | | Tabs | @juanarenas31/metrik-ui | Tabs · underline o pill | | Textarea | @juanarenas31/metrik-ui | Textarea con estado invalid | | Tooltip | @juanarenas31/metrik-ui | Tooltip · provider + content |

Hooks

| Hook | Descripción | |---------------------|---------------------------------------------------------------| | useTheme | Light · dark · system con persistencia en localStorage | | useDisclosure | Estado open/close para overlays | | useMediaQuery | Suscríbete a media queries · incluye useBreakpoint.lg() | | useDebounce | Valor debounced · útil para búsquedas | | useLocalStorage | Estado sincronizado con localStorage | | useClickOutside | Cierra paneles cuando el usuario hace click afuera | | useToggle | Helper para booleanos |

Utilidades

| Export | Descripción | |--------------|---------------------------------------------------------------| | cn(...) | Combina clases de Tailwind resolviendo conflictos (twMerge) | | cva | Re-export de class-variance-authority |


🎨 Tokens

Los tokens se exponen como CSS variables. Puedes usarlos:

  • vía clases utility de Tailwind (bg-primary, text-fg-muted, border-border…)
  • vía variables CSS directas (var(--metrik-primary), var(--metrik-fg)…)

Color · jerarquía semántica

| Rol | Token | Cuándo usarlo | |---|---|---| | Primary | --metrik-primary | Acción · CTA principal · botones primarios | | Accent | --metrik-accent | Énfasis visual · highlight · realce | | Info | --metrik-info | Información · datos · enlaces · métricas neutrales | | Success | --metrik-success | Estado positivo | | Warning | --metrik-warning | Advertencia | | Danger | --metrik-danger | Error · destructivo |

Color · tokens

| Token | Light | Dark | Uso | |---|---|---|---| | --metrik-bg | slate-50 | slate-950 | Fondo de la app | | --metrik-surface | white | slate-900 | Cards, panels | | --metrik-surface-muted | slate-100 | slate-800 | Hover states | | --metrik-border | slate-200 | slate-700 | Bordes sutiles | | --metrik-fg | slate-800 | slate-50 | Texto principal | | --metrik-fg-muted | slate-500 | slate-400 | Texto secundario | | --metrik-primary | teal-500 | teal-400 | Acción · CTA | | --metrik-accent | coral-500 | coral-400 | Énfasis · realce | | --metrik-info | blue-500 | blue-400 | Información · datos · links | | --metrik-info-soft | blue-100 | blue-500/18% | Fondo suave de badge/alert info | | --metrik-info-fg | blue-700 | blue-200 | Texto sobre info-soft | | --metrik-success | teal-600 | teal-400 | Estados positivos | | --metrik-danger | coral-600 | coral-400 | Errores, destructivos | | --metrik-warning | amber-500 | amber-300 | Advertencias |

Rampas primitivas para charts

teal-{50..950} · coral-{50..950} · blue-{50..950} · acceso vía Tailwind (bg-blue-500, text-teal-700, etc.).

Paleta categorial recomendada (alta separación perceptual): teal-500 (primary) · blue-500 (info) · coral-500 (accent) · slate-400 (baseline).

Radius · shadow · motion

rounded-{sm,md,lg,xl,full} · shadow-{xs,sm,md,lg} · duration-{fast,base,slow} · ease-metrik

Z-index

z-{sticky,dropdown,overlay,modal,toast} · 10 · 100 · 1000 · 1100 · 1500


🔧 Entrypoints modulares y tree-shaking

La librería se publica con un entry principal + subpaths especializados que aíslan las dependencias pesadas en chunks separados. El core nunca arrastra gráficos, tablas avanzadas, calendarios ni command palettes.

| Import | Contenido | Dependencia que aísla | |---|---|---| | @juanarenas31/metrik-ui | core: Button, Card, Input, overlays, navegación… | — | | @juanarenas31/metrik-ui/charts | Chart* | recharts | | @juanarenas31/metrik-ui/table | Table, DataTable | @tanstack/react-table | | @juanarenas31/metrik-ui/calendar | Calendar, DateRangePill, Floating* | react-day-picker | | @juanarenas31/metrik-ui/command | Command*, Combobox | cmdk | | @juanarenas31/metrik-ui/layout | Container, Stack, Grid, Separator, ScrollArea | — | | @juanarenas31/metrik-ui/forms | Input, Select, Checkbox, Form… | — |

// Core: ligero, cero dependencias pesadas
import { Button, Card, Badge } from "@juanarenas31/metrik-ui";

// Módulos pesados: solo se cargan cuando los importas explícitamente
import { ChartContainer } from "@juanarenas31/metrik-ui/charts";
import { DataTable } from "@juanarenas31/metrik-ui/table";

Todos los componentes están marcados como side-effect-free excepto los .css, y cada dependencia pesada vive en su propio chunk. Resultado verificado: importar solo componentes básicos no incorpora recharts, @tanstack/react-table, react-day-picker ni cmdk al bundle del consumidor.

Garantías automáticas: cada PR pasa por presupuestos size-limit y un guard de aislamiento del core (pnpm size:check). Detalles en CONTRIBUTING.md y docs/ARCHITECTURE-OPTIMIZATION.md.


⬆️ Migración desde v0.5.x

0.6.0 no cambia la API pública del entry principal: todo lo que importabas de @juanarenas31/metrik-ui sigue funcionando igual. Los subpaths son aditivos.

Único cambio a tener en cuenta — 3 dependencias pasaron a peer opcionales. Si usas estos módulos, instala su peer (la mayoría de proyectos ya las tiene):

| Si usas… | Instala | |---|---| | ChartContainer / Chart* | pnpm add recharts | | DataTable | pnpm add @tanstack/react-table | | Calendar / DateRangePill / Floating* | pnpm add react-day-picker |

No hay renombrados ni cambios de props. Recomendado: migrar los imports de módulos pesados a su subpath (…/charts, …/table, …/calendar) para máxima claridad, aunque el entry principal sigue exportándolos.


🛠️ Desarrollo local

git clone <repo>
cd metrik-ui
pnpm install
pnpm dev        # tsup watch
pnpm typecheck
pnpm lint
pnpm build

📐 Convenciones

  • Naming · PascalCase para componentes, camelCase + use* para hooks
  • Compound components para piezas con múltiples partes (Card.Header, Dialog.Footer…)
  • asChild prop disponible donde tiene sentido (Button, Trigger primitives) usando Radix Slot
  • Variantes tipadas con cva, exportadas como <Component>Variants para extender

🗺️ Roadmap

  • [ ] v0.2 · DataTable · Toast · Drawer · Slider · DatePicker · FileUpload
  • [ ] v0.3 · Charts (recharts wrappers) · KPIWidget · StatsCard · ActivityFeed
  • [ ] v0.4 · Layouts pre-armados (DashboardLayout · AuthLayout · AdminLayout)
  • [ ] v1.0 · Documentación interactiva (Storybook + Astro Starlight) · CI/CD · npm público

📄 Licencia

MIT © 2026 Universidad Simón Bolívar · Unidad de Ciencia de los Datos