@oopart/react-components
v0.5.2
Published
Librería de componentes React — Atomic Design, Tailwind v4, Radix/ShadCN re-estilado con los tokens de marca del consumidor.
Maintainers
Readme
@oopart/react-components
Librería de componentes React
React 19 · TypeScript · Atomic Design · Tailwind CSS v4 · Radix/ShadCN · Storybook 9
@oopart/react-components es una librería de componentes agnóstica de marca
lista para consumir en cualquier app React. Combina primitivas accesibles de
Radix UI (estilo ShadCN) re-estiladas con los los tokens de marca del consumidor: color
institucional #082129, acento lime #52a400, radios de 8px y sombras de marca. Incluye tema claro y oscuro vía CSS
variables.
Desde la v0.2.0 la tipografía y la iconografía las decide la aplicación, de modo que la misma librería puede servir a otra marca: ver PERSONALIZACION.md.
Tabla de contenidos
- Requisitos
- Instalación
- Uso
- Temas (claro / oscuro)
- Arquitectura — Atomic Design
- Catálogo de componentes
- Tokens de marca
- Scripts
- Storybook
- Agregar más componentes ShadCN
- Build y publicación
- Licencia
- Personalizar para otra marca
- Suite de pruebas
Changelog
v0.3.0
MarcaProvider. La identidad se declara una vez en la raíz y todos los logos del árbol la resuelven solos. Antes había que pasarbranda cada componente que mostrara el logo, lo que en un proyecto de cien pantallas son cien oportunidades de olvidarlo.brandylabelsiguen existiendo como sobrescritura puntual. Vive ensrc/sistema/, que es la carpeta nueva para infraestructura transversal: no es un componente visual y no encaja en átomos, moléculas ni organismos.- Paleta por defecto neutra. Gris azulado con un azul de acción. No es la identidad de nadie: es una base para que la librería se vea intencional recién instalada. Todos los contrastes verificados contra WCAG 2.2 AA.
- Sin gradientes de marca por defecto. Un degradado es una decisión de identidad, no de sistema; los dos tokens quedan en color plano.
- Node 22 obligatorio (
enginesy.nvmrc).jsdom30 llama awebidl.util.markAsUncloneable, que no existe antes de Node 22: con Node 20 las pruebas no arrancan. Con esto, las 44 pruebas corren. .yarnrc.ymlsale de.gitignore. Estaba ignorado, así que el arreglo delnodeLinkerno viajaba y cada clon nacía con el toolchain roto.
La librería deja de ser de una institución. La v0.2.0 desacopló la tipografía y la iconografía; faltaba lo demás: el nombre del paquete, los logos, el prefijo de los tokens y medio centenar de hexadecimales escritos a mano dentro de los componentes.
Paquete renombrado a
@oopart/react-components, publicado en el registro público. El bundle pasa adist/ui.js,dist/ui.cjsydist/ui.css. Cambio que rompe: hay que actualizar el import y el nombre del paquete.Los logos salen del paquete.
logo.tsximportaba cinco SVG de una institución concreta, así que todo consumidor los descargaba aunque nunca los mostrara. AhoraLogoexigebrandylabel, yMascotexigeartylabel. Los SVG quedan enejemplos/marca-ejemplo/, fuera desrc/. Cambio que rompe:<Logo variant="nav" />ya no compila.Navbar,Footer,LoginFormySidebarrecibenbrandybrandLabely los pasan aLogo. Si no se entregan, no dibujan logo.Tokens renombrados de
--marca-*a--brand-*, y las utilidades de Tailwind debg-marca-*abg-brand-*. Cambio que rompe para quien los usara directamente.51 hexadecimales sacados de los componentes. Estaban en 15 archivos, y entre ellos la paleta institucional completa:
#082129siete veces, más la escala de grises y el acento. Una marca distinta redefinía los tokens y seguía viendo los colores originales. Ahora no queda ninguno.Tintes de estado en el contrato de tokens:
--info-soft,--info-border,--info-strongy sus equivalentes para éxito, advertencia y peligro. Antes vivían como literales dentro deAlert,BadgeyModal.Corregido: en tema oscuro esos tintes no existían.
AlertyBadgepintaban el fondo claro del tema claro sobre el lienzo oscuro.Las sombras del bloque
@themereferencian la capa cruda en vez de repetir el literal. Antes tenían el color de acento incrustado dentro delrgba(), así que redefinir la marca no cambiaba la sombra.Navbartenía el azul oscuro escrito a mano como valor arbitrario de Tailwind, contra la regla de la propia librería. Corregido.Al documentar esto se descubrió otra fuga: Tailwind v4 detecta contenido por su cuenta y también escanea el README, así que escribir la clase literal en la documentación la regeneraba en
dist/ui.css. Cada consumidor se llevaba una regla muerta para un color que ya no existe en el sistema. Por eso aquí se describe en palabras.Añadido
.yarnrc.ymlconnodeLinker: node-modules. Yarn 3 usa Plug'n'Play por defecto y el proyecto no es compatible:yarn lintyyarn buildfallaban en cualquier commit, y lostypesdel tsconfig no resolvían.
Resuelto en v0.4.0: los valores por defecto de la paleta pasaron a ser neutros, que es lo coherente con una librería agnóstica de marca.
v0.2.0
La marca deja de estar incrustada en el sistema. La librería ya no impone tipografía ni familia de iconos, así que puede servir a otra identidad visual sin tocar componentes — ver PERSONALIZACION.md.
- Tipografía desacoplada.
globals.cssya no importa Google Fonts. Los tokens--font-sansy--font-displayleen--marca-font-sansy--marca-font-display, con caída a la tipografía del sistema. Cambio que rompe: una app que dependía de que la librería cargara Saira y Plus Jakarta Sans debe cargarlas ella. - Iconografía centralizada en
src/icons.ts. Ningún componente importa ya delucide-reactdirectamente. - Escala tipográfica en tokens (
--text-xs…--text-3xl). Logoacepta una marca propia por prop, junto con su nombre accesible.
Accesibilidad
Modalreescrito sobre Radix Dialog. Ahora atrapa el foco, lo restaura al cerrar y genera identificadores únicos para el título. Antes se podía tabular al contenido de detrás, el foco se perdía al<body>, y dos diálogos simultáneos producíanidduplicados. Nueva propinitialFocusRef.DataTableordena por columna, conaria-sorty tres estados (ascendente, descendente, sin orden).
Componentes nuevos
Progress · EmptyState · DescriptionField · Timeline · CommandPalette ·
FileImport · CatalogManager · SheetPanel · ConfirmDialog · InlineEdit
v0.1.6
"use client" en el entry point — compatibilidad con Next.js App Router
- Agrega la directiva
"use client"al inicio desrc/index.tsy comobanneren el build de Rollup. - Los bundles
dist/ui.js(ESM) ydist/ui.cjs(CJS) ahora declaran explícitamente la barrera cliente. - Los proyectos consumidores en Next.js App Router ya no necesitan agregar
"use client"en sus propios wrappers de componentes de esta librería.
v0.1.4
Sidebar — nuevo prop footer
- Nuevo prop
footer?: ReactNode— permite inyectar cualquier componente en la parte inferior del sidebar. Si no se pasa, el área queda vacía. - Eliminado el bloque estático "¿Necesitas ayuda?" (ahora es responsabilidad del consumidor vía
footer).
<Sidebar
footer={
<div className="rounded-lg bg-sidebar-accent/50 p-3">
<p className="text-sm font-semibold text-white">¿Necesitas ayuda?</p>
</div>
}
/>v0.1.3
Button — nuevo prop loading?: boolean
- Cuando
loading={true}muestra unSpinnera la izquierda del texto, aplicadisabledyaria-busyautomáticamente. - Al volver a
falserecupera el estado normal sin cambios de layout.
<Button loading={guardando}>Guardar</Button>TablePagination — nuevo componente controlado para tablas
- Props:
page,totalPages,onPageChange,variant. variant="table"(defecto): diseño compacto con botones bordeados, "Anterior" en gris y "Próximo" en lime.variant="default": estilo clásico con iconos ChevronLeft / ChevronRight y ghost buttons.
<TablePagination page={page} totalPages={50} variant="table" onPageChange={setPage} />Sidebar — correcciones visuales
- Los íconos de los ítems activos ahora se muestran en blanco (
text-white) en lugar de desaparecer contra el fondo de acento.
Requisitos
- Node.js ≥ 22. No es capricho:
jsdom30, que usa la suite de pruebas, llama awebidl.util.markAsUncloneable, que no existe antes de Node 22. Con Node 20 las pruebas ni siquiera arrancan. El repo trae.nvmrc. - React 19 y React DOM 19 (declarados como
peerDependencies) - Corepack habilitado, para que Yarn 3 se resuelva solo
- Una app con bundler moderno (Vite, Next, etc.)
Puesta en marcha tras clonar
git clone https://github.com/AndresEspinozaBringas/react-components.git
cd react-components
nvm use # lee .nvmrc → Node 22
corepack enable # una sola vez por máquina
yarn install
yarn test # 34 pruebas
yarn lint
yarn build
yarn dev # Storybook en localhost:6006Si nvm use dice que no tiene la versión, nvm install 22 primero.
Si algo falla
webidl.util.markAsUncloneable is not a function al correr las pruebas.
Estás en Node 20. nvm use y repite.
request for './fallback/encoding.js' is not in cache, o ESLint tira
Cannot convert undefined or null to object, o tsc no encuentra los tipos de
vitest/globals. Son tres síntomas del mismo problema: Yarn 3 usa Plug'n'Play
por defecto y este proyecto no es compatible. El repo trae .yarnrc.yml con
nodeLinker: node-modules, que lo resuelve. Si el archivo no está, créalo:
nodeLinker: node-modulesEse archivo estuvo en .gitignore hasta la v0.3.0, así que cada clon nacía con
el toolchain roto y los tres comandos fallaban en cualquier commit. Ya se
versiona. No pongas credenciales ahí: los tokens de registro van en
~/.yarnrc.yml o en la variable YARN_NPM_AUTH_TOKEN.
Instalación en una aplicación
yarn add @oopart/react-components react react-domMientras no se publique a un registro, puedes consumirla con
yarn pack/yarn link, o como dependencia de workspace en un monorepo.
Uso
Importa una sola vez la hoja de estilos (trae tokens, tema y utilidades de Tailwind) y luego usa los componentes:
import "@oopart/react-components/styles.css";
import { Button, Card, CardHeader, CardTitle, LeadForm } from "@oopart/react-components";
export function App() {
return (
<div className="p-8">
<Button>Postula ahora</Button>
<Button variant="outline">Solicita información</Button>
</div>
);
}Todos los componentes se exportan desde la raíz del paquete. Los tipos TypeScript viajan con cada export.
La tipografía la carga la aplicación
styles.css trae los tokens, no las fuentes. Añada las suyas al CSS global —o
con next/font, que es lo recomendable— definiendo estos dos tokens:
@import "tailwindcss";
@import "@oopart/react-components/styles.css";
/* La identidad de su institución */
@import url("https://fonts.googleapis.com/css2?family=Saira:wght@600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap");
:root {
--marca-font-sans: "Plus Jakarta Sans", system-ui, sans-serif;
--marca-font-display: "Saira", system-ui, sans-serif;
}Sin esos tokens la interfaz usa la tipografía del sistema y sigue siendo legible: no se rompe, se ve distinta.
Temas (claro / oscuro)
El tema se controla con la clase dark en un ancestro (convención ShadCN):
// Claro (por defecto)
<html>
// Oscuro
<html class="dark">- Claro: lienzo blanco, texto
#212529, acento lime. - Oscuro: lienzo superficie oscura de marca
#082129, texto claro, acento lime.
El sidebar del dashboard usa la superficie superficie oscura de marca incluso en tema claro, tal como define el design system.
Arquitectura — Atomic Design
src/
├── styles/globals.css ← tokens de marca + tema + @theme de Tailwind v4
├── lib/utils.ts ← cn() (clsx + tailwind-merge)
└── components/
├── atoms/ ← bloques mínimos
├── molecules/ ← composiciones simples
└── organisms/ ← composiciones de UI completasCada nivel tiene un index.ts barrel; src/index.ts re-exporta todo.
Catálogo de componentes
Átomos (17)
Avatar · Badge · Button · Checkbox · Heading · Icon (wrapper de la
familia de iconos) · Input · Label · Logo · Progress · RadioGroup ·
Select · Spinner · Switch · Text · Textarea · Tooltip
Moléculas (21)
Accordion · Alert · BadgeGroup · Breadcrumb · Card ·
ConfirmDialog · DatePicker · DescriptionField / DescriptionList ·
DropdownMenu · EmptyState · FileUpload · FormField ·
InlineEdit · Modal · Pagination · PdfViewer · SearchBar ·
SheetPanel · StatCard · Tabs · Toaster
Organismos (14)
ActivityFeed · CatalogManager · CommandPalette · DataTable ·
FileImport · Footer · Hero · LeadForm · LoginForm · Navbar ·
ProgramCard · Sidebar · Timeline · TopHeader
En negrita, los añadidos en v0.2.0.
Cuál usar cuando dudes
| Necesita | Componente |
|---|---|
| Una decisión que interrumpe | Modal o ConfirmDialog |
| Trabajo auxiliar sin perder de vista la lista | SheetPanel |
| Un catálogo con alta, edición y vigencia | CatalogManager |
| Cargar datos desde un archivo | FileImport |
| Un dato de sólo consulta, con su procedencia | DescriptionField |
| El historial de un registro | Timeline |
| Que no haya nada que mostrar | EmptyState |
| Editar un valor corto donde se lee | InlineEdit |
| Ir a una pantalla escribiendo su nombre | CommandPalette |
Cada componente tiene su archivo .stories.tsx con ejemplos en español de Chile.
Tokens de marca
Definidos en src/styles/globals.css como CSS variables y expuestos como
utilidades Tailwind:
| Token semántico | Claro | Uso |
|---|---|---|
| --primary | #52a400 (lime) | acción principal, CTAs |
| --secondary | #082129 (dark) | botón secundario, superficies dark |
| --accent | #cbf8ab | fondos suaves, chips, hover |
| --background / --foreground | #ffffff / #212529 | lienzo / texto |
| --destructive | #e53e3e | errores, acciones peligrosas |
| --ring | #52a400 | foco |
También hay tokens crudos de marca (--brand-primary, --brand-accent, etc.) y
sombras (--shadow-card, --shadow-button…). No inventes colores
fuera de estos tokens.
Pruebas
yarn test # 34 pruebas de comportamiento y accesibilidad
yarn verify # tipos + lint + testsSe prueba el comportamiento, no el aspecto: los defectos de esta librería han sido siempre de foco, teclado y orden, no de píxeles. Qué merece un test y qué no, en PRUEBAS.md.
Scripts
| Script | Acción |
|---|---|
| yarn dev | Levanta Storybook en :6006 (entorno de desarrollo) |
| yarn storybook | Igual que dev |
| yarn build-storybook | Genera el Storybook estático |
| yarn build | Type-check + build de la librería (Vite library mode) |
| yarn typecheck | tsc --noEmit |
| yarn lint | ESLint sobre src |
Storybook
yarn devAbre http://localhost:6006. Encontrarás:
- Introducción / Bienvenida — guía rápida.
- Átomos / Moléculas / Organismos — un grupo por nivel.
- Switcher Claro / Oscuro en la barra de herramientas.
- Addon a11y para revisar accesibilidad.
Agregar más componentes ShadCN
El proyecto ya trae components.json configurado (Tailwind v4, alias @/…,
iconos Lucide). Para sumar un componente nuevo de ShadCN:
npx shadcn@latest add dialogQuedará en src/components/atoms/ (alias ui). Re-estilízalo con los tokens
del sistema (usa bg-primary, text-foreground, rounded-md, etc.) y expórtalo
desde el barrel correspondiente.
Build y publicación
yarn buildGenera en dist/:
ui.js(ESM) yui.cjs(CommonJS)index.d.ts(tipos)ui.css(estilos — se importa como@oopart/react-components/styles.css)
react y react-dom quedan como externos (no se empaquetan).
Para publicar desde local:
yarn npm publishRequiere estar autenticado en
https://registry.npmjs.org/. Asegúrate de haber ejecutadoyarn buildantes de publicar.
Licencia
Software propietario. No es libre ni de código abierto.
Copyright © 2026 Andrés Espinoza Bringas. Todos los derechos reservados.
| | | |---|---| | Permitido, sin coste ni plazo | Instalar, ejecutar y evaluar la librería. Integrarla en maquetas, prototipos y demostraciones —incluidas las de un proceso de licitación—. Modificarla para esos fines | | Requiere acuerdo escrito | Usarla en producción: cualquier sistema que preste servicio a usuarios finales, esté disponible al público o se entregue a un tercero | | No permitido | Redistribuirla, publicarla en otro repositorio, sublicenciarla o distribuir obras derivadas |
Los términos completos están en LICENSE. Para una licencia de producción, escribir a través del repositorio del proyecto.
Componentes de terceros
La librería se distribuye compilada en un solo archivo, y ese archivo lleva dentro sus dependencias. Todas ellas usan licencias permisivas —MIT, ISC, Apache-2.0, BlueOak-1.0.0 y 0BSD—, ninguna es copyleft, y sus avisos de copyright están recogidos uno a uno en THIRD-PARTY.md, que viaja dentro del paquete publicado.
Ese archivo se regenera con npm run terceros y se comprueba solo antes de
cada publicación: una dependencia nueva sin aviso detiene el npm publish.
