Saltar al contenido
El Muestrario

El registro

Todas las versiones publicadas de @solu30/ui-kit, leídas del paquete instalado en este repositorio. Está ordenado para una pregunta: tengo una versión clavada en mi package.json, quiero ir a la 5.8.1, y necesito saber qué cambió en el medio y qué de eso me rompe. Lo que rompe está acá arriba; el resto va del más nuevo al primero.

Versiones
163
Instalada acá
5.8.1
Rompen
7
Con cuerpo entero
17
01

Las 17 con cuerpo entero

Dos criterios, los dos atados a la pregunta de arriba. Va entero todo release que declara ruptura, sea de cuando sea: si tu salto cruza uno, lo tenés que leer completo antes de estimar nada. Y va entera la línea 5.x, la que se instala hoy, que es el tramo que la mayoría va a recorrer. Entero quiere decir el cuerpo del changeset como se escribió al publicar, con su racional y su verificación adentro.

17 de 163 con cuerpo entero. El corte es de largo: las 376 entradas suman 197.790 caracteres de markdown, y una lista de ese tamaño se recorre peor que un índice. Las otras 146 están abajo con su tipo de cambio y su primera línea. Cada hueco de esta lista dice cuántas versiones se salteó.

  1. 5.8.1

    Parche1 entrada#

    El movimiento decorativo del hover respeta prefers-reduced-motion.

    Los 49 hover que escalaban o desplazaban el elemento por gusto —cards que crecen un 2%, chips que se corren dos píxeles, miniaturas que hacen zoom— lo hacían también para quien pidió al sistema que no haya movimiento. Ahora el prefijo motion-safe: lleva sólo la parte que se mueve: el cambio de color, de sombra o de borde del mismo hover sigue ocurriendo siempre, así que el estado sigue siendo visible sin depender de la animación (WCAG 2.3.3, movimiento de interacción).

    No hay cambio de API: las clases se emiten igual y ningún prop cambia. El consumidor que ya tenía prefers-reduced-motion: reduce en su sistema deja de ver el salto; el resto no nota diferencia. La regla UIP-009 de scripts/check-ui-polish.mjs marca el hover decorativo sin motion-safe: para que no vuelva a entrar.

  2. 5.8.0

    Menor2 entradas#

    Card con variant="default" pasa a incluir shadow-sm en reposo (antes solo elevated/solid tenían sombra), alineado con la spec de GestorSalon (design-system.md §2: "Card en reposo → sombra siempre visible; hover → se eleva; press → se hunde"). La transición va entera en la base del componente y nombra las propiedades que cualquier variant puede animar (transition-[box-shadow,background-color,border-color,scale], nunca transition-all por ADR-320), para que cualquier hover:shadow-* de call-site anime en vez de saltar en seco.

    Los variants hover y clickable dejan de declarar transition-* propia: caía en el mismo grupo de tailwind-merge y pisaba la de la base. Verificado con twMerge sobre las cuatro combinaciones — con hover y clickable juntos ganaba clickable y se perdían las dos. Cubierto por tests que fallan si la regresión vuelve.

    Es aditivo en la API — ninguna prop cambia, ningún tipo se rompe, ningún consumidor deja de compilar — pero visualmente breaking en ~500 <Card> de 4 repos en ^5.x: gestorsalon 278, carlosmartinpavon 141, dietaly 65, raquetea 16.

    Resuelto: minor (Martín, 2026-08-18). La primera lectura fue major, para que ningún vertical adoptara el cambio visual sin revisarlo. Medido el efecto real, sale carísimo: los packages del monorepo declaran ui-kit como peerDependencies: "workspace:^", así que un 6.0.0 deja el rango ^5 fuera y changesets bumpea 54 packages a major en cascada — 59 publicados en total, y 6 de ellos (contact-memory, forms, growth, learning, notifications, paid-ads) saltando de 0.x a 1.0.0, o sea de pre-release a estable, sin que nadie lo decidiera. Con minor no se mueve ninguno.

    Y el riesgo que el major evitaba es menor de lo que parecía: gestorsalon está pineado en una versión exacta, no en un rango, así que no adopta nada solo. Los expuestos son dietaly (^5.5.0), raquetea (^5.3.0) y carlosmartinpavon (^5.0.0), y lo que reciben es una sombra sutil en reposo: una mejora visual, no una rotura funcional. El semver de npm mide superficie de API, no píxeles — usar un major para frenar un cambio visual es la herramienta equivocada, y el precio eran 54 majors ajenos.

    Antes de correr changeset version/publish: reemplazar este placeholder por el bump elegido y completar el pase visual de los 4 repos (incluye revisar .card-elevated y .glass-card en gestorsalon por doble sombra — ver "Triggers para revisión" del ADR).

    Detalle completo, incluida la trampa de shadow-sm vs. los tokens --shadow-* brand-tinted (no bridgeados en @theme inline en gestorsalon — fuera de esta decisión): docs/adr/ADR-326-el-card-del-kit-trae-sombra-en-reposo-por-default.md.

    El gráfico como parte, no como tarjeta (ADR-324).

    @solu30/ui-charts — los cinco Chart* aceptan bare: dibujan el cuerpo dentro de un <div className>, sin <Card> ni CardHeader. Para el gráfico que va adentro de un panel que ya es tarjeta, o de una fila de lista, donde el envoltorio anidaba cromo. title sigue nombrando el <caption> de la tabla alternativa, que es sr-only, y no se dibuja.

    @solu30/ui-kitSparkline acepta variant: "line" | "bars". Cambia la línea base: la línea normaliza entre mínimo y máximo (interesa la forma), las barras siempre desde cero (interesa la magnitud). fillArea y showDots no aplican a "bars".

    @solu30/superadminAgentMetricsClient dejó de importar recharts para dibujar un sparkline de 128×48 px. El tooltip por barra pasa a ser la serie completa en el <title> del SVG.

    @solu30/ui-charts (fix) — AnalyticsTreemap: el borde entre celdas usaba --color-bg, un token que nunca existió, así que caía siempre al fallback transparente y los rectángulos salían pegados. El token es --color-bkg. Además, recharts pasa el nodo raíz por el mismo renderer que las celdas: se dibujaba el total del dataset como una marca de agua gris en el medio del gráfico.

    Aditivo salvo el fix del treemap, que sólo cambia lo que ya se veía mal.

  3. 5.7.0

    Menor1 entrada#

    SearchInput: rootClassName — el prop que le faltaba para participar del layout

    SearchInput rutea su className al <input>, no al <div> raíz. El elemento que participa del layout del padre es la raíz, así que un flex-1, un min-w-0 o un sm:flex-none pasados por className nunca llegaban al flex item: el buscador no crecía, y el código decía que sí.

    Medido en gestorsalon el 2026-08-18: 12 call sites, entre ellos las toolbars de clientes, tareas, calendario, proveedores, espacios e inventario. La raíz además es el bloque contenedor de la lupa y de la X de limpiar (las dos absolute contra ella), así que un input más angosto que la raíz deja la X desalineada del borde del campo.

    Aditivo a propósito: className sigue yendo al input. Rutearlo a la raíz sería lo semánticamente correcto y lo que hace el resto del kit, pero cambia el comportamiento de 11 repos consumidores en silencio — esa corrección va por ADR aparte, no colada en un minor.

  4. 5.6.0

    Menor5 entradas#

    Tres piezas que no respetaban prefers-reduced-motion, y una barra que exigía saber cuánto falta

    Collapse honra la preferencia de movimiento por su cuenta. Documentaba que la heredaba de un <MotionConfig reducedMotion="user"> ancestro, y eso es cierto para transform y layout — pero height no es ninguno de los dos, así que el panel se seguía desplegando con animación para quien había pedido no ver movimiento. Ahora llama a usePrefersReducedMotion() y, cuando está activa, monta directamente en el estado final: sin fotograma inicial colapsado y con las transiciones en cero.

    Esto es lo que destraba a un consumidor que tenía siete archivos con el mismo patrón resuelto a mano (duration: reduceMotion ? 0 : 0.22) y había frenado su migración a <Collapse> a propósito, para no cambiar código accesible por código que no lo es.

    Spinner deja de girar para quien pidió no ver movimiento. El giro pasó a motion-safe:animate-spin, y en su lugar late con animate-pulse: un spinner detenido se lee como colgado, así que lo que se quita es la rotación, no la señal de que algo está pasando.

    ProgressBar acepta el modo indeterminado: value pasa de requerido a opcional, y omitirlo produce una barra continua que comunica "en curso" sin afirmar cuánto falta. Es la semántica del <progress> nativo y de ARIA — la ausencia de aria-valuenow es lo indeterminado —, por eso se modela como value? y no como un flag: no hay que inventar un número para decir que no se sabe. La animación va bajo motion-safe: con el keyframe viajando en el componente (mismo patrón que StatusDot), y para movimiento reducido queda una barra completa que late, en lugar de un tercio detenido que se leería como un progreso real trabado.

    Compatible hacia atrás: pasar value sigue funcionando igual y está cubierto por tests nuevos, incluido value={0}, que es un valor y no una ausencia.

    Los tres cambios salieron de un consumidor que los reportó con el diagnóstico hecho (Luigi, ADR PERF-001, reglas R7/R8/R9).

    Las superficies que cruzan la pantalla se quedan quietas con prefers-reduced-motion

    DATA_STATE_ANIMATIONS.slideIn* —el vocabulario que copian los consumidores— emitía slide-in-from-right y sus tres hermanas sin escape. Sin sufijo numérico esa clase desplaza el 100% del elemento: la superficie entera viaja por la pantalla, que es la clase de movimiento que WCAG 2.3.3 apunta. Ahora llevan motion-safe:.

    El fade queda sin prefijo a propósito, en el kit y en los dos consumidores: con prefers-reduced-motion la superficie sigue apareciendo con un fundido, así que se sigue anunciando sola. Apagar todo hace que aparezca de golpe, que es peor para todos.

    Mismo tratamiento en @solu30/ui-layouts: el drawer móvil de AdminLayout deja de viajar 288 px y el bottom-sheet de PageShell deja de subir el 100% de su alto. Ninguno cambia de aspecto con el movimiento activado.

    Lo hace cumplir MOT-004 en scripts/check-motion-a11y.mjs, con contra-test en scripts/__tests__/motion-a11y.test.mjs.

    Las transiciones del kit nombran lo que animan, y el preset gana el outline de medios

    transition-all sale del kit. Había 71 ocurrencias en 45 archivos. Cada una obliga al browser a vigilar toda propiedad computada del elemento, así que cualquier cambio de color, padding o borde que ocurra por otra razón —un cambio de tema, un data-state, una clase condicional— pasaba a animarse sin que nadie lo pidiera. Es la causa típica del "por qué parpadea al cambiar de tab".

    Dos de esas 71 no eran usos sino fuentes, y son las que explican por qué el patrón se propagaba a cada componente nuevo:

    • TRANSITION_TRANSFORM (de @solu30/ui-kit/utils) valía transition-all. La constante se llama TRANSFORM y transicionaba todo: quien la usaba creyendo que acotaba lograba exactamente lo contrario. Ahora vale transition-transform. Si dependías del comportamiento anterior —animar algo que no es un transform— el valor a usar es CSS_TRANSITIONS.common.
    • CSS_TRANSITIONS.all queda deprecado. No se remueve, así que nada rompe; en su lugar hay CSS_TRANSITIONS.common, con el conjunto que cubre el caso típico de una superficie con hover: color, background-color, border-color, box-shadow y opacity.

    Las 69 restantes se migraron decidiendo qué anima cada componente, no con un reemplazo ciego: las barras de progreso animan width, los sidebars colapsables width, los labels flotantes top,translate,scale,color, las superficies de input border-color,box-shadow, y el sol/luna del theme-toggle rotate,scale.

    El press de los botones se unificó en scale(0.96). Los dos desvíos que quedan son deliberados y ahora están declarados en el código con su razón: el desplazamiento del press es proporcional al ancho del elemento, así que 0.96 en una fila de nav de 240px encoge 9.6px y se lee como un salto, mientras que en un botón de 40px encoge 1.6px y se lee como tacto. Las filas anchas usan 0.985; un chip inline dentro de texto corrido usa 0.99 para no desplazar el renglón.

    TabsContent ya no anima su entrada al cargar la página (initial={false}): el panel activo en el primer render ya está ahí, no "llega". Los cambios de tab posteriores animan igual, y el fade-out no se toca.

    @solu30/tailwind-preset suma --ui-outline-media, disponible como outline-ui-media. Es el borde de 1px para imágenes, video y embeds: negro puro al 10% en light, blanco puro al 10% en dark. Tiene que ser negro o blanco puro —nunca un slate-900 o un zinc-800— porque un neutro teñido levanta el color de la superficie que tiene debajo y se lee como suciedad en el borde de la imagen.

    Todo esto queda sostenido por scripts/check-ui-polish.mjs, que reporta las violaciones como error y admite supresión explícita por línea para los desvíos deliberados. El kit queda en cero.

    Pasada completa del kit sobre las reglas del React Compiler: se sacan del cuerpo del render las escrituras y lecturas de refs, los setState síncronos en el cuerpo de effects y los componentes declarados adentro de otros componentes. Casi todo es equivalente en comportamiento, pero hay una cosa que un consumidor puede notar — de ahí el minor y no un patch.

    1. FormItem llama a React.useId() siempre. Estaba como valor por defecto del parámetro, así que sólo corría cuando el consumidor no pasaba id: alternar entre pasarlo y omitirlo cambiaba la cantidad de hooks entre renders y React tiraba «Rendered fewer hooks than expected». Ahora el hook va incondicional en el cuerpo y el id del prop se usa como override. El id que ve cada FormItem es el mismo que antes, pero en árboles renderizados en cliente React genera los ids con un contador global: un FormItem con id explícito ahora también consume un turno de ese contador, así que los ids autogenerados de otros componentes pueden correrse. Si algún test o selector está pinneado a un id de useId, es ahí donde se va a ver.

    useAutoSave conserva su contrato — pero estuvo cerca de no hacerlo. Al migrar este hook fuera del effect se había cambiado sin querer qué pasa al re-habilitarlo: el ledger del fingerprint pasaba a actualizarse también con enabled: false, así que volver a habilitar dejaba de persistir lo editado durante la pausa. Antes lo persistía, y perderlo es pérdida silenciosa de trabajo del usuario: supplier-invoices enciende el auto-save al pasar a la fase review, así que quien cargaba la factura y pasaba a review sin tocar nada más se quedaba sin draft. Se detectó revisando el diff para armar este changeset y se revirtió antes de publicar. El hook queda como estaba, y ahora con tests que fijan los tres casos que se confundían entre sí: montar con enabled: false (toma línea de base, no guarda lo hidratado), hidratar y habilitar en el mismo render (no guarda), y pausar y re-habilitar ( guarda lo editado durante la pausa).

    Resto del cambio, sin efecto sobre la API:

    • Overlays (Modal, Sheet, Popover, Select, MultiSelect, SelectComposition, AlertDialog, ConfirmDialog, DateRangePicker, FilterDropdown, FilterableHeader, LanguageSwitcher): el target del portal sale de useSyncExternalStore. Un overlay que se monta después de la hidratación recibe el target en su primer render en vez del segundo, así que deja de perder un frame antes de aparecer. ConfirmDialog deriva el montaje de isOpen en vez de hacerlo desde un effect, lo que además mata la carrera que obligaba a cancelar el requestAnimationFrame a mano.
    • Sync de prop a estado durante el render en AddressAutocomplete, ChipToggle, EditableField, Combobox, CommandPalette, WalkerTimeSettings, DebouncedSearchInput y el phone del form: mismo disparador, sin el frame con el valor anterior.
    • CollapsibleSection y ContextualSidebar: la rehidratación desde localStorage y el merge de secciones forzadas pasan a ajuste durante el render. En ContextualSidebar el disparo pasa a ser el id de la sección activa y no la referencia de sections: un consumidor que pasa el array inline ya no reabre la sección que el usuario cerró a mano. En CollapsibleSection desaparece además una escritura transitoria a localStorage con el estado sin rehidratar.
    • NavigationProvider: las media queries y la hidratación salen de hooks con useSyncExternalStore en vez de un effect de montaje con cinco setState — era un render en cascada en el provider que envuelve toda la app. Lo que ve el consumidor durante SSR no cambia.
    • Hooks: useLocalStorage relee al cambiar de key durante el render y deja de volcar un objeto nuevo al montar por la misma key (rompía la memoización de quien lo consume); useServerSyncedState elimina el ref espejo de isDirty; useGridKeyboardNav y useListKeyboardNav dejan de resetear el foco cuando el consumidor pasa callbacks sin memoizar; useScrollVisibility lee prefers-reduced-motion con el hook de media query; useQrCode, useSpeechRecognition, useKeyboardShortcuts, useModalStack, useModalWithBack, useCloseOnNavigation y useScrollSentinel sincronizan sus espejos en effects declarados antes de quien los lee.
    • TabbedPanel detecta el modal en la callback ref (en el commit, no después del paint), Button compone con JSX en vez de cloneElement, y EmptyState monta el ícono resuelto por nombre con createElement (ADR-194).

    Las transiciones de todos los packages nombran las propiedades que animan

    transition-all obliga al browser a vigilar toda propiedad computada del elemento. La consecuencia visible: cualquier cambio de color, padding o borde que ocurra por otra razón —un cambio de tema, un data-state, una clase condicional— pasa a animarse sin que nadie lo haya pedido. Es la causa típica del "por qué parpadea al cambiar de tab".

    Había 449 ocurrencias en 240 archivos del monorepo. Quedan 0. Las 396 que el codemod pudo clasificar salieron por inferencia; las 53 restantes —donde el estado vive en otra variable, o donde el elemento no animaba nada— se decidieron leyendo el componente, una por una.

    Ninguna se reemplazó a ciegas: cada una declara ahora lo que ese elemento realmente anima, inferido de sus clases de estado. Las barras de progreso animan width, las superficies de input border-color y box-shadow, los chips y botones color y background-color, los labels flotantes top, translate y color.

    Esto es un cambio de comportamiento visual, no de API. Ningún export cambió y ninguna prop se movió, así que no hay nada que migrar. Pero un elemento que antes animaba una propiedad de rebote —porque all las cubría todas— ahora no lo hace. Si notás que algo dejó de animarse, la clase a mirar es la transition-[…] de ese elemento: le falta una propiedad en la lista, y agregarla es la corrección.

    Siete elementos no animaban nada —className literal, sin una sola clase de estado— y ahí la transición se borró en vez de acotarse. TRANSITIONS.all de @solu30/email-system queda deprecado con valor acotado, como ya lo estaba CSS_TRANSITIONS.all del kit: era la fuente que repartía la violación con nombre propio.

    La regla queda sostenida por pnpm check:ui, que corre los tres checkers de UI del ecosistema —color (ADR-003), movimiento apagable (ADR-310) y micro-detalle (ADR-320)— y da un veredicto único.

  5. 5.5.0

    Menor1 entrada#

    TabsContent (composition-mode: TabsRoot/TabsList/TabsTrigger/TabsContent) cumple lo que el header del archivo prometía desde antes de que existiera el vocabulario para lograrlo: un if (!isActive) return null desnudo pasa a envolver el panel en <MotionConfig reducedMotion="user"><AnimatePresence><motion.div variants={panelVariants} .../></AnimatePresence></MotionConfig>panelVariants sale de ../../tokens/motion-vocabulary (ADR-314). ADR-319.

    Cambio de comportamiento visual para todos los consumidores existentes de TabsContent, sin flag de opt-out — ningún prop ni tipo cambia (se trata como minor, no major), pero el switch de tabs ahora hace fade real (entrada Y salida) en vez de aparecer/desaparecer sin transición. Consecuencia esperada: el panel saliente y el entrante coexisten brevemente en el DOM durante la transición exit — en layouts sin posicionamiento absoluto puede verse un salto de layout muy breve, mismo trade-off de cualquier fade con AnimatePresence sobre contenido en flujo normal.

    Migración para quien ya lo suplía a mano (ej. GestorSalon night-app-view.tsx, que envuelve <TabsContent> con su propio motion.div + panelVariants importado de @/lib/animations, con un comentario propio que documentaba la limitación que estaba parchando): ese wrapper ahora produce doble animación anidada. Quitarlo y dejar que TabsContent anime solo.

  6. 5.4.0

    Menor2 entradas#

    Nuevo subpath tokens suma el vocabulario de motion de framer-motion (ADR-314): EASE_CONTENT, EASE_QUICK, SPRING_SLIDE, SPRING_SIZE (transitions) y panelVariants, rowVariants, listContainerVariants, staggerContainer, staggerItem, fadeIn, collapseVariants (variants hidden/visible/exit). Todos los valores derivan de SPRING/DURATION/EASING/STAGGER — cero números nuevos. Promovido desde GestorSalon (src/components/reception/motion.ts, src/lib/animations.ts), que puede migrar a re-exports de @solu30/ui-kit/tokens.

    listContainerVariants cambia de definición: el legacy (initial/animate/exit, sin consumidores verificados en cmp-packages ni GestorSalon) se retira de tokens/animations.ts y el nombre pasa a ser el canónico hidden/visible de tokens/motion-vocabulary.ts. Consumidores que sí lo usaban deben migrar a hidden/visible/exit.

    Restaura el subpath @solu30/ui-kit/images — desde la migración de @solu30/cloudflare-images (deprecated 2026-05) package.json declaraba ./dist/utils/images/index.{js,d.ts} en exports, pero tsup.config.ts nunca tuvo un entry para src/utils/images/index.ts, así que ningún build (incluida la 5.3.0 ya publicada) generó ese archivo — el tarball real solo traía el src/ crudo. Un consumidor que importara ese subpath rompía con TS2307/ERR_MODULE_NOT_FOUND; el barrel raíz (@solu30/ui-kit) no estaba afectado, porque re-exporta los mismos símbolos. Detectado por guard-release-tarballs.mjs al intentar publicar 5.4.0.

  7. 5.3.0

    Menor4 entradas#

    Nuevo token border-ui-field para el borde de los controles, separado del hairline decorativo.

    Un input del kit tiene bg-container, o sea el mismo relleno que la tarjeta sobre la que se apoya. Su borde no es decoración: es lo único que dice "acá se puede escribir". Ese borde se dibujaba con border-content/10 o border-ui-border, que medidos sobre la paleta real de un vertical dan 1.22:1 en tema claro y 1.31:1 en oscuro — muy por debajo del 3:1 que WCAG 1.4.11 pide para lo que permite identificar un control. Una casilla sin marcar es el caso extremo: sin borde visible no hay casilla.

    Lo que NO cambia: los 190 usos decorativos de border-content/10 —separadores, bordes de tarjeta, hairlines entre filas— se quedan como están. Ninguno identifica un control y 1.4.11 no los alcanza; subirlos a 3:1 habría convertido cada tarjeta en una caja con marco.

    Migrados a border-ui-field: TextInput, TextArea, Input, input/textarea/dropdown/ multiSelect/phone/time24h/document/checkbox de form, el botón outline, Checkbox sin marcar, la casilla de CheckboxGroup, y los cinco editores inline (InlineText, InlineEditText, InlineEditTextarea, InlineCurrency, InlineCostInput, InlineSelect).

    De paso, tres arreglos que aparecieron al mirarlos de cerca:

    • hover:border-content/20 sobre un borde de control ahora sería un retroceso —1.5:1 contra los ≥3:1 del reposo—, o sea que pasar el mouse aclaraba el borde. Reemplazado por hover:border-primary donde el hover era la única señal, o por la sombra sola.
    • form/utils/textarea.tsx y form/inputs/document.tsx declaraban border-content/10 y border-ui-border en la misma clase: una de las dos nunca se aplicaba y cuál ganaba dependía del orden en que Tailwind emitiera las utilidades.
    • form/inputs/document.tsx arrastraba dos false literales en el className, de una interpolación que quedó a medias.

    Para el consumidor: definir --ui-field en globals.css con un valor que llegue a 3:1 contra bkg, container e inner. Sin definirla cae a --ui-border y todo queda exactamente como está hoy — el arreglo es opt-in por vertical, no hay regresión visual para nadie.

    Nuevo <Collapse open={boolean}>: wrapper animado de height: auto medido en runtime vía framer-motion, para acordeones y paneles expandibles cuyo alto real no se conoce hasta renderizar contenido. DATA_STATE_ANIMATIONS (el mecanismo CSS existente, atado a los data-state de Radix) solo conoce dos estados instantáneos y no puede llegar ahí — Collapse es la salida que ADR-310 reserva para ese caso puntual, no un reemplazo general.

    Peer de framer-motion ampliado de ^12.0.0 a ^12 || ^13 — 13.1.0 es el mismo paquete que motion con un único breaking real (la dependencia opcional @emotion/is-prop-valid, que el kit no usa).

    Eliminado el duplicado interno tokens/motion.ts: exportaba los mismos SPRING/INTERACTION/ STAGGER/GLASS/VARIANTS que ya vive en tokens/animations.ts (el barrel público de tokens/index.ts siempre re-exportó desde ahí). Sin cambio de API pública — su único consumidor interno (ActionCard) apuntaba a animations.ts en todo lo que importaba a través del barrel.

    StaggerContainer / StaggerItem dejan de usar framer-motion y hacen la cascada en CSS.

    La versión anterior arrancaba en initial="hidden" con un useInView, y eso servía el HTML del servidor con style="opacity:0". La consecuencia medible: en un dashboard la fila de KPIs suele ser el elemento más grande de la pantalla, y un elemento en opacidad 0 no cuenta para el LCP — el Largest Contentful Paint se corría hasta después de la hidratación. Además el contenido no aparecía nunca sin JavaScript, y podía no aparecer tampoco con JavaScript si un ancestro con overflow:hidden recortaba al elemento y el IntersectionObserver no llegaba a dispararse.

    Ahora el estado en reposo es visible y la entrada la hace un @keyframes con animation-delay calculado por :nth-child. Arranca en el primer pintado, no depende de la hidratación, respeta prefers-reduced-motion y ninguno de los tres modos de falla existe.

    Dos consecuencias para el consumidor, ninguna requiere tocar código:

    • Los dos componentes ya no llevan 'use client': se pueden renderizar dentro de un Server Component y no suman framer-motion al bundle del cliente.
    • La cascada corre al cargar la página, no al llegar scrolleando, y se corta en el hijo 12 (más de un segundo de cascada se lee como lentitud, no como animación). Para revelado por scroll real está MotionSection, que sigue usando framer.

    MotionSection conserva su comportamiento y gana dos arreglos: un <noscript> que fuerza el estado final cuando no hay JavaScript, y un rootMargin que ahora recorta solo por abajo — el recorte en los cuatro lados dejaba permanentemente invisible a cualquier elemento de menos de 100px pegado al borde superior de la ventana.

    La API pública (nombres, props, tipos, motionSectionVariants) no cambia.

    Modal gana dismissible y initialFocusRef.

    dismissible={false} hace el modal genuinamente no-cerrable: bloquea Escape, el click en el backdrop (independiente de static) y oculta el botón X (independiente de hideCloseButton). El role pasa de dialog a alertdialog, la misma semántica ARIA que ya usa AlertDialog.

    Es opt-in y el default (true) no cambia ningún comportamiento existente. El JSDoc de la prop deja escrito el riesgo de a11y: sin una salida alcanzable por teclado dentro del propio modal es una trampa de teclado (WCAG 2.1.2), así que con dismissible={false} el foco inicial va a la acción primaria del footer en vez del primer focuseable, y initialFocusRef permite apuntarlo explícitamente.

  8. 5.2.0

    Menor1 entrada#

    DateRangeColumnFilter — presets de un click y labels i18n (pedido de Martín sobre Finanzas › Gastos: "el filtro de fechas de la columna, que sea más interactivo").

    • presets?: readonly DateRangePreset[] — chips arriba de los inputs From/To. Cada preset es { label, value: [string | null, string | null] }; el rango lo calcula el consumidor (el kit no trae lógica de calendario ni idioma). El chip cuyo value coincide exactamente con el valor actual se renderiza activo (bg-primary text-white border-primary); el resto, inactivo (border-content/10 text-content/60 hover:bg-upper). Click en un chip llama onChange(preset.value).
    • labels?: { from?: string; to?: string; clear?: string } — overrides de i18n para los textos de los inputs y el botón de limpiar; default en inglés ("From"/"To"/"Clear filter"), sin cambio de comportamiento para quien no lo pasa.
    • Nuevo type exportado: DateRangePreset, junto a DateRangeFilterProps, desde el subpath @solu30/ui-kit/data-display.

    Sin presets ni labels, el filtro renderiza exactamente igual que antes.

    Bump = minor: dos props opcionales nuevas, un type nuevo, cero cambios en la firma existente.

  9. 5.1.1

    Parche1 entrada#

    StatCard — la columna de texto encoge, así que el contenido deja de salirse de la tarjeta.

    El problema: la columna de texto era flex-1 a secas. El default de un ítem flex es min-width: auto, o sea que no encoge por debajo de su ancho intrínseco: truncate no recortaba nada y el texto se DESBORDABA de la tarjeta, pisando la de al lado. Reportado por Martín el 13/08 sobre las tarjetas de Finanzas de GestorSalón ("revisá los contenedores para que adapten el contenido, no que los textos se salgan de él"). MetricCard, el hermano de al lado, ya tenía flex-1 min-w-0 — este era el que faltaba.

    El cambio:

    • La columna de texto pasa a min-w-0 flex-1 (slot nuevo body, para que se pueda mirar y testear como los demás).
    • El envoltorio del ícono gana shrink-0: sin eso, ahora que la columna encoge, un título largo podía achicar el ícono.
    • description pasa de truncate a line-clamp-2. Es donde vive la aclaración que hace legible al número de arriba ("$ 0 de ingreso todavía no tiene costo cargado") y media frase con puntos suspensivos no aclara nada. Dos renglones es el techo: más y la tarjeta deja de ser una tarjeta.

    Bump = patch: no cambia la API ni la forma de los datos. El único cambio visible es en tarjetas cuyo texto ya estaba roto — donde entraba, entra igual.

  10. 5.1.0

    Menor1 entrada#

    ContextualSidebar — prop sectionBadgeRollup: la sección cerrada anuncia en su header lo que sus hojas esconden (ADR-027 addendum v6).

    El problema: los badges viven en las hojas — «Pedidos 3» dentro de «Locales», «Reclamos 2» en la misma sección. Un acordeón cerrado los tapa por completo, así que el contador sólo existe para quien ya abrió la sección: el aviso llegaba justo a quien no lo necesitaba. Pedido de Martín sobre el menú de Luigi: "ver las alertas en el menú principal cuando hay alertas en alguna subsección".

    El cambio:

    • ContextualSidebar y ContextualSidebarContent reciben sectionBadgeRollup?: boolean, default false.
    • En true, una sección cerrada pinta la SUMA de los badges numéricos de sus hojas, con el tono de mayor severidad entre las que aportaron: error > warning > info > accent > (sin tono) > success. success va último porque es el único tono que no reclama nada.
    • Sólo cerrada — abierta, el badge del hijo está a la vista y repetirlo arriba haría leer dos pendientes donde hay uno. Excepción: el rail de collapsedMode="sections", donde las hojas no se dibujan en ninguna parte, así que ahí el roll-up va siempre como burbuja sobre el ícono.
    • Sólo números. Un badge: "Nuevo" no participa: el roll-up es un contador.
    • Nunca en la fila plana (isFlatSingleItem): esa fila ES el ítem y ya pinta su badge.

    Por qué lo deriva el kit y no el consumidor: la alternativa era badge/badgeTone en la sección y que cada vertical sumara. La regla es presentación pura, no dominio, y replicada en cinco verticales se desincroniza sola.

    Bump = minor: prop nueva, puramente aditiva. Con el default false ningún consumidor existente cambia un píxel — la forma de los datos tampoco cambia (no hay nada nuevo que calcular ni pasar).

    Cubierto por src/components/contextual-sidebar/ContextualSidebar.test.tsx (5 casos: suma, severidad máxima, se esconde al abrir, default apagado, badges de texto ignorados).

  11. 5.0.0

    Mayor1 entrada#

    EmptyState deja de importar lucide-react de cualquier forma — ni estática, ni dinámica (React.lazy) — así que iconName se resuelve por inyección, no por lazy-load. Mismo mecanismo que KPICardGrid desde 4.19.0, ver ADR-194 (sección "Reincidencia #3 — 2026-08-11").

    El problema que corrige: el React.lazy(() => import("./LucideDynamicLoader")) que EmptyState usaba desde 2026-06-20 (ADR-194 original) parecía gratis — no toca el First Load JS — pero un bundler de producción materializa ese chunk SIEMPRE, se use iconName o no. Medido en GestorSalon: ~620 KB en el build de producción, en un repo con 142 usos de EmptyState y cero consumidores de iconName.

    El breaking change:

    • EmptyStateProps gana resolveIcon?: (name: string) => React.ComponentType<{ className?: string }> | undefined. EmptyStateAction.iconName usa el mismo resolver inyectado en el padre.

    • Sin resolveIcon, un iconName (en EmptyState o en action(s)) no renderiza ningún ícono — no rompe el layout, pero deja de mostrarse. Antes se resolvía solo, vía el mapa completo de lucide-react cargado lazy.

    • Para la DX anterior ("cualquier ícono de Lucide por nombre"), importar resolveLucideIcon desde @solu30/ui-kit/dynamic-icon y pasarlo:

      import { resolveLucideIcon } from "@solu30/ui-kit/dynamic-icon";
      
      <EmptyState
        iconName="package"
        resolveIcon={resolveLucideIcon}
        title="Sin resultados"
      />;
      

    Migración: en cada <EmptyState iconName="..." .../> o action={{ iconName: "...", ... }}, agregar resolveIcon={resolveLucideIcon} (import de @solu30/ui-kit/dynamic-icon) al EmptyState padre.

    Relevamiento de consumidores (ver ADR-194 para el detalle completo): luigi tiene ~142 usos de iconName en 87 archivos; carlosmartinpavon tiene 1 (apps/web/src/app/lab/page.tsx). Ambos ya migrados en este mismo lote (ver commits de luigi y carlosmartinpavon). Dentro de este monorepo, packages/social (10 usos en 8 archivos) también se migró en el mismo lote.

    Aviso en desarrollo: si un iconName queda sin resolveIcon (o resolveIcon no resuelve ese nombre), EmptyState avisa por console.warn solo en NODE_ENV !== 'production' (deduplicado por nombre) — cero costo en producción, para que la regresión no sea silenciosa.

    LucideDynamicLoader.tsx fue removido (sin consumidores tras el fix).

  12. 30 versiones entre medio, en el índice

    4.0.0

    Mayor1 entrada#

    F2.5 (ADR-229): remoción física de la superficie @deprecated marcada por F2 (ADR-225/226). Se eliminan ~130 exports sin consumidores, verificados dependency-aware: 0 consumidores en los 11 verticales (consumer-imports.json) + 0 uso interno vivo (gate cross-file) + mapa de dependencias de exports vivos para no romper componentes que referencian types internamente. Wholesale: 46 archivos 100% muertos borrados (compat shims, hooks/utils/components huérfanos) + limpieza de barrels. Surgical: types/variants deprecados removidos de archivos mixtos preservando el export vivo (Toolbar, ActivityTimeline, useFloatingPosition, useModalWithBack, Z_INDEX…); los types usados internamente se des-exportan (quedan privados) en vez de borrarse. Subpath ./compat eliminado (barrel vacío, 0 consumidores). Neto ~−5.300 LOC. Breaking por contrato semver aunque el set tiene 0 consumidores conocidos; los verticales suben de 3.x a 4.x a su ritmo (F1). No toca el subsistema command/CommandPalette ni el leakage de dominio vivo (queda para F4).

  13. 2 versiones entre medio, en el índice

    3.0.0

    Mayor6 entradas#

    Split del god-package (ADR-220, F3): el subpath @solu30/ui-kit/charts (charts analytics + recharts) se extrae al package 2-domain @solu30/ui-charts. El kit deja de exportar ./charts (breaking) y suelta el peer recharts. Migración: @solu30/ui-kit/charts@solu30/ui-charts (símbolos idénticos). Consumidor interno marketing-admin ya migrado. Mecánica member→root (ui-charts importa Card/Badge/Skeleton/Popover/Table de @solu30/ui-kit), sin ciclo. Guía: docs/migration/ui-kit-3.0.md.

    Split del god-package (ADR-221, F3): el DataTable headless + shell + hooks/filters/variants se extrae del barrel raíz @solu30/ui-kit al package 2-domain @solu30/ui-data-table. El kit deja de exportar los símbolos data-table por data-display.ts y components/index.ts (breaking); los primitivos Table/TableHeader/EmptyState de bajo nivel permanecen. member→root (importa tv/cn de @solu30/ui-kit/utils, useClickOutside de @solu30/ui-kit/hooks), sin ciclo. Migración: import { DataTable } from '@solu30/ui-kit'from '@solu30/ui-data-table' (símbolos idénticos). Los 9 consumidores internos ya migrados; las 7 verticales van a la guía docs/migration/ui-kit-3.0.md.

    Split del god-package (ADR-219, F3): el subpath @solu30/ui-kit/geo (GeoMap + Leaflet/react-leaflet) se extrae al package 2-domain @solu30/ui-geo. El kit deja de exportar ./geo (breaking) y suelta los peers leaflet/react-leaflet. Migración de import: @solu30/ui-kit/geo@solu30/ui-geo (símbolos idénticos, sin renames). Guía: docs/migration/ui-kit-3.0.md. Mecánica member→root (ui-geo importa cn+Skeleton de @solu30/ui-kit), sin ciclo (ADR-017).

    ActivityTimeline: las entradas con action: 'field_updated' ahora aceptan details.changes: ActivityTimelineFieldChange[] para pintar varios diffs de campo (field/oldValue/newValue) apilados dentro de la misma entrada, en vez de forzar al consumidor a emitir una entry separada por campo cambiado (motivo repetido N veces cuando un solo evento tocaba varios campos).

    Aditivo y retrocompatible: el details.field/oldValue/newValue singular existente sigue funcionando igual para todos los consumidores actuales. Precedencia: si details.changes viene no-vacío, gana sobre el singular (que queda @deprecated mas no se remueve). Nuevo tipo exportado: ActivityTimelineFieldChange.

    Nuevo export público headless SidebarNavGroups (+ tipo SidebarNavGroupsProps), extraído del renderer interno de SidebarNavigation (ADR-223). Permite a hosts con chrome propio (AdminLayout del CMP: aside + toggle + drawer) renderizar solo la lista de items — grupos, childGroups drill-in, badges, roving arrow-key nav sobre top-level — sin anidar un segundo sidebar auto-contenido. SidebarNavigation ahora lo consume internamente (desktop + drawer mobile): cero cambio de comportamiento para consumidores actuales. Prop ariaLabel? (default "Sidebar navigation") override-able para componer múltiples instancias sin violar landmark-unique (a11y). Gate /adversarial --mode redblue 2026-07-17: SHIP WITH FIXES (aria-label override + JSDoc honesto), equivalencia de branches de label verificada por absorción booleana.

    Build plumbing fix, no consumer-facing change: the build script now invokes node --max-old-space-size=8192 directly against tsup's CLI (node_modules/tsup/dist/cli-default.js) instead of the plain tsup binary. This is a workaround for a CI-only OOM (ERR_WORKER_OUT_OF_MEMORY generating .d.ts for the package's 29 entry points) — NODE_OPTIONS configures the main process's V8 isolate but doesn't populate process.execArgv, and worker_threads inherit execArgv, not NODE_OPTIONS, so the DTS worker never saw the heap flag regardless of its value. Passing it as real argv fixes that. Published output (dist/**) is unaffected.

  14. 13 versiones entre medio, en el índice

    2.0.0

    Mayor1 entrada#

    feat(ui-calendar-grid): new leaf package for the headless calendar-grid engine, extracted from @solu30/ui-kit (ADR-160, supersedes ADR-043/ADR-044)

    Amendment 2026-07-11: renamed @solu30/calendar-grid@solu30/ui-calendar-grid and moved to packages/ui/2-domain/calendar-grid/ pre-publish (ADR-160 amendment, ui-* naming convention) — the package never shipped to the registry, so this is a plain rename, not a breaking change to a published surface.

    Rationale (see ADR-160): MonthGrid, DayCell, EventChip, CalendarLegend, useCalendarNavigation (+ types) lived in @solu30/ui-kit foundation layer per ADR-043 as a headless cross-vertical primitive. It has 2 real consumers today, found via a symbol-by-symbol cross-repo grep (not just the composite BookingCalendar name, which is how the first pass under-counted): GestorSalon's booking-calendar-view.tsx (via <BookingCalendar>, event/venue booking) and luigi/apps/admin's employee-month-view.tsx (via MonthGrid + CalendarLegend + useCalendarNavigation directly, no <BookingCalendar> — an HR attendance view, unrelated to booking domain). No other vertical imports any of these symbols (TheCharterPanel, easycharter, prepchat, dietaly, EasyYachts, pagebuilder, elliot, zentrya, carlosmartinpavon — all checked, all clean).

    A first iteration of this changeset moved everything into @solu30/booking-system (already the domain package for booking). Escalated to Martín with 3 options once the non-booking consumer was found (move to booking-system / new leaf package / partial split); Martín chose a new leaf package, @solu30/ui-calendar-grid — his choice is the explicit OK for this package's alta (non-delegable). This avoids forcing luigi/apps/admin to adopt @solu30/booking-system's full peer-dependency contract (@trpc/server, drizzle-orm, framer-motion, qrcode.react) purely to render a calendar grid it doesn't otherwise need.

    New package: @solu30/ui-calendar-grid (minor, first release)

    Contains MonthGrid, DayCell, EventChip, CalendarLegend, useCalendarNavigation, plus types (CalendarEvent, StatusConfigEntry, StatusConfigMap, CalendarLegendItem, NavigationDirection, UseCalendarNavigationReturn) and pure date utilities. peerDependencies: react, react-dom, lucide-react, and @solu30/ui-kit (only for the cn helper — no cycle). No @trpc/server/drizzle-orm/framer-motion/next. No naming collision here — CalendarEvent keeps its original name (the BookingCalendarEvent rename only applies at the @solu30/booking-system boundary, see below).

    Breaking change in @solu30/ui-kit (major)

    BookingCalendar, MonthGrid, DayCell, EventChip, CalendarLegend, useCalendarNavigation, and their associated types are removed from the @solu30/ui-kit root export. No re-export shim: @solu30/booking-system already depends on @solu30/ui-kit, so a shim would create a foundation→domain→foundation cycle. Migration:

    - import { BookingCalendar, type CalendarEvent } from "@solu30/ui-kit";
    + import { BookingCalendar } from "@solu30/booking-system";
    + import { type CalendarEvent, useCalendarNavigation, type StatusConfigMap, type CalendarLegendItem } from "@solu30/ui-calendar-grid";
    

    Additive change in @solu30/booking-system (minor)

    BookingCalendar (the composed, booking-flavored wrapper — same alwaysScroll/mobile-scroll design from ADR-043/ADR-044, same BookingCalendarProps public shape) stays here as src/components/BookingCalendar.tsx, now composing MonthGrid + CalendarLegend + date utilities from the new @solu30/ui-calendar-grid dependency instead of owning them as local files. New peerDependency: @solu30/ui-calendar-grid (optional: false, same treatment as @solu30/ui-kit). Internally aliases the leaf's CalendarEvent as BookingCalendarEvent to avoid confusion with this package's own CalendarEvent (the Google Calendar API event shape) — no actual compile collision today since the raw grid pieces are not re-exported from this package's root.

    Consumer impact

    2 real consumers today, both to be migrated as part of this release (working trees edited, uncommitted, not built/typechecked):

    • saloneventos (GestorSalon) booking-calendar-view.tsxBookingCalendar from @solu30/booking-system; useCalendarNavigation, CalendarEvent, StatusConfigMap, CalendarLegendItem from @solu30/ui-calendar-grid. useIsMobile stays on @solu30/ui-kit (unrelated, not moved).
    • luigi/apps/admin employee-month-view.tsxMonthGrid, CalendarLegend, useCalendarNavigation, CalendarEvent (no prefix — the BookingCalendarEvent rename does not apply outside booking-system), StatusConfigMap, all from @solu30/ui-calendar-grid directly. ChevronLeftIcon/ChevronRightIcon/EmptyState/IconButton/Spinner stay on @solu30/ui-kit.

    Gating: @solu30/ui-calendar-grid does not exist on the registry yet (new package) and the published @solu30/booking-system does not yet compose it. saloneventos and luigi will fail to build/typecheck against the current registry state — expected, and not fixed locally. Those two verticals' commits

    • lockfile bumps are gated to the coordinated publish sequence (ui-calendar-grid first release → booking-system minor → ui-kit major), which needs Martín's explicit OK — not part of this changeset.
  15. 31 versiones entre medio, en el índice

    1.0.0

    Mayor2 entradas#

    ui-kit 1.0.0 — SemVer estable (ADR-048). Salto de 0.x a 1.0.0 para cortar el peer-cascade de versionado: en 1.x los minors/patches de ui-kit dejan de arrastrar majors en los 41 packages que lo declaran como peerDependency. Esta versión no introduce breaking changes de API más allá de la estabilización; el major refleja el cruce de rango ^0.80 → ^1.0.

    Sheet: nuevo size xxlmax-w-5xl (1024px) + nueva prop keepMounted.

    • size="xxl": variante max-w-5xl en sheetVariants para editores de ancho amplio (e.g., InvoicesManager de @solu30/superadmin). ADR-045.
    • keepMounted?: boolean (default false): cuando es true, el contenido permanece montado en el DOM mientras el Sheet está cerrado. Útil para children con refs imperativos (useImperativeHandle / forwardRef) que deben mantenerse vivos antes de que el Sheet abra. Backwards-compatible: el comportamiento por defecto no cambia.

    BookingCalendar (MonthGrid + utils): la grilla renderiza solo las semanas que el mes necesita (5 o 6 filas), nunca una semana final compuesta enteramente por días del mes siguiente.

    • getDaysInMonth ahora rellena hasta completar la última semana parcial (Math.ceil(days.length / 7) * 7) en lugar de forzar siempre 42 celdas. Resultado: 35 celdas (5 semanas) en la mayoría de los meses y 42 (6 semanas) solo cuando el mes realmente se extiende a una sexta semana.
    • MonthGrid deriva gridTemplateRows de la cantidad real de filas (days.length / 7).

    DataTableShell: el slot root pasa a flex flex-col, habilitando que el slot content (flex-1 overflow-auto) llene el alto cuando el consumidor le da un alto definido (cadena flex / h-full). Retrocompatible: en usos de alto automático el stack vertical es idéntico. Habilita fill-height en ~20 listados de verticales.

  16. 12 versiones entre medio, en el índice

    0.72.0-pre.1

    Ruptura9 entradas#

    refactor(tokens): erradicación ABSOLUTA de /N opacity modifiers en todo src/ del kit (Pasada 2). Cero clases con alpha sufijo en producción — todos los tokens de opacidad migrados a sólidos semánticos o tokens horneados.

    All overlay classes (bg-black/50, bg-black/40, etc.) → bg-overlay (token --color-overlay: rgb(10 22 20 / 0.55) horneado en globals).

    All focus rings (ring-primary/50, ring-primary/20) → ring-ring (token --color-ring: var(--ui-accent) sólido).

    All error/warning rings (ring-yellow/40, border-yellow/40, border-red/50) → ring-ui-warning-border / ring-ui-error-border.

    All decorative gradients (from-primary to-primary/70) → two-stop solid (from-primary to-primary-hover).

    Editor tag polychrome colors → Tailwind scale solids (bg-amber-100 text-amber-800 ring-amber-300).

    Scroll fades via-container/80via-container.

    Shadow color tokens GLOW (shadow-primary/20) → shadow-lg (plain).

    Requires 3 new tokens in vertical globals: --color-overlay, --color-overlay-light, --color-ring (added in Dietaly globals.css).

  17. 0.72.0-pre.0

    Ruptura1 entrada#

    refactor(tokens): Pasada 1 — erradicación de /N en componentes principales (Card, inputs, borders). 907 → 190 ocurrencias.

58 versiones más viejas, en el índice

02

El índice: las otras 146

Del más nuevo al primero, agrupadas por línea mayor. Cada renglón trae el tipo de cambio —que es lo que separa un menor, con API nueva y migración posible, del ruido de parches— y la primera línea de la primera entrada. Ninguna de estas rompe: las que rompen están todas arriba, con cuerpo. Las 163 tienen ancla propia, esté su cuerpo desplegado o no: /kit/changelog#v5-8-1 lleva a la suya.

Línea 4.x30 versiones

  1. 4.22.1ParcheEl toast informa, no bloquea: deja de comerse los toques de lo que hay debajo.
  2. 4.22.0MenorSelect y SelectComposition: se anuncian como desplegables.
  3. 4.21.0MenoruseQrCode acepta loader — el QR vuelve a generarse en el browser
  4. 4.20.0MenorFormSheet expone size (ADR-264)
  5. 4.19.0Menorfix(ui-kit): KPICardGrid.icon string resuelve por inyección, no React.lazy — el React.lazy de 4.18.0 costaba ~665 KB…
  6. 4.18.0Menorfix(ui-kit): aísla LucideIcon (mapa completo de lucide-react) del barrel raíz y de KPICardGrid — ADR-194 reincidencia
  7. 4.17.0MenormanageHistory en TODOS los overlays que consumen useModalStack (ADR-262, alcance final)
  8. 4.16.0MenorSheet, FormSheet y SheetContent ganan manageHistory?: boolean (default true). useModalStack — usado por…
  9. 4.15.0MenorPopoverTrigger asChild compone el ref del hijo en vez de pisarlo. Hasta 4.14.0, <PopoverTrigger asChild><button ref={miRef}…
  10. 4.14.0MenorQuantityStepper: nueva prop editable que vuelve tipeable el número del medio (draft local, commit en blur/Enter clampeado a…
  11. 4.13.0MenorInlineStat / InlineStatGroup — cifra en línea: ícono + valor + etiqueta en el mismo renglón, sin card ni padding. Es la forma…
  12. 4.12.0MenorDos correcciones de ancho que impedían que el contenido se achicara en pantallas angostas.
  13. 4.11.1Parchefix(sheet): el backdrop ya no captura clicks tras cerrar ni se autocierra por el gesto de apertura.
  14. 4.11.0MenorContextualSidebar gana la prop decorativeGlow?: boolean (default true). En false quita el halo…
  15. 4.10.1ParcheContextualSidebar: movimiento — motion-safe, despliegue animado y micro-interacciones
  16. 4.10.0MenorContextualSidebar: los 4 tipos del addendum v2 salen por el barrel público
  17. 4.9.0MenorContextualSidebar: decorativeAccent acepta accent-5 y accent-6
  18. 4.8.0MenorContextualSidebar: 3er nivel (groups) + acento decorativo por sección (ADR-027 addendum v2/v3)
  19. 4.7.0MenorSeasonRangeCalendar — usability fixes for dated (punctual) dualPane ranges, confirmed against real production data where users…
  20. 4.6.1ParcheFix SeasonRangeCalendar dualPane dated (punctual) mode: navigating a pane's year past the year already stored in…
  21. 4.6.0MenorQuantityStepper: add optional size?: "sm" | "md" prop (default "md", unchanged behavior). "sm" renders compact h-6 buttons, h-3…
  22. 4.5.1ParcheFix low-contrast solid warning (amber) foreground: Badge, Button, and PercentageBadge now use…
  23. 4.5.0MenoruseCollapsibleSections gains an optional 3rd argument, options?: { persistKey?: string; forceOpenIds?: T[] } —…
  24. 4.4.0MenorSeasonRangeCalendar — two new opt-in props, both additive/backwards-compatible
  25. 4.3.0Menorfix(SeasonRangeCalendar): dated (punctual) context ranges are now year-aware
  26. 4.2.1ParcheFixed ConfirmDialog and AlertDialog stacking below Sheet/Dialog when opened on top of one.
  27. 4.2.0MenorSeasonRangeCalendar — two visual fixes reported from GestorSalon's real usage (a non-primary
  28. 4.1.0MenorSeasonRangeCalendar's value now accepts an optional year, alongside a new dual-pane layout —
  29. 4.0.2ParcheFixed an infinite re-render loop ("Maximum update depth exceeded") in useModalWithBack when
  30. 4.0.1Parchefix(ui-kit): clases de color muertas → cmpPreset válido (task 3165). 6 componentes usaban clases

Línea 3.x2 versiones

  1. 3.2.0MenorF2 tanda 2 (ADR-226): @deprecated de 48 símbolos de superficie muerta en archivos mixtos —
  2. 3.1.0MenorF2 (ADR-225): marca @deprecated la superficie muerta inequívoca del kit — 79 exports en 37 archivos 100% muertos (compat shims,…

Línea 2.x13 versiones

  1. 2.9.1ParcheSelectRoot (composition API): agregado className?: string, mergeado sobre el wrapper (cn("relative w-full", className)). Antes…
  2. 2.9.0MenorMultiSelect suma 3 props opt-in, retrocompatibles (sin usarlas, el DOM
  3. 2.8.0MenorGeoMap (@solu30/ui-kit/geo, ADR-186): nuevo soporte de estilo por punto
  4. 2.7.1ParcheFix: RadioGroupItem no pintaba el punto de selección — renderizaba
  5. 2.7.0Menorfeat(geo): new GeoMap component at isolated subpath @solu30/ui-kit/geo (ADR-182)
  6. 2.6.1ParchePulido visual de listas editables. ui-kit: ArrayItemsEditor acepta FieldDef.width: "xs" — inputs cortos (7rem) para campos…
  7. 2.6.0MenorTabs / DetailSheetTabs: new optional divider?: boolean prop (default true). divider={false} removes the full-width baseline…
  8. 2.5.0MenorTabs — safety-floor a11y fix (found by adversarial review of the elevated-variant change above, same release): the component…
  9. 2.4.0MenorNew SectionEyebrow component (@solu30/ui-kit root, display subpath). Small uppercase label rendered above a section heading —…
  10. 2.3.0MenorSeasonRangeCalendar — two changes:
  11. 2.2.0MenorBatch of 6 new/extended primitives from the RRHH review — ADR-165, gate redblue 2026-07-12 (GO WITH CHANGES).
  12. 2.1.0MenorAdd SeasonRangeCalendar — a monthly calendar-grid primitive for yearless, recurring "MM-DD" date ranges tinted by color…
  13. 2.0.1ParcheFix SelectValue (composition-mode Select) showing the raw controlled value instead of the matching SelectItem label while the…

Línea 1.x31 versiones

  1. 1.16.0MenorAdd <Markdown> component under its own subpath @solu30/ui-kit/markdown — promotes luigi's audited RRHH markdown renderer…
  2. 1.15.0MenorNew Combobox export: a creatable searchable dropdown that replaces the "plain Select + separate create button" pattern.…
  3. 1.14.0MenorDataTable: nuevo prop rowClassName?: (item, index) => string | undefined para estilar la fila completa (<tr>) según los datos…
  4. 1.13.3ParcheFix: <Tabs> (config-driven) and <TabsTrigger> (composition-mode) rendered
  5. 1.13.2ParcheSheet: side sheets ahora ocupan ancho completo en móvil (<640px); los caps max-w aplican desde sm:
  6. 1.13.1ParcheConfirmDialog: el dialog ahora lleva border border-content/10 para separarlo del backdrop en fondos oscuros. Cambio puramente…
  7. 1.13.0MenorContextualSidebar: dos props aditivos para estandarizar el shell en consumidores.
  8. 1.12.0MenorDataTable: paginación client-side vía nuevo prop pageSize. Cuando se pasa (y no hay pagination controlado ni serverFilter), el…
  9. 1.11.1ParcheFix: componentes que leían rgb(var(--color-primary)) / --color-content / --color-container / --color-bkg directamente (no vía…
  10. 1.11.0MenorCapacityBar gana direction?: "fill-is-bad" | "fill-is-good" (default "fill-is-bad", cero cambio de comportamiento para…
  11. 1.10.2ParcheSidebarUserMenu: corrige alineación de slots (quita doble-padding en las filas de theme/language toggle, que quedaban…
  12. 1.10.1Parchefix(SidebarUserMenu): ancho legible del panel en rail colapsado. El popover heredaba el ancho del trigger (~40px en sidebar…
  13. 1.10.0MenorSidebarUserMenu trigger gains an affordance chevron and a more perceptible hover state (ADR-087 follow-up, additive).
  14. 1.9.2ParcheCollapsibleSection: fix WCAG 2.4.3 focus-order violation — collapsed content was hidden only via CSS (grid-template-rows: 0fr +…
  15. 1.9.1ParcheLanguageSwitcher: add compact?: boolean prop for icon-only trigger in collapsed sidebars. When true, the trigger renders only…
  16. 1.9.0MenorAdd optional disabled?: boolean prop to TagInput and ArrayItemsEditor.
  17. 1.8.1ParcheSidebarNavigation: fix hydration id drift on the footer slot's Radix components (e.g. a DropdownMenu). The component renders a…
  18. 1.8.0MenorAdd EmojiPicker — a WhatsApp-style emoji picker (search + virtualized categories) for chat/wall composers. Built on the…
  19. 1.7.2ParcheCellPrice: add format="money" prop (focus-swap es-AR formatting, select-all on focus) + onChange callback for live preview.…
  20. 1.7.1ParcheChipPill: convert to forwardRef + spread extra props for Radix asChild trigger support.
  21. 1.7.0Menorfeat(ui-kit): add DropdownMenuCheckboxItem — switch-style, multi-select friendly
  22. 1.6.1Parchefix(Modal): guarantee unmount when isOpen transitions to false regardless of onAnimationEnd
  23. 1.6.0Menorfeat(charts): ChartInfoPopover + BarChartItem.href navegable (PROP-07 Gate A)
  24. 1.5.0Menorfeat(inline-fields): add plain prop to CellPrice, InlineTextField, InlineNumberField, InlineMarkupField
  25. 1.4.1Parchefix(SegmentedControl): sube contraste del segmento inactivo de text-content/60 a text-content/80
  26. 1.4.0Menorfix(overlays): portal + viewport-clamp para Select, MultiSelect, SelectComposition, LanguageSwitcher, FilterableHeader,…
  27. 1.3.1ParcheFix "Can't resolve 'lucide-react/dynamic'" under Next 16 / Turbopack
  28. 1.3.0Menorfeat(SidebarNavigation): add labelClassName to SidebarNavGroup
  29. 1.2.0Menorfeat(navigation): AppLayout — shell de aplicación estándar del ecosistema CMP (ADR-070)
  30. 1.1.0MenorAñade aislamiento lazy de lucide-react/dynamic para eliminar over-bundling en Turbopack (ADR-194).
  31. 1.0.1ParcheAdd tone?: 'default' | 'comingSoon' prop to ContextualSidebarItem and ContextualSidebarSection.

Línea 0.x70 versiones

  1. 0.79.1Parchefix(inline-fields): hardening round 2 — controlled draft pattern extended to InlineNumberField; CellPrice draft now seeds on…
  2. 0.79.0Menorfeat(BookingCalendar): alwaysScroll prop + scroll-to-anchor on mount (ADR-044)
  3. 0.78.0MenorSparkline: add 6 optional props (backward-compatible, ADR-035)
  4. 0.77.2Parchefix(tokens): replace residual no-op shadcn color tokens with cmpPreset equivalents (token-migration v2)
  5. 0.77.1Parchefix: migrate undefined token family to cmpPreset canonical tokens
  6. 0.77.0MenorAdd ContextualSidebar — domain-agnostic collapsible sidebar primitive (ADR-027). Supports L1 accordion sections, L2 contextual…
  7. 0.76.0MenorAdd AnalyticsTreemap to @solu30/ui-kit/charts subpath.
  8. 0.75.0Menorfeat(charts): add recharts-backed ChartLine, ChartArea, ChartBar, ChartPie, ChartCombo components
  9. 0.74.0MenorCommandInput / Command — controlled mode (MINOR, additive, 100% retrocompat).
  10. 0.73.0Menorfeat(inline-edit): InlineEditText + InlineEditTextarea — hover-to-reveal pencil edit pattern.
  11. 0.72.1ParcheFix token muerto en STATUS_COLORS.neutral + inicio del doc-set (task 3159, B0+B1).
  12. 0.72.0ParcheADR-003: reemplaza tokens shadcn muertos por tokens canónicos del cmpPreset (Tailwind v4)
  13. 0.71.13Parchefix(Modal): publish the stacked-modal scroll fix (0.71.12 was published from a stale build without the touchmove change).…
  14. 0.71.12Parchefix(Modal): fix stacked-modal scroll on iOS. The touchmove handler that blocks background scroll now uses…
  15. 0.71.10ParcheAdd useServerSyncedState hook (from @solu30/ui-kit/hooks): encapsulates the dirty-flag + sync-in-render + in-flight-mutation…
  16. 0.71.9Parchefix(CurrencyInput): wire the previously-ignored locale prop into formatting and add a symbol prop (default '$') so non-ARS…
  17. 0.71.8ParcheAdd SegmentedControl: generic segmented control with unified bordered container, icon + optional text labels, sizes (xs/sm/md)…
  18. 0.71.5Parchefix(z-index): z-layer-\ tokens → arbitrary z-[N] values — modales/overlays ahora self-contained, ya no dependen del preset del…
  19. 0.71.4Parchefix(Popover): anchor within Sheet animating at 0,0 — two-part fix:
  20. 0.71.1ParcheMinor: add optional label and icon props to InlineSelect for labeled inline dropdowns. Backward compatible — without label,…
  21. 0.71.0MenorInlineSelect: add optional label and icon props for labeled inline dropdowns. Restores the InlineEditableDropdown ergonomics…
  22. 0.70.0MenorRestore ColorPicker: color swatch radiogroup with sm/md sizes
  23. 0.66.4MenorFix CurrencyInput losing focus and caret position on rapid typing under React 19. The previous implementation used an inline…
  24. 0.64.0MenorAdd successMessage/errorMessage toast props to useSheetForm hook
  25. 0.63.0MenorAdd canonical status/intent token helpers and Tailwind v4 ui- intent color aliases for package-first visual migrations.
  26. 0.62.0MenorAdd SM2_RATING_META and MicroToast barrel exports.
  27. 0.55.0MenorAdd preference-form section icons (IconDietary, IconAllergies, IconActivities, IconOccasion, IconFlight, IconEmergencyContact,…
  28. 0.52.0Menorfeat(ui-kit): add FilterableSection component — extracted from TheCharterPanel PreferencesTab
  29. 0.50.1MenorFix PageShell rendering when used without header content and allow content overflow.
  30. 0.49.0MenorAdd ImageUpload, ImagePickerDialog, and ImageUrlInput components under src/components/image-upload/.
  31. 0.48.0MenorAdd AddressAutocomplete component — debounced address input backed by Nominatim (OpenStreetMap, no API key). Returns…
  32. 0.47.0MenorAdd FloatingPanel component — desktop floating card (glass, fixed position) with full-screen mobile fallback
  33. 0.46.0MenorCheckbox: add async/optimistic onChange, icon slot, isLoading, and description props
  34. 0.45.0 (2026-05-17)Menorframer-motion removed from 12 components (only Tabs, TabbedPanel, MotionSection retain it)
  35. 0.44.0MenorAdd inline-fields and variant management components (v0.43.0)
  36. 0.42.0Menorfeat(icons): add IconRss to social icons — extracted from ecosystem BlogListPage
  37. 0.41.3 - 2026-05-16ParcheuseModalWithBack(isOpen, onClose, options?) hook for stacked modal back-button support on mobile.
  38. 0.41.0MenorExport snapToCorner, getCornerStyle, Corner, CornerOffsets from main barrel.
  39. 0.39.0MenorAdd onSliceClick, onBarClick, onPointClick click handler props to AnalyticsPieChart, AnalyticsBarChart, and AnalyticsLineChart…
  40. 0.38.0MenorAdd KPICardGrid component with responsive grid (2–6 columns), Lucide icons, semantic color tokens, skeleton loading state, and…
  41. 0.37.2ParcheFix hydration mismatch in DataTable skeleton rows — replace Math.random() with deterministic seeded widths.
  42. 0.37.0MenorAnalyticsLineChart: add multi-series mode (series prop) with zoom/pan and multi-tooltip. Backwards compatible — data prop still…
  43. 0.36.0MenorAdd AnalyticsPieChart, PeriodSelector to ui-kit; add BalanceProjectionCards to billing/cashflow.
  44. 0.35.1Parchefix: wrap all inline style var(--color-_) with rgb() for proper CSS color resolution. Consumers that define --color-_ as raw…
  45. 0.35.0MenorAdd ColorPicker component — color swatch grid with radiogroup accessibility, sm/md sizes.
  46. 0.34.0MenorAdd DropZone, MasonryGrid, and FilePreviewModal components for media/file management UIs.
  47. 0.33.0MenorAdd runConcurrentQueue utility to hooks — generic bounded concurrent queue runner.
  48. 0.32.0MenorAdd runConcurrentQueue utility to hooks — generic bounded concurrent queue runner.
  49. 0.31.0MenorAdd ./editor subpath: generic rich template editor with variable chip support.
  50. 0.30.1MenorAdd translationFn prop to ActivityTimeline for i18n support on action labels and status change badges.
  51. 0.29.7ParcheFix TabbedPanel scroll arrows z-index from z-10 to z-20 so they render above tab text content.
  52. 0.29.5ParcheFix ESM import error: next/link → next/link.js in LinkPendingIndicator
  53. 0.29.0MenorAdd TagListInput and ArrayItemsEditor components — extracted from dietaly enrichment forms.
  54. 0.19.0MenorAdd TabbedPanel (Composition API) and Toolbar components.
  55. 0.18.0MenorAdd GradientText component — generic gradient text wrapper accepting from/to Tailwind color class props and optional as…
  56. 0.17.3ParcheAgrega DataTableShell: contenedor estandarizado para tablas con overflow horizontal, toolbar y footer slots. Agrega StatRow:…
  57. 0.17.2ParcheAgrega FormSheet y FormSection: panel lateral para formularios con header de gradiente, footer con acciones, y sección…
  58. 0.17.1ParcheRouteTabs: agrega flechas de scroll horizontal con ResizeObserver. Acepta icon como ComponentType o ReactNode.
  59. 0.17.0MenorAgrega ScrollableTabsBar: barra de tabs con scroll horizontal y flechas de navegación dinámicas via ResizeObserver. Actualiza…
  60. 0.15.9ParcheFix DataTable borders: use --ui-border token (defined in every project's globals.css) instead of --color-upper/--color-content…
  61. 0.15.8ParcheFix DataTable borders: use --color-upper token instead of --color-content for all border colors. Gives subtle borders in dark…
  62. 0.15.7ParcheFix remaining DataTable border colors: pagination divider, RangeFilter clear button divider, and TextFilter clear button…
  63. 0.15.6ParcheFix all DataTable border colors: use color-mix 15% opacity instead of solid --color-content for subtle borders across table…
  64. 0.15.5ParcheFix DataTable row borders using color-mix for subtle appearance instead of solid --color-content.
  65. 0.15.1ParcheFix FormProvider barrel optimization: use local assignment instead of
  66. 0.15.0Menorfeat: add InlineNumber, InlineColor, InlineTime, StaticSection, SectionTileGrid components
  67. 0.14.0Menor058cc38 Thanks @EasycharterDev! - Extract 4 Tier-3 presentational components from GestorSalon.
  68. 0.13.0MenorAdd LucideIcon and ThemeToggle components extracted from GestorSalon.
  69. 0.12.0MenorMerge hooks-common, cloudflare-images, and analytics-charts into ui-kit.
  70. 0.11.0InicialCentralize date formatting utilities into @solu30/ui-kit.

Leído del CHANGELOG.md que viaja adentro de @solu30/ui-kit 5.8.1, el paquete instalado en este repositorio · lo escribe changesets al publicar · esta página no guarda copia. El paquete trae además un BREAKING-CHANGES.md que acá no se usa: su última entrada es la 0.45.0 y no documenta ninguno de los cinco saltos de mayor posteriores.