@jbernous/cts-ui
v0.7.0
Published
CTS-UI — Librería transversal de componentes UI reutilizables
Maintainers
Readme
CTS-UI
Libreria transversal de componentes UI reutilizables de CoTessIA.
Proposito del producto: tomar componentes de librerias y registries de
referencia (shadcn Base UI, ReUI, Base UI) y convertirlos en piezas propias,
hermosas y funcionales, listas para reutilizar en los desarrollos posteriores.
La definicion completa (que es una pieza terminada, principios e invariantes,
fuentes de referencia y barra estetica) vive en docs/producto.md.
Stack
- React 19
- TypeScript 6
- Vite 8
- Tailwind CSS 4
- Recharts 3
- @base-ui/react
Estructura
src/
primitives/ Componentes base (Button, Card, Input, Dialog, etc.)
charts/ Graficas (Area, Bar, Pie, Radar, Radial)
kpi/ Tarjetas KPI y utilidades
shell/ Shell de aplicacion (Sidebar, Header, AppShell)
patterns/ Patrones compuestos (KpiGrid, Pagination)
tokens/ Design tokens, utilidades (cn, status, density)
ui-lab/ Catalogo y sistema de curaduriaImágenes del demo (
src/patterns/storefront.demo-images.ts): el patrón de demo del storefront usa URLs reales de la tienda de referencia para verse con fotos en vez de los placeholders SVG. Es material de terceros y de referencia para evaluar el diseño: no es un activo del proyecto, no se redistribuye (solo se apunta al CDN de la tienda) y sus URLs están versionadas por producto (?v=…), así que pueden morir cuando cambien una campaña. El archivo se puede borrar entero: todos los call sites pasan porproductImage(fill, size, remoteUrl?), que anteundefinedvuelve al placeholder neutro, sin tocar ninguna pieza del subset. Las categorías del demo tienenidpropios, así que su mapeo es una aproximación declarada (ver la cabecera del archivo).
Desarrollo
npm install
npm run dev # Dev server del Lab en http://localhost:5273
npm run build # Library build
npm run typecheck # TypeScript checkEmpaquetado (tarball)
npm pack --dry-run --ignore-scripts --json # inspeccionar el packlistprepack corre npm run build: npm pack sobre un clon limpio no tiene
dist/ (está gitignored) y el tarball nunca debe salir sin bundles frescos. Por
eso cualquier npm pack dispara un build completo; para mirar sólo la lista de
archivos, usar --ignore-scripts como arriba.
files acota el tarball a lo que exports declara: los artefactos del dev-app
(dist/ui-lab/**, dist/App.d.ts, dist/main.d.ts y sus .map) no se
publican. El contrato está cubierto por src/packaging.test.ts, que no necesita
build ni invoca npm (se saltea si dist/ no existe).
Guardarraíl e2e (Playwright)
npm run test:e2e # Playwright contra el Lab (levanta Vite en el puerto 5273)Corre las invariantes de navegador que jsdom no puede ver (geometría real del
Frame, alternancia Plus/Minus y contraste de iconos) sobre el Lab en
http://localhost:5273 — no en 5173, que en esta máquina sirve otra app.
El config usa channel: "chrome": necesita el Chrome del sistema y NO
descarga navegadores (npx playwright install no hace falta). Si la máquina no
tiene Chrome, el fallback es npx playwright install chromium y cambiar
channel a "chromium" en playwright.config.ts.
npm run test:e2e queda fuera de npm test a propósito: la suite jsdom
(Vitest) no debe depender de un navegador.
Uso
import { Button } from "@jbernous/cts-ui"
import "@jbernous/cts-ui/styles.css"@jbernous/cts-ui/styles.css es obligatorio, una sola vez, para @jbernous/cts-ui y @jbernous/cts-ui/auth
(el CSS completo del sistema). Medido: dist/auth.js tiene 0 referencias a
.css y dist/index.css cubre el 100% de las clases que usa auth. Sin ese
import las pantallas renderizan sin estilos.
Subpath @jbernous/cts-ui/auth (pantallas de login/signup, UI pura):
import { LoginScreen } from "@jbernous/cts-ui/auth"
import "@jbernous/cts-ui/styles.css"Subset @jbernous/cts-ui/store — su propio CSS acotado
import "@jbernous/cts-ui/store/styles.css" // ANTES del CSS propio
import { StorePrice } from "@jbernous/cts-ui/store"@jbernous/cts-ui/store/styles.css (dist/store.css) trae sólo las utilidades del
subset: 15,9 kB (3,7 kB gzip) frente a 85,6 kB del completo, sin preflight y
sin los resets globales que le repintan el body al consumidor. Es plano (sin
@layer) a propósito, y la razón está medida:
- Tailwind 3 secuestra
@layer base|components|utilitiesen su plugin PostCSS y rechaza el stylesheet entero (@layer utilities is used but no matching @tailwind utilities directive is present) — un consumidor Tailwind 3 no podría ni importarlo; - y el estilo sin capa le gana al estilo con capa sin importar
especificidad, así que un reset de tipo del consumidor
(
h1,h2,… { font-size: inherit; font-weight: inherit }) le comía la tipografía al componente (medido:<h1>en 400 con capas, 700 plano).
En Tailwind 3, importalo antes de tu propio CSS. Con artefacto plano el orden decide los empates clase-vs-clase: el que manda en su documento es el consumidor, así que va primero la librería.
En Tailwind 4, metelo en una capa propia. Un CSS plano le gana a todo lo que esté en capa, sin importar orden ni especificidad — y Tailwind 4 emite todo en capas. Importado tal cual, las utilidades de la librería pisarían a las del consumidor y no habría forma de ordenarlas. La solución es una línea:
@layer theme, base, components, cts, utilities; /* orden explícito, cts antes de utilities */
@import "tailwindcss";
@import "@jbernous/cts-ui/store/styles.css" layer(cts);Así cts queda por debajo de utilities y el consumidor gana los empates. Un
solo artefacto sirve a los dos mundos: plano para Tailwind 3, en capa propia para
Tailwind 4. Verificado en smoke/ (Next 15 + Tailwind 4) con la aserción
la capa \cts` queda antes de `utilities``.
Sección @jbernous/cts-ui/store-columbia — subpath propio, con su CSS
import "@jbernous/cts-ui/store-columbia/styles.css" // ANTES del CSS propio
import { ColumbiaHeader } from "@jbernous/cts-ui/store-columbia"Son las nueve piezas del relevamiento Columbia Chile (barra de anuncios, header, hero,
carrito, consentimiento de cookies, chat, volver arriba, badge tecnológico y slider de
colección). Viaja como subpath propio y no dentro de ./store porque Columbia es la
procedencia de las piezas, no un dominio del design system: un consumidor que no arma ese
storefront no carga estas nueve.
@jbernous/cts-ui/store-columbia/styles.css (dist/store-columbia.css) es el mismo tipo de
artefacto que el del subset —plano, sin preflight, con las clases de colapso propias
cts-layout-col-* de la sección— y se consume igual: Tailwind 3, antes de tu propio CSS;
Tailwind 4, en capa propia (@import "@jbernous/cts-ui/store-columbia/styles.css" layer(cts),
con el mismo @layer theme, base, components, cts, utilities de arriba).
Un solo import alcanza. El artefacto cubre el directorio de la sección y también las tres
piezas del subset que la sección compone (ColumbiaHeader monta StoreThemeToggle;
ColumbiaCartDrawer monta StoreCartItem y StorePrice): sus clases viajan acá dentro, así que
un consumidor de la sección importa un stylesheet y no necesita además
@jbernous/cts-ui/store/styles.css. Lo que no cubre es el resto de src/store/**: el
escaneo nombra esas tres piezas archivo por archivo, nunca el subset entero. Si además usás
piezas sueltas del subset, sí te toca el artefacto del subset (los dos, antes del CSS
propio). El detalle medido está en docs/ecommerce/build-subpaths.md §4.3.
Los tipos resuelven desde dist/auth/index.d.ts, dist/store/index.d.ts y
dist/store-columbia/index.d.ts (el exports de cada subpath ordena types
primero). Los detalles de empaquetado (exports, sideEffects, tsconfig.build.json,
medición del bundle y el análisis diferido de exports por pieza) viven en
docs/ecommerce/build-y-tipos.md; el porqué de un config de Vite por artefacto y el
layout de subpaths, en docs/ecommerce/build-subpaths.md.
Requisitos del consumidor
- React 19 (
peerDependencies.react=^19.0.0): un proyecto en React 18 no instala sin conflicto. - Bundler: los artefactos son ESM + CJS; no hay UMD desde
67b2f3b. moduleResolution:bundler(onode16/nodenext). El viejo"node"no leeexportsy no resuelve@jbernous/cts-ui/store,@jbernous/cts-ui/authni@jbernous/cts-ui/store-columbia(TS2307: Cannot find module).- Next / App Router: envolvé el componente en un client boundary
(
"use client"). Los subpaths no traen la directiva y las primitivas que componen (Button, vía@base-ui/react) usan hooks. - Tailwind 3: importá el artefacto del subpath que uses —
@jbernous/cts-ui/store/styles.cssy/o@jbernous/cts-ui/store-columbia/styles.css(la sección trae el suyo, uno solo, y cubre las piezas del subset que compone) — como módulo, antes del CSS propio. El stylesheet completo@jbernous/cts-ui/styles.cssno se puede importar por bundler en un consumidor Tailwind 3: su plugin PostCSS rechaza cualquier@layer utilitiesque venga de afuera. Alternativa para ese caso: servirlo como asset estático con<link>. - Tailwind 4: los mismos archivos, pero en capa propia —
@import "…/store/styles.css" layer(cts)o…/store-columbia/styles.csslayer(cts)— y con el orden de capas declarado (ver arriba). El paquete no los envuelve por vos: la capa es una decisión del consumidor.
Licencia
MIT
