sebs7n-ui0.1.0

Instalación

Una dependencia, un `@import` y tres variables de marca.

Una dependencia, un @import y tres variables de marca. No hay tailwind.config.js ni archivos copiados al repo de la app.

Instalar

pnpm add sebs7n-ui @base-ui/react next-themes sonner geist
npm install sebs7n-ui @base-ui/react next-themes sonner geist

Todavía no está en npm. Hasta que se publique, el paquete se distribuye como tarball: npm pack en el repo y pnpm add ./sebs7n-ui-0.1.0.tgz, o pnpm add github:sebafermanelli/sebs7n-ui#v0.1.0. Todo lo de abajo vale igual en los dos casos.

Las peerDependencies las instala la app, para que haya una sola copia de React y de Base UI:

Peer Rango
react · react-dom ^19.2.0
@base-ui/react ^1.8.0
next-themes ^0.4.6
sonner ^2.0.7

Compatibilidad

Versión
React 19.2+ (Server Components y "use client")
Next.js 15 o 16, App Router (Turbopack o webpack)
Tailwind CSS v4 — tokens por @theme, sin tailwind.config.js
Base UI @base-ui/react 1.8+
TypeScript moduleResolution: "bundler" (o node16 / nodenext)
Módulos Solo ESM

Los componentes no dependen de Next: funcionan en cualquier bundler que entienda exports y la directiva "use client". Lo único específico de Next en los ejemplos es next/link y next/navigation.

1. CSS

En globals.css, en este orden:

@import "tailwindcss";
@import "sebs7n-ui/theme.css";
/* Las clases de los componentes las genera el Tailwind de la app, en una sola
   hoja ordenada. La ruta es relativa a ESTE archivo:
   app/globals.css → "../node_modules/…"; src/app/globals.css → "../../node_modules/…". */
@source "../node_modules/sebs7n-ui/dist";

:root {
  --brand-base: oklch(0.573 0.214 258);
  --brand-base-dark: oklch(0.573 0.214 258);
  --brand-contrast-dark: #fff;
}

Existe también @import "sebs7n-ui/styles.css" (la hoja precompilada), pero con dos hojas de utilidades un hidden lg:block de la app pierde contra el hidden del paquete. No combines @source con @import "sebs7n-ui/styles.css": es una cosa o la otra, y la recomendada es @source.

2. Layout raíz

GeistSans.variable y GeistMono.variable en <html>, el ThemeProvider de next-themes con attribute="class", y TooltipProvider + <Toaster /> de sebs7n-ui.

import { GeistMono } from "geist/font/mono"
import { GeistSans } from "geist/font/sans"
import { Toaster, TooltipProvider } from "sebs7n-ui"
import { ThemeProvider } from "next-themes"
import "./globals.css"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="es" className={`${GeistSans.variable} ${GeistMono.variable}`} suppressHydrationWarning>
      <body>
        <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
          <TooltipProvider>
            {children}
            <Toaster />
          </TooltipProvider>
        </ThemeProvider>
      </body>
    </html>
  )
}

suppressHydrationWarning en <html> no es opcional: next-themes escribe la clase dark antes de hidratar y sin eso React avisa en cada carga.

Geist tiene que ser la fuente variable (rango 100 900): la corrección óptica de los títulos usa pesos intermedios (450, 500, 550) que con una estática se redondean.

3. Usar

import { Button } from "sebs7n-ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "sebs7n-ui/card"

export function Panel() {
  return (
    <Card>
      <CardHeader><CardTitle>Facturas</CardTitle></CardHeader>
      <CardContent>
        <Button variant="accent">Nueva factura</Button>
      </CardContent>
    </Card>
  )
}

Imports por componente

Cada módulo tiene su entry point. En páginas de marketing o landing, importá por subpath; el barrel queda para dashboards, que igual usan casi todo.

import { Button } from "sebs7n-ui/button"
import { Card, CardContent } from "sebs7n-ui/card"
import { ThemeSwitcher } from "sebs7n-ui/theme-switcher"
import { buttonVariants } from "sebs7n-ui/variants/button" // sin "use client"
import { cn } from "sebs7n-ui/lib/utils"
Subpath Archivo
sebs7n-ui/<componente> src/components/<componente>.tsx (kebab-case: alert-dialog, app-shell, user-menu, …)
sebs7n-ui/variants/<nombre> button, badge, card, link, menu, sidebar, input, toggle
sebs7n-ui/lib/<nombre> utils

Por qué: el barrel hace export * de ~30 módulos "use client". Next no puede podar referencias cliente a través de ese barrel (tampoco con optimizePackageImports), así que una página con Button + Card + ThemeSwitcher se lleva también Sonner, Sidebar, Select, AlertDialog y el resto. Medido en Next 16.3 (Turbopack) con esa página: 297,5 KB → 234,8 KB de JS cliente gzip (−21 %).

No mezcles barrel y subpaths en la misma página: el barrel vuelve a traer todo.

Verificar que quedó bien

  1. bg-blue-500 no tiene que compilar: la paleta de Tailwind está reseteada y solo existen los tokens del paquete.
  2. En oscuro, un Input tiene que verse más claro que el fondo de la página (#0a0a0a sobre #000). Si son el mismo negro, falta la 2.0 o hay un parche viejo pisando --sf-background-100.
  3. Tabulá: la primera parada de un AppShell es «Ir al contenido».