Novedades
Lo que llegó en cada versión, sacado del registro de cambios (CHANGELOG.md) que viaja en el paquete de la 0.17.1. Una entrada marcada Rompe cambia algo que tu código tiene que tocar al actualizar: npx amset-ui migrate se encarga de casi todo.
0.17.1, del 19 de octubre de 2026
La 0.17.0 se publicó desde un pipeline depreanterior al arreglo deFlag, y salió con ese fallo: con SSR, la portada y cualquier página con una bandera daban el error 418 al hidratar. Si usasFlagcon un servidor, sube a esta.
Flagya no rompe la hidratación cuando el servidor ya tenía su región (AMS-1894). Un build estático pinta cientos de páginas en el mismo proceso, y desde la segunda la región de banderas ya estaba en la caché del servidor:Flagla pintaba sin frontera deSuspense, el navegador (que empieza de cero) la pintaba con ella, y React daba el error 418 al hidratar. Pasaba en la portada de amset.tech y en cada página con el selector de idioma. Ahora la frontera va siempre y el navegador conserva lo del servidor hasta que llega la región; lo prueba una hidratación real con el servidor caliente y el navegador frío.migrateydoctorven las hojas de antes importadas desde CSS (AMS-1912).migratereescribe también los@import '@amset/ui/estilos.css'yestilos/<hoja>.cssde tus .css, ydoctorda error si el CSS global importa una hoja que ya no existe (antes decía que no importabas ninguna).doctorcuenta ahora los mismos nombres quemigratecambia, también los de la 0.16, y señaladata-tema, que la biblioteca ya no lee.npx amset-ui create: una aplicación nueva con la biblioteca ya puesta (AMS-1786). Dos plantillas enstarters/: Next con App Router (la de por defecto) y Vite con--template vite. TypeScript estricto y ESLint de serie, sin Tailwind, el armazón (AppShell,TopBar,ThemeButtonySideNavmarcando la página actual) y cada página en su propio fichero: en Next, páginas de servidor; en Vite,React.lazyy la carga adelantada al pasar por el enlace. Después pasainit, así que salen con el.npmrc, el tema del color de--brandy el proveedor, ydoctorno tiene nada que decir. No escribe en una carpeta con algo dentro.npm run consumerscrea los dos con el CLI del paquete empaquetado y comprueba que se instalan, pasan sus tipos y su lint, compilan, navegan sin recargar y caben en su tope de JS (medido: 150 KB gzip en Next y 85 KB en Vite).- Bloques: siete pantallas enteras que
npx amset-ui addcopia al proyecto (AMS-1786). Panel de gestión (dashboard), listado de clientes (customer-list), alta de cliente (customer-form, con su esquemacustomerSchemapara validar también en el servidor), ficha de cliente (customer-detail), emisión de factura (invoice-form), ajustes (settings) e inicio de sesión (sign-in). Hechos solo con componentes de la biblioteca: importan@amset/uicomo tu proyecto y no copian código de ningún componente, así que no pesan nada.npx amset-ui list --blockslos enseña;addavisa de lo que necesitan y tu proyecto no tiene. En Storybook, en Bloques, con sus estados en claro, oscuro y móvil, y traducidos al inglés.scripts/blocks.test.tsvigila que solo importan la biblioteca, que no escriben ni un color ni una medida, y que los siete copiados compilan en estricto. useLocale()se exporta: el idioma del proveedor ({ locale, direction }) para formatear una fecha o un importe propio igual que la biblioteca. Lo necesitaban los bloques y cualquier aplicación que escribe sus propias cifras.TablecononRowPress: la fila se abre también con Intro. La fila ya se enfocaba con las flechas, pero solo respondía al clic: con el teclado no había forma de abrir una factura del panel. Intro sobre un control de dentro de la fila sigue siendo de ese control.
0.17.0, del 19 de octubre de 2026
La 0.15.0 y la 0.16.0 no llegaron a publicarse: esta es la primera versión publicada desde la 0.14.0, así que quien venga de la 0.14 recibe de golpe lo de las tres secciones (la 0.15.0, la 0.16.0 y esta).
- Los componentes de IA, configurables para meterlos en el chat del asistente. Lo que el chat real de AMSET hace y las piezas no dejaban configurar:
PromptInputadmitebusyModes(seguir escribiendo mientras responde -añadir al turno, parar y corregir, encolar-: radios de verdad sobre el campo yonSubmitrecibe la forma elegida en{ mode }; «Parar» pasa a estar junto a «Enviar»),inputRef,actionsa la derecha,onPasteText(un texto enorme se adjunta como fichero en vez de pegarse),onInputKeyDownpara atajos propios,inputProps(corrector, idioma, tope de longitud) yattachDisabled.ChatMessageganaactions(acciones propias junto a copiar, regenerar y editar),actionsVisibility,editor(editar en la misma burbuja) yhighlighted(coincidencia de una búsqueda, con un aro fijo y no una animación).ActivityLogganagrouppor evento (un historial de varias tareas, con título por sección y sin juntar lo igual de dos secciones),renderTextyloading. Y un componente nuevo,FindBar: la barra de buscar dentro de una conversación larga, con contador, siguiente, anterior y cerrar (Intro, Mayús+Intro, Escape). El segundo argumento deonSubmites nuevo pero no rompe a nadie; 36 pruebas con teclado y foco reales. - Las piezas de IA igualan al chat real del asistente (AMS-1773). Se leyó el chat de AMSET pieza a pieza y se añadió lo que hacía y la biblioteca no:
PromptInputganapastedFileName, un freno contra el rebote del doble envío (el mismo texto dos veces en 0,3 s),busyModeScope,history(flechas arriba y abajo, con el cursor al final) ysubmitOn="auto";Attachmentganaicon,detail,onPreview,compacty el anuncio de «Subido»;ThumbnailganaonPress,loading,errorycaption; nuevosImageViewer(el visor a pantalla casi completa, un diálogo de verdad) y los ganchosuseBlobUrlyuseElapsed;ArtifactCardganaonRetry;ModelSelector,maxFixes;Conversationganaloading,loadingOlder,onReachStart(mantiene el sitio de lectura al llegar mensajes arriba),stickToBottom,revealKey,highlightedyemptycomo nodo;ReasoningyActivityLogcuentan el tiempo en vivo (startedAt,endedAt,label,emptyIcon);Suggestions,allowPartial;UsageBadge,cachedTokens;FindBar,hasMore. Todo opcional: nadie que ya los use cambia. Dejado fuera a propósito: la vista previa de PDF (uniframesinsandboxabriría la puerta a ejecutar HTML ajeno; falta una pieza que solo admita PDF) y el markdown (sin dependencias en el núcleo). - Dos fichas ya no comparten título (AMS-1783): el campo de importe pasa de «Importe» a «Campo de importe» y el de tarjeta de «Tarjeta» a «Campo de tarjeta», como «Campo de texto» o «Campo numérico». Los ids de las fichas no cambian, y
scripts/docs.test.tsfalla si dos fichas vuelven a repetir título. - Los umbrales de cobertura suben a lo medido (AMS-1763): de 80/80/75/80 a 95/94/86/92 (líneas, funciones, ramas, sentencias). Pruebas nuevas para el arrastre de filas, el tirador de ancho de columna, el teclado de la rejilla y los estados de interacción, que eran lo más flojo (entre un 12 % y un 42 %).
- Rompe Rompe: tres clases raíz con el nombre del componente que de verdad las usa (AMS-814).
.amset-code(el campo de código de un solo uso) pasa a.amset-code-fieldcon todos sus hijos y modificadores,.amset-heading(el encabezado de página) a.amset-page-header, y.amset-list(la lista del panel de los desplegables) a.amset-listbox. Las variables--amset-code-*y--amset-heading-*no cambian. Quien estilara esas clases desde fuera tiene que usar los nombres nuevos. - El menú de acciones busca escribiendo con el buscador de la biblioteca (AMS-1200):
useTypeaheadya acepta una función para leer las claves del DOM y el menú dejó de llevar su copia. Sigue encontrando «Éxito» con «e» y dejando el espacio como parte de lo escrito. - La campana de la bandeja y el botón de abrir del buscador y del selector múltiple usan
amset-trigger-open(AMS-1293): se borran las dos copias de las reglas de «disparador con el panel abierto». Flagya no mete las 244 banderas en el JavaScript de la página (AMS-1893). Los dibujos van por regiones (Europa, América, África, Asia, Oceanía), cada una en un fichero que se pide cuando se pinta una de sus banderas: lo que cuenta deFlagpasa de unos 17,6 KB comprimidos de JS a unos 1,5 KB. Mientras llega el dibujo se enseñan las dos letras, y un servidor que espera a la suspensión (Next,renderToPipeableStream) ya trae la bandera en el HTML. NuevopreloadFlags()para tenerla en el primer pintado de un cliente. El presupuesto del paquete gana el escenarioFlagconstaticOnly, que solo cuenta lo que se descarga por imports estáticos.Table: la cabecera de fila (rowHeader) ya no sale centrada (AMS-1884). El<th>heredaba el centrado del navegador; ahora alinea al inicio y en negrita como el resto de celdas, yalign="end"/"center"siguen mandando.Pagination: unsizeque no está en la lista de tamaños ya se ve en el selector (AMS-1883). Antes el selector salía vacío o cortado; ahora el tamaño se añade a las opciones, sin duplicados y ordenado.RadarChart: la marca del máximo ya no pisa el nombre del eje de arriba (AMS-1895). Cuelga por debajo del contorno exterior.- La prueba de hidratación de
RelativeTimeya no depende de la zona horaria de la máquina (AMS-1896): fija eltimeZonepor defecto deIntl.DateTimeFormat, porque cambiarprocess.env.TZno vale en los hilos de Vitest. - Todo el código está en inglés: ni una variable, función, parámetro, propiedad ni tipo en castellano (AMS-1887). Se renombraron unos 3.400 nombres distintos (más de 37.000 usos en 800 ficheros) con el LanguageService de TypeScript, no con sustituciones de texto:
claveDeFila→rowKey,esClicDeEnrutador→isRouterClick,alPulsarFila→onPressRow,ordenable→sortable. Lo que un consumidor puede notar:bin/proyecto.mjspasa abin/project.mjs;dist/fuentes/(lo que leeamset-ui add) pasa adist/sources/; los ids de los hallazgos deamset-ui doctorvan en inglés (registry,layer-order,legacy-names,provider,router,project,installed,theme-not-imported); el estado de cada fichero deaddy deinitesnew/equal/overridden/kept/modified;AppShellusacontentcomoidpor defecto del<main>; el atributodata-photodeAvatarvaleloading/loaded.amset-ui migratesigue buscando los nombres antiguos (escritos como cadenas enbin/migrations.mjs); su lista teníainitialsValuesen vez devaloresInicialesy no encontraba esa prop, ya corregido. Lo vigilascripts/identifiers-english.test.ts, que recorre el árbol de sintaxis de todo el repositorio. LocaleProviderya no arrastra el selector de fechas (AMS-1885). Estaba definido dentro dedate-picker.tsx, así que cualquier aplicación que lo montara -que es todas, en todas sus páginas- se llevaba la hojadate.css(4,6 KB gzip, medido con Vite) y, en un build de Next con Turbopack, además el calendario, los trozos editables y el selector entero: ~45 KB sin comprimir en una portada que no pintaba ni una fecha. Pasa a su propio módulo,components/locale-provider.tsx. No cambia la API:LocaleProvider,LocaleProviderPropsyDEFAULT_LOCALEsalen del mismo índice y de las mismas subrutas. Medido en amset.tech: el JavaScript inicial baja 10 KB gzip y el de la biblioteca de 144 a 116 KB sin comprimir. Lo vigilansrc/locale-provider-weight.test.ts(el grafo de imports del proveedor no puede tocar fechas ni capas flotantes) y un escenario nuevo del presupuesto de peso,LocaleProvidercon 2,2 KB, que cuenta solo lo estático: el catálogo de claves que el proveedor pide conimport()en desarrollo son 20 KB que nadie descarga en producción y taparían un cambio de unos pocos KB.RichTextToolbaryToggleButton: la barra de un editor de texto enriquecido, sin meter el editor en la biblioteca (AMS-1787). La barra habla con el editor porstate(lo que hay puesto donde está el cursor) yonCommand, así que vale con Tiptap, con Lexical o con cualquier otro y@amset/uisigue sin dependencias. Es unaToolbar(una parada del tabulador, flechas, memoria del foco) con unToggleButtonpor formato (aria-pressed, atajo enaria-keyshortcutsy en la ayuda, Control o Comando según el sistema), grupos con nombre, enlace e imagen con paneles que solo se montan al abrirlos y que no son un<form>(vale dentro del formulario de la aplicación), y pulsar con el ratón no le quita el foco al editor. Las direcciones se comprueban connormalizeLinkHrefynormalizeImageSrc, que se exportan: lista blanca de esquemas, unjavascript:o undata:no pasan. La imagen pide texto alternativo o marcarla como decorativa (alt=""). Hojas nuevastoggle-button.cssyrich-text.css, con el marco del editor (.amset-richtext__frame) y la tipografía de lo escrito (.amset-richtext__content). Guía en inglés endocs/rich-text-editor.mdcon las dos recetas enteras (sobre el núcleo de cada editor, sin@tiptap/reactni@lexical/react, que arrastran@floating-ui), comprobadas por el compilador y atadas a la guía por una prueba, y un checklist para la pasada manual con NVDA y VoiceOver deTableeditable,Combobox,InvoiceLinesEditory la propia barra. Tiptap y Lexical entran solo como dependencias de desarrollo del Storybook, donde dos historias los ejecutan de verdad.WeekAgendaya no rompe la hidratación en inglés (AMS-1894). La cabecera de la semana sale deformatRange(«Oct 11 – 17, 2026»), y el ICU de Node escribe espacios finos (U+2009) alrededor de la raya donde Chrome pone espacios normales: pintada en el servidor, la agenda no casaba al hidratar y React tiraba el HTML de la página entera (error 418, medido en amset.tech, solo en inglés porque en castellano ese formato no lleva espacios). El calendario, los trozos de fecha y el cronograma ya lo normalizaban, cada uno con su copia; ahora los cuatro pasan porutils/plain-spaces.ts. Lo fija una prueba deweek-agenda.test.tsxque pinta la agenda enen-US(comprobado que falla sin el arreglo).- Decidido:
onPresssigue siendo el evento de pulsar, eisDateUnavailableeisLeafconservan su «is» (AMS-1762). Primer registro de decisiones del repositorio, endocs/adr/(en inglés):onPresses el nombre que ya escriben todos los fronts y une ratón, teclado y dedo en un solo evento;onClickse queda solo como puerta de migración en la familia del botón y enTextButton, siempre junto aonPressy con elMouseEventnativo (AMS-735).isDateUnavailablees un predicado, no un estado, eisLeafes la marca de tres estados de la carga diferida. No cambia ni una prop.prop-names.test.tsdeja de tolerarlos en silencio: los lista como excepciones con enlace al registro y falla con cualquier otro «is» o con unonClickpropio sinonPress. npx amset-ui migratecubre también lo que la 0.16 pasó al inglés (AMS-1894). Se escribió para lo que retiró la 0.14 y no sabía nada de la tabla de la 0.16; migrando amset.tech salieron los huecos. Ahora reescribe las 133 hojas por componente (@amset/ui/estilos/boton.css→@amset/ui/styles/button.css, también importadas sueltas),estilos.css,fichas.jsonymandos.json,Tooltip para→trigger,PasswordField nueva→newPasswordyerrorDeImporte→amountError. La tabla de hojas se emparejó por CONTENIDO, comparando las reglas de cada hoja de las dos versiones publicadas: las 133 tienen destino y ninguna cae en la misma que otra. Arreglado de paso: el codemod solo miraba las props deTableyFormescritas en el código, así que una entrada nueva enPROP_RENAMESse ignoraba sin avisar; ahora manda la tabla. Lo que sigue siendo a mano: las claveslinea1/linea2de una dirección (el compilador ya dice «¿quisiste decirline1?») y los atributosdata-tema/data-densidaddel HTML, que pueden ser de la propia aplicación.- Storybook: las descripciones de los controles dejan de usar nombres viejos y la guía de tallas ya enseña la columna «Largo» (AMS-1667).
SlotPickerdecíahoradonde la prop estime,WeeklyScheduleyRangeFilterdecían{ desde, to }donde es{ from, to }yclosedSchedule()salía comohorarioCerrado(); y las historias deSizeGuidedeclaraban la medidalargopero daban los valores bajo la clavelong, así que la columna salía vacía. Solo documentación e historias: sin cambios de API. BarChart: marcas de referencia y una segunda cifra por barra (AMS-1818).markersdibuja líneas que cruzan todas las barras a su valor (la media del grupo, un objetivo), en columnas y en filas: entran en el techo del eje, salen en la tabla con su nombre y su valor y no cuentan en el resumen del dibujo; la capa mide lo mismo que el sitio donde crecen las barras, así que la línea al 62 % cae exactamente donde acabaría una barra de 62 (medido a 0 px).DataBar.detailpone una segunda cifra visible detrás del número de la barra («+12 pts»), solo en filas, donde hay sitio; en columnas se queda en la tabla, junto a la nota. Nuevas variables--amset-bars-namey--amset-bars-row-gap. Lo que sigue pendiente de ese ticket (barra apilada acierto/blanco/fallo y el detalle por punto enLineChart) es API nueva y queda aparte.MonthAgenda: los eventos se pueden pulsar (AMS-1817).AgendaEventadmitehref(el evento de la lista del día es un enlace, que pasa por elRouterProviderigual queLink) yMonthAgendaadmiteonEventPress(key): sinhref, el evento pasa a ser un botón que avisa de su clave; conhref, avisa además al seguir el enlace. La tarjeta se ve igual y entera es pulsable (foco de 2 px, borde del color del evento al posar el ratón). Sin ninguno de los dos, el evento sigue siendo texto. Lo que sigue pendiente de ese ticket (rejilla de mes con títulos dentro de cada día y vista de agenda por rango) es API nueva y queda aparte.Table: el selector de filas por página ya no se corta, y la columnarowHeaderse alinea al inicio (AMS-1819). El selector medía 4,5 rem fijos y con una cifra más ancha salía «5…»: ahora es un mínimo y crece con su valor. Unth scope="row"lo centra el navegador, así que una columnarowHeaderquedaba centrada bajo una cabecera a la izquierda; la biblioteca dice ahoratext-align: startpara todas las celdasthque no sonendnicenter. Y la barra libre del toolbar (.amset-table__bar-free) ya no se encoge sin suelo hasta cortar un toolbar propio en 390 px: tiene un mínimo y baja de línea.PhoneField: el número cabe con prefijos largos en un campo estrecho (AMS-869). Con +351 en un campolgde 224 px el último dígito se salía 7,4 px; el disparador del prefijo gasta ahora 12 px menos (menos aire entre la bandera, el prefijo y la flecha: 97,2 → 85,2 px medidos) y el número tienemin-width: 0.- Ficha propia para 14 componentes que solo salían dentro de la de su grupo (AMS-1898).
AreaChart,StackedBarChart,Grid,Container,Center,ScrollArea,AspectRatio,Kbd,Mark,Highlight,Blockquote,ColorPanel,ToggleChipGroupyUploadertenían API pública y no tenían página, ni entrada en el índice, ni búsqueda por su nombre: ahora cada uno tiene la suya en Storybook y endocs.json(202 fichas), con su historia «Interactivo», su texto en inglés y su teclado y su accesibilidad medidos en un navegador.Uploaderno salía descrito en ningún sitio. Las fichas deStack,Code(ahora «Código en línea»),LineChart,ColorPicker,ToggleChipyFileUploaddejan de contar lo de las otras y las enlazan. De paso:Blockquoteconciteperdía los atributos que se le pasaban (ahora van al<figure>); enUploaderlos atributos,classNameystylecaían en la zona de dentro y no en la caja, y al quitar el último fichero el foco se iba albodyy el aviso se perdía (ahora vuelve al botón de elegir y se anuncia); el tirador del área de color se colocaba contra el flotante y no contra el área (52 px desviado enColorPicker, fuera del panel conColorPanelsuelto), y ahora cae donde dice su valor; en la tira de tono, Fin (o arrastrar hasta el borde) llevaba el tirador al principio porque 360° es 0°, y ahora acaba en 359°;ToggleChipGroupyUploaderestrenan--amset-chip-group-gapy--amset-uploader-gap; y el Storybook vuelve a construir, que importabaLocaleProviderde su sitio viejo. - Las capas flotantes se recolocan al cargar la tipografía (AMS-1176). Un reflujo que mueve el disparador sin cambiarle el tamaño (la fuente web que acaba de llegar) no disparaba ni el scroll, ni el resize, ni el observador del disparador, y la capa se quedaba con la medida vieja hasta el primer resize. Ahora se vuelve a colocar cuando resuelve
document.fonts.readyy cuando cambia el tamaño delbody. - Hidratar una pantalla con
DatePickercuesta un tercio: el panel cerrado ya no monta su calendario (AMS-1886).FloatingDialog-el panel del selector de fecha, del rango, del color, de los menús, delDropdowny de los filtros de la tabla- pintaba su contenido también cerrado, así que cadaDatePickermontaba un mes entero que nadie veía, y cada día creaba sus formateadores deIntl(calendarOf, la fecha larga, el número del día). Ahora el contenido se monta al abrir y se desmonta al acabar la salida, y los formateadores se crean una vez por idioma (utils/intl-cache.ts). Medido con el benchmark de apps (CPU ×4, mediana de 15): la pantalla de pedidos hidrata en 150 ms en vez de 264, a la par que Mantine (149) y por delante de MUI (242) y antd (352); el selector de fecha cerrado pasa de sumar ~250 ms a ~40. Aviso: quien buscase en una prueba algo de dentro de un panel cerrado ya no lo encuentra hasta abrirlo. - Rompe Rompe: TODA la API que se toca desde fuera pasa al inglés. AMS-550 tradujo componentes y props, pero quedaban en castellano funciones, atributos, ficheros, subrutas y la CLI, y los ejemplos de la referencia seguían con la API de antes (
<Boton variante="solido">). Qué cambia al subir de versión:Antes Ahora @amset/ui/estilos.css,@amset/ui/estilos/<hoja>.css@amset/ui/styles.css,@amset/ui/styles/<sheet>.css(las hojas, también en inglés:boton.css→button.css)@amset/ui/formulario,@amset/ui/esquemas@amset/ui/form,@amset/ui/schemasfichas.json,mandos.json,ejemplos.json,idiomas/*.jsondocs.json,knobs.json,examples.json,locales/*.json(y sus claves:component,module,title,family,description,stories,name,knobs,story,code)npx amset-ui tema,npx amset-ui mandos,npx amset-ui migrar,amset-tema.cssnpx amset-ui theme,npx amset-ui knobs,npx amset-ui migrate,amset-theme.css;addescribestyles/ystyles.cssdata-tema,data-densidaddata-theme,data-densitydata-cargando,data-seleccionada,data-deshabilitada(Card) y el resto dedata-*en castellanodata-loading,data-selected,data-disabled… (data-actual→data-current,data-sin-leer→data-unread,data-nivel→data-level…)@layer amset-ui.componentes@layer amset-ui.componentsClases amset-no-imprimir,amset-only-impresion,amset-for-imprimir,amset-card-pago*,amset-motion-esencial,amset-order-state__hitoamset-print-hidden,amset-print-only,amset-printable,amset-payment-card*,amset-motion-essential,amset-order-state__milestone(y--amset-order-state-milestone-gap)Animaciones amset-*-entra,-entrar,-salir,-aparece,amset-progress-va-y-viene,amset-rating-marcar-enter,-exit,-appear,amset-progress-back-and-forth,amset-rating-markcsvDeTabla,descargarCsv,separadorPorDefecto,filtrarFilas,ordenarFilas,paginarFilas,opcionesDeColumna,valorDeCeldatableToCsv,downloadCsv,defaultSeparator,filterRows,sortRows,paginateRows,columnOptions,cellValuecalcularFactura,errorDeImportecalculateInvoice,amountError; yrejectionReason/FileLimitsdeFileUpload, que el README daba por exportados y no lo estabanTooltip para,AddressFieldlinea1/linea2,parseHotkey(...).teclaTooltip trigger,line1/line2,.keyPasswordField nuevanewPasswordAvatar state="inline"(era «en línea» mal traducido)state="online"TaxZone:peninsula,baleares,canariasmainland,balearic-islands,canary-islands(ceutaymelillase quedan)TransferListmoveText(..., 'elegidos' | 'disponibles'),ROOT_FIELD = 'raiz''chosen' | 'available','root'Claves de textos CabeceraDeAgenda.*,FilaDeFranja.*,TablaDeDias.*,NumerosDePagina.*,TiraDeDias.*,HorasDelDia.*AgendaHeader.*,SlotRow.*,DayTable.*,PageNumbers.*,DayStrip.*,DayHours.*Ids de Storybook componente-boton--jugar,componentes-*,sistema-*,fundamentos-*component-button--playground,review-*,system-*,foundations-*; los exports de las historias también en inglés (Cargando→Loading) y el nombre que se ve, en castellanoTambién los ficheros y las carpetas del repositorio pasan al inglés (
src/components,src/utils,src/styles,src/form,src/schemas,src/table,src/messages/texts,boton.tsx→button.tsx,DISENO.md→DESIGN.md...). Lo que se nota desde fuera:npx amset-ui addcopia acomponents/,utils/,messages/texts/ystyles/en vez de los nombres de antes;DESIGN.mdyACCESSIBILITY.mdviajan en el paquete con su nombre nuevo; y los ficheros dedist/cuelgan de las mismas carpetas (dist/components/button.js), aunque desde fuera solo se llega a ellos por el indice y las subrutas deexports.Los scripts de
package.jsontambién:build,build:css,build:css-components,build:docs,build:examples,build:sources,build:locales,build:storybook,typecheck,verify,test:watch,e2e:engines,e2e:engines:gate,consumers,budget,audityexport:figma(antesconstruir*,tipos,verificar,test:mirar,e2e:motores*,consumidores,presupuesto,auditaryexportar:figma). Quien tenga unnpm run construiren su CI de una rama, lo cambia.Los parámetros de las funciones exportadas también se llaman en inglés, y
scripts/docs-api.test.tscomprueba con el compilador cada ejemplo de README, REFERENCE y Primeros pasos: imports, componentes, props y valores. - Storybook sin ruido de accesibilidad y con el código en inglés. Pasado axe sobre las 1.036 páginas (historias y documentación, claro y oscuro): las reglas de PÁGINA (
region,landmark-one-main,page-has-heading-one) saltaban en casi todas las historias por construcción y se apagan en el banco (src/bench/a11y.ts); las de orden de títulos y puntos de referencia repetidos se apagan solo en las páginas de repaso y de sistema, que apilan piezas sueltas. Lo que sí era del componente: la cabecera del calendario y de la agenda era un<header>que axe contaba comobanner, ahora es un<div>; la columna vacía deBoardySortableListvacía no tenía fila válida dentro delgrid(aria-required-children); el nombre accesible de cada columna deBoardllevaba «tarjetas» escrito en castellano y ahora sale del paquete de textos (Board.column, con plural, también en inglés);RangeFilterdeshabilitado llevaaria-disabledy deja de medirle el contraste a lo apagado; y la barra neutra deTimelineno llegaba a 4,5:1 en el tema oscuro. Por otro lado, 504 historias se llamaban en castellano (Cargando,ConError,SoloLectura…): ahora el export y el id de Storybook van en inglés, el nombre que se ve sigue en castellano, y los identificadores de las historias, el banco y.storybook/también (fotoDeMuestra→samplePhoto). - El panel lateral y el diálogo vuelven a parar la página de detrás y a decir en su disparador si están abiertos (AMS-1284). Al pasar los dos a
<dialog>se perdieron, sin un solo error, dos cosas que hacía React Aria: la página de detrás se seguía desplazando con el modal abierto (medido en el banco: la rueda encima del velo la llevaba de 200 a 1.000 px) y el disparador dejó de llevararia-expandedyaria-controls, así que un lector no decía «expandido» ni se quedaba hundido con su capa abierta. Ahora, mientras hay unDialog, unDrawero unaCommandPaletteabiertos, la raíz llevaoverflow: hiddeny, si la barra de desplazamiento ocupa sitio,scrollbar-gutter: stable, para que nada salte de lado; lo que la página tenía en línea se devuelve al cerrar, y un diálogo abierto desde un panel no suelta la página hasta que se cierra también el panel. El disparador que se pasa portriggerrecibearia-expanded,aria-controls(solo con la capa abierta, apuntando aliddel<dialog>, el tuyo si le das uno) yamset-trigger-open. Cuesta 0,34 KB comprimidos en cada uno; el presupuesto de peso suma un escenarioButton, Drawer. benchmark/: MUI y Mantine en las piezas sueltas, y tres apps equivalentes medidas en un navegador real (AMS-1784). Las piezas (Button;Button + Select + DatePicker;Table) ya comparan siete bibliotecas con versiones fijadas; Mantine con el CSS mínimo, solo las hojas de lo que queda en el bundle, comprobado al construir. Además, la misma pantalla (formulario con validación, tabla con orden y paginación, diálogo de confirmación) hecha con @amset/ui, MUI, Mantine y antd, pintada en el servidor e hidratada en Chromium: se mide lo que descarga la página, el JS que se parsea y la hidratación, y cada app pasa el mismo recorrido por roles y axe sin fallos.npm run benchmarkconstruye, mide y reescribebenchmark/RESULTADOS.mdcon las versiones, la fecha y las salvedades. Medido hoy: la pantalla carga 127 KB gzip con @amset/ui, 176 con Mantine, 220 con MUI y 391 con antd.- El botón se queda con el foco al pulsarlo con el ratón también en Safari (AMS-1881). Safari no enfoca un
<button>nativo al pulsarlo, ni con el ratón ni con el dedo, así que al abrir unDialog, unDrawero un menú con un clic y cerrarlo, el navegador no tenía a quién devolver el foco y caía en elbody(quien seguía con el teclado volvía al principio de la página).ButtoneIconButtonenfocan ahora en elclick-no en elmousedown, donde Safari lo deshace y Firefox saca el aro de foco con el ratón- y antes deonPress, que es cuando la capa mira quién tiene el foco. Con el teclado no cambia nada. Cuesta 70 bytes comprimidos.
0.16.0, del 18 de octubre de 2026
npx amset-ui migrar: reescribe el código que usa los nombres que la 0.14 retiró (AMS-1831). Las props y las claves de columna deTable(enServidor,paginacion,ordenable,numerica...), elsentidodel orden,Formulariocon sus props (esquema,onEnviar...), los campos*Form,BotonEnviar,resolverDeEsquema,PropsCalendarRange,TableSortInputy la llamada aariaGranularity. Lo hace con el árbol de sintaxis del TypeScript del propio proyecto, no con expresiones regulares: distingue una prop de la misma palabra en un comentario, una cadena u otro componente, respeta tu sangría y tus comentarios, sigue imports con alias y con espacio de nombres, y las columnas declaradas en una variable. Lo que no puede decidir solo (un...props, un.sentidoleído en un manejador,PropsForm,ERROR_NETWORK_COUPON) sale como aviso con fichero y línea.--dry-runcuenta antes,--checksale con 1 si queda algo (para la CI), y es idempotente.doctoravisa (error) cuando quedan esos nombres. Probado con 19 pruebas, entre ellas una que compila el código de antes (falla) y el migrado (compila) contra los tipos reales de la biblioteca, y ejecutado en seco contra cuatro fronts de la casa sin ningún falso positivo.
0.15.0, del 17 de octubre de 2026
InvoiceLinesEditor: la fila ya no se sale de su caja (AMS-1812). Con la columna de retención (la de serie) la fila de una sola línea suma unos 65 rem de columnas, pero el umbral para pasar de tarjeta a fila era 52 rem, el mismo con y sin retención: entre 832 y 1040 px de contenedor sobresalía hasta 210 px y el importe y el botón de quitar quedaban fuera, recortados (sin retención, 38 px a 832). Ahora hay dos umbrales, 55 rem sin retención y 66 con ella; por debajo, cada línea es la tarjeta de dos columnas.e2e/factura-ancho.spec.tsbarre doce anchos y falla si la fila sobresale (comprobado que muerde). Lo destapó un barrido de axe sobre todas las historias del Storybook, que contaba el importe recortado como un contraste de 1,12:1.- Rompe ROMPE (arregla un fallo):
NumberField,QuantityPickeryMoneyFieldmandan en un formulario el numero, no el texto que se ve. Elnameiba en el campo visible, que lleva el numero con el formato del idioma: en un formulario nativo,10,5llegaba como"10,5"(un servidor lo lee comoNaNo como10),12.000como"12.000"(un12) y un importe como"1.234,56 €". Ahora elnameva en un<input type="hidden">con el numero canonico —10.5,0.21,12000,1234.56—, siempre con punto, sin separador de miles ni simbolo, ya cuadrado a minimo, maximo y paso como lo deja el campo al salir, y vacio si no hay cifra o no se entiende. Un campo apagado no se envia, como un<input disabled>. Aviso: el campo visible ya no llevaname; si una prueba o un script lo buscaba por[name=...], ahora encuentra el oculto. NuevocanonicalNumber()enutilidades/numero. 12 pruebas nuevas (numero-formulario.test.tsx); 9 fallan contra el codigo anterior. - Recargo de equivalencia en las facturas (AMS-1785).
calcularFacturayInvoiceLinesEditorsuman el recargo al IVA según su tipo -5,2 % con el 21 %, 1,4 % con el 10 %, 0,5 % con el 4 %, art. 161 de la Ley 37/1992- con su fila propia en el desglose (surchargeBreakdown) y en el total. Se pide para toda la factura (equivalenceSurcharge) o línea a línea (surchargePerLinetrae una casilla por línea y la de la línea manda). Se redondea una vez por tipo, como el IVA, no línea a línea (diez líneas de 0,10 dan 0,05 y no 0,10).surchargeRatescambia los tipos; el 0,62 % transitorio del 5 % no va por defecto. Sin pedirlo, los totales de siempre no cambian. Sale por el índiceSPANISH_SURCHARGE_RATESy los tipos nuevos. Item,ItemGroup,InputGroupyRadarChart. La fila genérica (conhrefoonPressel TÍTULO es el enlace o el botón y su zona se estira por CSS hasta cubrir la fila: un solo elemento interactivo, y las acciones por encima como botones aparte); el campo con segmentos pegados dentro de una sola caja, con el anillo de foco, la ayuda y el error deTextField; y el gráfico de radar (escala desde cero, unnulles un hueco y no un cero, resumen para el lector con lo más alto y lo más bajo de cada serie, y la tabla entera).radarPoint,ringPathyseriesPathsalen deutilidades/grafico-radar.ts.TextButton, la acción de texto (AMS-1792). Un<button>con el aspecto y los tonos deLinkpara una acción que va dentro de una frase (reintentar, dejarlo): el lector dice «botón», responde a Intro y Espacio, estype="button"por defecto y no envía un formulario dos veces mientrasloading. Sin la caja ni el relleno deButton: hereda tamaño y altura de línea, y con el dedo la zona de pulsación crece a 44 px con un pseudo-elemento, sin mover la línea (un<button>es siempre inline-block: el relleno sí la estiraría). Sustituye el botón escrito a mano que cada front montaba para esto.HoverCard,MenubaryNavigationMenu. La tarjeta que sale al pasar el ratón por un enlace (su contenido se puede tocar:closeDelaydeja llegar hasta ella, Tab entra porque va justo después y Escape la descarta, WCAG 1.4.13); la barra de menús de escritorio (un solo tabulador, flechas laterales con vuelta y al revés en RTL, y con un menú abierto el ratón abre el vecino, que unActionMenumodal no puede); y la navegación principal con despliegues de ARIA (enlaces de verdad, el panel justo tras su botón,aria-current, Escape y salir con Tab la cierran, y pulsar un botón que el ratón acaba de abrir lo FIJA en vez de cerrarlo).useMenuListsale deMenuPanelpara compartir el teclado de la lista con la barra de menús.- El prefijo del teléfono sale completo en la lista (AMS-1703). Dominica, Bahamas, Jamaica y el resto del +1 se veían todos como «+1»; ahora la lista y el nombre accesible dicen «Dominica (+1 767)», y con varios rangos «+1 809 · 829 · 849» en el nombre accesible y el título, y «+1 809…» en la fila (el completo partía el nombre del país en dos líneas: 43 px la fila de República Dominicana frente a 36). El disparador sigue en «+1» (las cifras del reparto ya van en el número escrito, y repetirlas daría «+1 767 767 555…»); Estados Unidos, Canadá (demasiados rangos) y los repartos de una o dos cifras se quedan en el indicativo. El E.164 no cambia.
dialLabel()sale por el índice. ModelSelectoryArtifactCard(+ArtifactFrame), los dos que faltaban para migrar el chat del asistente. El selector de motor y modelo es de presentación pura (el catálogo y la elección los pone quien lo usa): dos filas de radios nativos —las flechas saltan los motores apagados—, un motor apagado dice su motivo con texto, y si lo elegido se ha apagado el botón se marca y el panel avisa;fixLabel+onFixpone el enlace para arreglarlo. La tarjeta de artefacto trae la miniatura o el icono según el tipo, abrir y descargar como botones HERMANOS de la cabecera, y la vista previa solo se monta abierta;onVisiblepide el contenido de lo que entra en pantalla.ArtifactFrameaísla el HTML consandbox="allow-scripts"y nuncaallow-same-origin, y no se deja quitar desde fuera. Salen sueltosselectionLabelyartifactKind.npx amset-ui initynpx amset-ui doctor(AMS-1778).initdeja un proyecto con la biblioteca puesta como toca: la línea@amset:registrydel.npmrc(nunca un token),@amset/ui/estilos/base.cssarriba del CSS global —lo que fija el orden de las capas antes que Tailwind 4 sin pagar el CSS dos veces—, tu tema generado con el comandotema(contraste comprobado),AmsetProvidery el enrutador de Next montados en el layout,_appo<App />. Es idempotente, no pisa un fichero tuyo (solo le añade líneas),--dry-runcuenta lo que haría y--installinstala con tu gestor.doctordiagnostica un proyecto que ya la usa y sale con código 1 si hay errores: Tailwind 3 sin orden de capas,data-temacon otros valores, token como${VARIABLE}en el.npmrc, Pages Router sintranspilePackages, y avisos por el tema sin generar, los enlaces que recargan la página, Jest sin mapa de CSS. Probado con proyectos de Next y de Vite sin configurar que pasan porinit, compilan, se abren en Chromium y llevan el color de marca pedido (npm run consumidores,scripts/init-doctor.test.ts).reffunciona con React 18 (AMS-1782). En React 18 un componente de función no reciberefcomo prop y lo descarta:<Button ref={nodo}>lo dejaba anullyDropdownno abría su panel desde unButton(medido con React 18 real).Button,IconButtonyFloatingButton-los únicos tres que exponenrefen su tipo público- van ahora envueltos conwithRef, unforwardRefque entrega elrefal render; en React 19 todo sigue igual. Nuevonpm run test:react18(y su paso en la CI): instalareact@18aparte y pinta cada ficha y los botones conrefsin que salga ni una excepción ni un aviso de React, así que «React 18 y 19» deja de ser una promesa sin vigilar.
0.14.0, del 17 de octubre de 2026
- Rompe Rompe: fuera los nombres de antes que quedaban como alias (AMS-1761). Un alias obsoleto es código muerto: se van los de
Table(enServidor,seleccionadas,alSeleccionar,paginacion,columnasConfigurables,alCambiarColumnas,redimensionables,alPulsarFila,barraSuperior; en la columnanumerica,ordenable,filtrable,ocultaPorDefecto,pegada,cabeceraDeFila; ysentidoen el orden, con su tipoTableSortInput), los nombres en español del formulario (Formulario,CampoTextoForm,CampoAreaForm,SelectorForm,SelectorFechaForm,CasillaForm,InterruptorForm,GrupoRadioForm,BuscadorForm,BotonEnviar,resolverDeEsquema,PropsForm,EsquemaEstandar,IncidenciaEstandar,ShippingAnswer),PropsCalendarRange,ariaGranularityyERROR_NETWORK_COUPON. La tabla de equivalencias está en el README. amset-ui-web y amset-assistant suben a esta versión a la vez. - El calendario del
DatePickerya no vuelve a aparecer al cerrarse (AMS-1766). El cierre de las capas esperaba a TODAS las animaciones y transiciones del panel, también a las que pone el CSS de quien usa la biblioteca: en amset-assistant, una transición de 200 ms enscrollbar-colorhacía que la salida de 100 ms acabara, el panel volviera a verse entero unos 80 ms y solo entonces se cerrara. Ahora se esperan las animaciones de@keyframesy las transiciones que mueven la capa (opacity,transform,translate,scale,display,overlay), y la salida de las capas flotantes y delTooltipse queda en su último fotograma (forwards) hasta elclose(). - README en inglés (AMS-1716).
README.mdpasa a ser la portada en inglés —lo que muestran npm y GitLab—: instalación, uso, estilos, temas, Next.js, idiomas bajo demanda, formularios, peso medido, pruebas, herramientas para asistentes de IA y licencia. La referencia completa por componente sigue en castellano enREADME.es.md, que ahora viaja en el paquete.src/readme-ingles.test.tsata la portada a la realidad: cadanpm runy cada subruta@amset/ui/...que cita tiene que existir, y la prueba de documentación vigila tambiénREADME.es.md. - Referencia en inglés (AMS-1774).
REFERENCE.mdtraduce al inglés la referencia completa por componente (README.es.md, 2.674 líneas) y viaja en el paquete, enlazada desde la portada.src/referencia-ingles.test.tsimpide que se separen: mismos encabezados en el mismo orden, mismos bloques de código y mismas tablas, y las mismas variables--amset-*y componentes citados en las dos. - Componentes para interfaces de IA:
PromptInput,ChatMessage,Reasoning,ToolCall,ActivityLog,Attachment,SuggestionsyUsageBadge. Salen de lo que ya hacía el chat del asistente de AMSET, sin perder nada: la caja crece con el texto hasta un tope, envía con Intro y baja de línea con Mayús+Intro (sin enviar mientras se compone con un IME), recupera lo último con la flecha arriba, admite ficheros pegados, arrastrados o elegidos y pasa de «Enviar» a «Parar» mientras responde; el registro sigue el final solo si ya se estaba abajo (useStickToBottom, exportado) y junta los eventos repetidos; una llamada a herramienta que falla se abre sola. Todo dicho con texto además de con color, sin dependencias y con un CSS por componente. Salen sueltosformatDuration,formatBytes,collapseRunsyisNearBottom. Un@keyframesnuevo,amset-caret-blink, compartido por los tres cursores. FormTreeSelect, el campo conectado que faltaba (AMS-1675).@amset/ui/formularioya tiene un campo conectado por cada campo con valor:FormTreeSelectguarda la clave del nodo elegido, o la lista de claves conselection="multiple"(en el orden del árbol, no en el del clic), y enseña el error del esquema en el campo.FormCascaderyFormTreeSelectentran también en las tablas de campos conectados de la referencia, de donde faltaba la primera.- El orden del CSS ya no decide como se ve un campo (AMS-1775). La caja y la entrada de todos los campos (
.amset-field__controly.amset-field__entry) pesaban una clase, lo mismo que sus variantes (el area de texto, el campo de numero, el buscador...), y mandaba la que el empaquetador dejara la ultima: en la biblioteca sola el orden lo fijaindex.css, pero en un consumidor no. Medido en un navegador con las hojas en el orden malo: elTextAreasalia con una caja de 36 px alrededor de un textarea de 56 px (en Next 16 con Turbopack, 46 alrededor de 67), y con el interlineado ennormalen vez de 21 px. Ahora las dos bases van en:where()y pesan cero: cualquier variante, y cualquier CSS de quien usa la biblioteca, las gana siempre. En los dos ordenes la caja mide lo mismo (80,3 px) y no desborda. Una prueba nueva (orden-del-css.test.ts) impide volver a escribir la base con una clase suelta. Sin cambios en la API. - Firefox y WebKit en el e2e, y lo que encontraron (AMS-1716).
npm run e2e:motorespasa el e2e de comportamiento en los dos motores (opt-in conAMSET_MOTORES=1, sin axe ni capturas), y la CI lleva una puerta rápida,e2e:motores. Dos fallos reales que Chromium no veía: en Safari, al cerrar un desplegable o una confirmación el foco se perdía en el<body>(Safari no da el foco a un botón al pulsarlo con el ratón, y el navegador devuelve el foco a «lo que lo tenía»); ahora la capa lo devuelve a su disparador si se ha perdido. Y en Firefox y Safari el diálogo, el panel lateral y la paleta de comandos desaparecían de golpe al cerrarse (solo Chromium animadisplayyoverlay); ahora la salida va pordata-exitingyclose()espera a que acabe, igual en los tres motores. Sin cambios de API. - La marca ya admite un segundo color:
secondary. Hasta ahora solo existíabrand(el acento), así que un diseño con dos colores de marca no se podía expresar. Nueva familia de tokens--amset-secondary,-hover,-active,-text,-softe-ink(violeta por defecto, en claro y en oscuro, con sus pares de contraste AA medidos), clavepalette.secondaryenamset-ui.config.js, ytone="secondary"en todo lo que ya aceptabaaccent(botones, etiquetas, avisos, tarjetas, avatares, gráficos, texto, enlaces…). Sin cambios para quien no la use. - Y un tercero:
tertiary. Misma receta quesecondary: tokens--amset-tertiary*(verde azulado por defecto, claro y oscuro, contraste AA medido),palette.tertiaryytone="tertiary"en todo lo que aceptabaaccent. - El segundo y el tercer color llegan a TODOS los componentes con
data-tone. Casillas, interruptores, deslizadores, pestañas, pasos, el día elegido del calendario, el aro de foco… pintaban siempreaccenty no teníantone. Ahoradata-tone="secondary"odata-tone="tertiary"en el propio componente o en cualquier contenedor repinta todo lo de dentro (remapea--amset-accent*y--amset-focus), sin tocar cada componente. - La documentación cuenta los tres colores de marca. Fundamentos → Color lista las familias
secondaryytertiary(53 roles) y estrena «Los tres colores de marca», una demo en vivo detoneydata-tone; Primeros pasos explica las claves nuevas depalettey las fichas que hablaban de «seis tonos» ya nombran los ocho. npx amset-ui add: copiar el código de un componente a tu proyecto (AMS-1771). Para quien no quiere depender de una versión publicada:add button iban-field(o--all) copia el código con todo lo que necesita, sus hojas de estilo y un catálogo de textos generado solo con lo copiado;listenseña los 190 que hay. Quita el.jsde los imports (--keep-jslo deja), no pisa un fichero que ya habías cambiado (--overwritesí) y apunta lo copiado enamset-ui.json. El paquete publica el código endist/sources/(npm run construir:fuentes). Medido: lo copiado compila en estricto yadd --alltambién.@amset/ui/esquemas: las comprobaciones de los campos españoles, sueltas (AMS-1771).documentSchema,ibanSchema,cnaeSchema,socialSecuritySchema,plateSchema,cardNumberSchemaypostalCodeSchema: la letra del NIF, el módulo 97 del IBAN, la división del CNAE... como esquemas Standard Schema que además traencheckymessagepara engancharlas a Zod o Valibot sin que la biblioteca dependa de ninguno. Mismos mensajes que el campo, enesyen. Subruta de servidor: sin'use client'.ExportButtonyexportElement: gráficos y tablas a PNG, SVG y PDF (AMS-1771). En el navegador y sin librerías: el elemento se copia con los estilos ya calculados, se mete en un SVG conforeignObjecty de ahí salen los tres formatos (elementToPng,elementToSvg,elementToPdf,downloadBlob).scalehasta 4 para imprimir; abarca lo que se sale de la caja; incrusta tipografías e imágenes. El PDF lleva la imagen, no texto. Un elemento oculto falla conExportError. Medido en Chromium conLineChart,BarChartyDonutChart. Nuevos textosExportButton.*.Tablecon 100.000 filas, medido (AMS-1771). ConvirtualizedymaxHeight: primer pintado 121 ms con 16 filas en el DOM, 61 fotogramas por segundo al desplazar y 42 ms para ordenar. No cambia código: solo se documenta con la medida.ACCESIBILIDAD.md: lo que se mide (axe con WCAG 2.0, 2.1 y 2.2 AA), lo que se vigila aparte y lo que no se ha medido todavía, sin declarar un nivel que no se ha comprobado (AMS-1771).
0.12.0, del 16 de octubre de 2026
- La biblioteca se prueba en proyectos reales (AMS-1716).
npm run consumidoresempaqueta el paquete, lo instala en un proyecto Next (App Router) y en otro Vite, los compila, los sirve y los abre en Chromium: tipado de un proyecto ajeno, HTML de servidor traducido, hidratación sin errores, interacción, idiomas perezosos que no se descargan si nadie los pide y tope de JS de primera carga. Jobconsumidoresen la CI. Sin cambios para quien usa la biblioteca. - Los avisos para quien programa ya no hablan ni cargan nada en producción (AMS-1716). Con
process.env.NODE_ENVdetrás de una guardatypeof process, en el navegador la producción se tomaba por desarrollo: concatalogsel proveedor descargaba el catálogo entero de la biblioteca (18 KB gzip, medido en un Vite real) para comprobar las claves, y los avisos de props obsoletas de la tabla salían por consola. Ahora lo decideisDevelopment(), que deja la expresión que sustituye el empaquetador sin guarda. - Elegir un día en un
DatePicker(o cualquier capa) dentro de unDialogya no cierra el modal. Elclosey elcanceldel<dialog>hijo subían por el árbol de React hasta elonClose/onCanceldel modal que lo contiene y lo cerraban (también con Escape dentro del calendario).Dialog,Drawer,CommandPaletteyFloatingDialogsolo atienden el cierre que es suyo. - El texto sobre un relleno de color es claro en los dos temas (AMS-1737). En el tema oscuro los rellenos sólidos (
accent,error-solid,success-solid,warning-solid,info-solid) eran claros y llevaban texto casi negro: con una marca roja o verde salía letra negra sobre rojo o verde. Ahora el relleno tiene la misma luminosidad oscura en los dos temas y su tinta (*-ink) es casi blanca, teñida del tono de la familia, no blanco ni negro puros. Medido: con cualquier tono y croma de marca la tinta llega a 4,5:1 sobre su relleno en claro y en oscuro (prueba nueva enpaleta.test.ts).highlightpasa a negro también en oscuro, así que el pasar el ratón oscurece siempre. Aviso: en oscuro los botones primarios y de estado se ven más profundos que antes; si forzabascolorscon un relleno claro, el generador se negará a escribir el tema. - El texto de un
ToggleChipactivado se lee con cualquier color (AMS-1737). Llevaba el 75 % del color del chip y el resto de la tinta del tema: un rosa intenso en claro o el acento en oscuro quedaban por debajo de 4,5:1. Ahora lleva el 30 % (--amset-chip-ink-share), y una prueba barre el espacio de color en los dos temas. Markdeja de pintar texto oscuro sobre negro. Usaba el rolhighlight(hacia dónde se oscurece un relleno) como fondo; ahora va sobregold-softcon la tinta principal, y el par está en las pruebas de contraste.- Lo hecho dentro de una barra del cronograma oscurece la barra en vez de aclararla, para que las fechas sigan leyéndose sobre ella.
- Los componentes de bloque ocupan el ancho de su hueco sin pedirlo. Campos y
Select, tabla, acordeón, tarjetas, alertas, pestañas, paginación, gráficos, listas y el resto de componentes de bloque llevaninline-size: 100%desdebase.css: metidos en una fila flexible se encogían hasta su contenido (en la web, 80 componentes; elSelectcambiaba de ancho al elegir). Los que miden lo que su contenido (botones, etiquetas, avatares, interruptores,SegmentedGroup, texto) no cambian, y una prueba obliga a clasificar cada componente nuevo. Dentro de otros componentes se mantienen compactos el prefijo del teléfono, las horas del horario semanal y el selector de filas de la paginación. Calendarsuelto se mide contra su caja y sale centrado: los días encogen con el hueco en vez de con la ventana (en una tarjeta de 238 px el mes medía 274 y se salía) y el calendario ocupa su hueco hasta lo que mide el mes.Gridiguala el alto de lo que va en cada celda (equalHeight, por defectotrue): una tarjeta dentro de un envoltorio llena la fila igual que si fuera hija directa.Linkva subrayado por defecto (underline="always"): dentro de un párrafo el color no basta (axelink-in-text-block, 1,12:1 medido).underline="none"lo quita para menús y barras.SegmentedGroup: el relleno ya no sale volando tras desplazar la página. El punto de partida se medía al elegir la opción anterior; con la página movida entre medias arrancaba a miles de píxeles y el marco enseñaba la barra de desplazamiento.benchmark/: peso comparado con react-aria-components, shadcn/ui, HeroUI y antd (P1.6).npm run benchmarkempaqueta con Vite lo mismo en cada una (Button;Button,SelectyDatePicker;Table) y cuenta JS y CSS en gzip, con las versiones fijadas y las salvedades escritas enbenchmark/README.md.- Pedir un componente ya no trae los textos de todos (AMS-1718). El catalogo
es/enentero (65 KB sin comprimir) viajaba con cualquier componente que dijera algo: unButtonpesaba 23,4 KB gzip y ahora 5,9;Tablepasa de 54 a 38 KB y la agenda de semana de 35 a 18. Cada componente trae solo su paquete (src/mensajes/textos/);ESyENsiguen siendo el catalogo entero.translatorFor(locale, paquetes, mensajes?)yuseMessages(paquetes)reciben ahora los paquetes del componente, ycatalogForse sustituye porcatalogEntry. Un texto pedido a un paquete que no lo trae sale en la consola y hace fallar las pruebas. - Seguridad:
LinkyButtonLinkcontarget="_blank"llevanrel="noopener noreferrer"(AMS-1716). Antes no llevabanrely la pagina de destino recibia la ventana de origen y la cabeceraReferer. Elrelque pasa quien lo usa se respeta y se completa. La direccion de seguimiento deOrderStatustambien llevanoopener. SECURITY.md, auditoria y presupuesto de peso en CI (AMS-1716).SECURITY.mdcon como informar y que hace y no hace la biblioteca;npm run auditar(altas y criticas de lo que viaja) ynpm run presupuesto(peso gzip de lo que se importa, medido con Vite) en el jobverificar;src/seguridad-urls.test.tsxfija quejavascript:queda bloqueado.- axe pasa tambien
wcag22aa(AMS-1716): en axe-core 4.13 es la reglatarget-size; 568 casos sin violaciones. - Todas las descripciones de props, también en inglés (AMS-1687). Las 300 que faltaban, de componentes antiguos y de los nuevos, ya tienen su versión en
src/mensajes/props.en.json; una prueba nueva enscripts/api.test.tsfalla si una prop nueva se queda con su descripción solo en castellano. LineChart,AreaChartyStackedBarChart(AMS-1698). Gráficos de varias series con hueco (null), eje con números redondos, leyenda, valores al pasar el ratón, resumen y tabla para el lector de pantalla. Hojaseries.css, textosSeriesChart.*.- El panel del
Selectse topa a 20 rem. Con muchas opciones el panel llegaba a medir toda la ventana, con una barra de desplazamiento kilométrica. Ahora mide como mucho 20 rem (unas ocho opciones y media) y el resto se recorre con scroll; se cambia con--amset-select-panel-max-height. Cualquier capa flotante puede pedir su propio tope con--amset-floating-cap; si la ventana es más baja, el panel sigue encogiéndose. WorkCalendarView,createWorkCalendary lectores de festivos (AMS-1700). Calendario laboral sin festivos incrustados: se cargan de la fuente oficial conparseIcsHolidays/parseCsvHolidays. Resumen de días laborables por mes,isDateUnavailablepara los selectores de fecha. Hojacalendario-laboral.css, textosWorkCalendar.*.WeekAgendaen móvil: en pantalla estrecha pasa a la lista de días con la franja de la semana arriba, como Teams (layoutauto|full|compact). En táctil las citas se pulsan y no se arrastran.WeekAgenda(AMS-1700). Agenda de semana y de día con citas que se arrastran y se estiran (puntero y teclado), reparto de las que coinciden, línea de «ahora» y avisos al lector de pantalla. Hoja propia,agenda-semana.css; textosWeekAgenda.*en español e inglés;placeBlocks,moveBlockyresizeBlocksalen sueltos.- Rompe ROMPE: un solo nombre para cada prop, el del HTML y sin «is» (P0.1, AMS-1727). La biblioteca tenia dos dialectos: 44 controles decian
isDisabled,isReadOnly,isRequired,isInvalideisSelectedy 24 piezasdisabled,readOnly,selectedyopen, asi que quien montaba un formulario tenia que acordarse de cual iba en cada uno. Ahora sondisabled,readOnly,required,invalid,selected,open,indeterminate,dismissableykeyboardDismissDisableden todos, y no queda ningun alias de los nombres de antes: hay que cambiarlos al actualizar (en JSX,isDisabledpasa adisabledy asi con cada uno).MultiSelectse queda condisabled,readOnlyyrequiredcomo todos, y desaparecen sus alias de «is». Se quedanisDateUnavailable(es una función, no un estado),onPressy elonClicknativo, yuseDisclosuresigue devolviendoisOpenjunto a sus verbosopen()yclose(). Una prueba (nombres-de-props.test.ts) impide que vuelva un nombre con «is». InvoiceLinesEditor(AMS-1700). Editor de las líneas de una factura con IVA y retención de IRPF por línea y el desglose de totales;calcularFacturaylineAmountsalen sueltos. Hoja propia,factura.css. TextosInvoiceLinesEditor.*en español e inglés.Tableavanzada (AMS-1699). Agrupación por columna, filtros con varias condiciones, exportar a CSV, columnas guardadas entre visitas, edición en celda y virtualización de filas. Todo opcional: sin las props nuevas la tabla se comporta como antes. Nuevos textosTable.*en español e inglés;csvDeTablaydescargarCsvsalen sueltos.ContextMenuy submenús enActionMenu(AMS-1698). El menú del clic derecho, con teclado, yMenuAction.childrenpara listas anidadas.CodeField: rellenar o vaciar una casilla del medio ya no corre las cifras de la derecha (AMS-1747). En modo controlado el hueco no sobrevivía a que el padre devolviera el valor sin huecos, y teclear en la casilla 5 con "123" puesto dejaba el 7 en la 4. Ahora las casillas conservan sus huecos mientras el padre devuelva lo queonChangeemitió, yonChange/onCompleteentregan siempre el código sin huecos (antesonCompletepodía llegar con un espacio dentro). Escribir sobre una casilla que ya tiene cifra la sustituye en vez de repartir las dos, y una casilla vacía ya no se pinta con un espacio.- Firefox y WebKit en el e2e, y lo que encontraron (AMS-1716).
npm run e2e:motorespasa el e2e de comportamiento en los dos motores (opt-in conAMSET_MOTORES=1, sin axe ni capturas), y la CI lleva una puerta rápida,e2e:motores. Dos fallos reales que Chromium no veía: en Safari, al cerrar un desplegable o una confirmación el foco se perdía en el<body>(Safari no da el foco a un botón al pulsarlo con el ratón, y el navegador devuelve el foco a «lo que lo tenía»); ahora la capa lo devuelve a su disparador si se ha perdido. Y en Firefox y Safari el diálogo, el panel lateral y la paleta de comandos desaparecían de golpe al cerrarse (solo Chromium animadisplayyoverlay); ahora la salida va pordata-exitingyclose()espera a que acabe, igual en los tres motores. Sin cambios de API. - Campos sin caja:
variant="bare"y--amset-field-touch-height. Un campo que va dentro de otra caja (la barra de un chat, un buscador dentro de una barra de herramientas) tenia que quitarse la suya a base de CSS propio, y al quitarla perdia tambien el relleno y el alto, asi que el texto quedaba pegado al borde y el campo medía mas o menos que los botones de al lado.bareenTextField,TextAreay el resto de campos no pinta borde, fondo ni raya, conserva el alto y el relleno del tamaño y marca el foco con un anillo (y el error con la raya de abajo). Y--amset-field-touch-heightfija el alto de la caja con el dedo (por defecto 48 px, como siempre) para el front que usa controles mas bajos en el movil. Aditivo.
0.11.0, del 16 de octubre de 2026
- Los enlaces precargan el destino (como
next/link).RouterProvideraceptaprefetch(router.prefetchde Next):LinkyButtonLinklo llaman una vez por destino al posar el raton o recibir el foco. No lo hacen con un destino de fuera, contargetpropio ni apagados. Opcional: sinprefetchno cambia nada. Next sin cablear nada:@amset/ui/nextexportaNextRouterProvider(App Router) y@amset/ui/next/pagesexportaNextPagesRouterProvider(Pages Router); se ponen una vez arriba del arbol y todos los enlaces navegan y precargan por el enrutador de Next.nextes peer opcional: quien no importe esos subpaths no lo necesita. Nueva ficha Enrutado en Storybook (y, con ella, en la web) que explica el proveedor genérico y los dos de Next. PageHeader,ButtonGroupyComboboxya no llevan'use client'. No usan estado, efectos ni manejadores propios: en Next se renderizan como componentes de servidor y su codigo no viaja al navegador. Medido en una app de Next 16 con una pagina de servidor que los usa:PageHeaderyButtonGroupdesaparecen del JS de cliente (-1,4 kB, -469 B gzip). El resto de componentes con la directiva si usan hooks, contexto o manejadores.- Textos que la biblioteca seguía escribiendo a mano en castellano (AMS-1720). Con el idioma en inglés salían en castellano: la calculadora de cuotas («al mes durante», «La última…», el mínimo y «TIN / TAE»), el selector de envío («Te faltan… para que salga gratis»), el aviso del tablero al mover una tarjeta, el «de 5» de la valoración, el contador de la visita guiada, la pregunta de después de la encuesta NPS (su función por defecto se llamaba sin traductor), los errores de dirección y de tarjeta y los nombres de color y de canal del selector de color. Ahora salen del catálogo de mensajes.
addressErrors,postalCodeError,cardNumberError,expiryError,cvvError,cardErrors,moveAnnouncementycolorNameaceptan el traductor como último argumento opcional y, sin él, devuelven lo mismo que antes; nuevochannelName. La prueba del catálogo ya no se conforma con buscar tildes: recorre el JSX y las plantillas de cada componente. - Storybook: las demos, la documentación y los controles, de verdad en el idioma elegido (AMS-1720).
- Los textos de las demos (botones, tablas, avisos, importes) salen en inglés: los args llegan traducidos al componente y el resto se cambia en la vista previa con
src/banco/demos.en.json(3.400 textos; los nombres propios y los códigos se quedan). Los importes y fechas que las historias formateaban con'es-ES'fijo usansrc/banco/formatos.ts. En castellano todo queda igual que antes, también lo que fotografían las capturas de referencia. - Veinticinco historias de Repaso y Sistema envolvían la demo en un
LocaleProvidersin idioma que forzaba el castellano: fuera. - Las fichas cuyas historias van en su propio marco (
inline: false) salían en castellano: cada marco recibe el idioma y la página lo lee de su propio estado. - «Jugar» pasa a llamarse «Interactivo» (Playground en inglés). Los ids no cambian.
- Pulsar un enlace de una demo ya no navega la ventana entera (Storybook pone
<base target="_parent">): un ancla desplaza, uno de fuera abre otra pestaña. - Los controles que el componente solo lee al montarse (
default…,initial…, el estado de la historia) ya funcionan: la demo se vuelve a montar al cambiarlos, salvo en Capas, donde cambiar el título de un diálogo abierto no lo cierra.currencyylocalese eligen de una lista: escritos a mano tumbaban la historia. - La documentación de 198 ficheros usaba los nombres de la API anteriores a AMS-550 (
alCambiar,valorInicial,plana,fantasma,clave…): ahora usa los reales (onChange,initialValue,flat,ghost,key…), comprobado contra las props y tipos de cada componente. - Siete historias pedían iconos con su nombre viejo («Subir», «Carrito») y pintaban la palabra: arreglado, con una prueba.
- Los textos de las demos (botones, tablas, avisos, importes) salen en inglés: los args llegan traducidos al componente y el resto se cambia en la vista previa con
- Render en el servidor con idiomas perezosos (AMS-1721).
loadMessages(locale, catalogs)resuelve el catálogo del idioma antes de renderizar (componente de servidor,loader,getServerSideProps) y su resultado viaja comomessagesenAmsetProvider/LocaleProvider: el HTML sale traducido y se hidrata sin avisos. Comparte la caché depreloadCatalog, no lleva'use client'y rechaza si la carga falla. UnAmsetProviderrecibe ahora tambiénmessages. Sin cambios para quien no lo usa. - Los huecos que quedaban para migrar un front (AMS-1730).
Selectgananative(un<select>de verdad: el selector del sistema en el movil, cononChangeEvent) ygroupen la opcion, que agrupa bajo un titulo tanto en el<select>(optgroup) como en el panel propio.ButtonganaloadingText, que sustituye al contenido mientras envia, sin girador.IconButtonganafullWidth.NumberFieldganaonChangeEvent. Y--amset-field-required-display: noneesconde el asterisco de «obligatorio» (el lector lo sigue anunciando). Todo es aditivo. - Los idiomas de
catalogsse cargan bajo demanda (AMS-1721). Cada etiqueta puede ser el JSON o una función (fr: () => import('./fr.json')): solo se pide el idioma dellocaleactivo, una sola vez, sin repetir la carga aunque la función se escriba en línea, conStrictModeni al volver a un idioma ya visto. Mientras llega salen los textos de la biblioteca; si falla se avisa por consola, se llama aonCatalogErrory se reintenta al volver a pedirlo; si se cambia de idioma con otro en camino, el tardío no pisa al nuevo.preloadCatalog(cargador)precarga (selector de idioma, servidor). Nuevos tiposCatalogLoader,CatalogSourceyCatalogErrorHandler. Compatible: un objeto sigue saliendo en el primer pintado. - API para migrar un front que ya existe (AMS-735). Tres cosas que antes obligaban a reescribir cientos de manejadores:
onClickdeButton,IconButtonyButtonLinkentrega ahora elMouseEventnativo (conpreventDefault,stopPropagationycurrentTarget; antes recibia la pulsacion unificada);onChangeEvententrega el evento del<input>enTextField,PasswordField,TextArea,CheckboxySwitch(e.target.value,e.target.checked), sin quitaronChange; einputRefllega aNumberFieldyFileUpload, asi queinputRef.current?.click()abre el selector de ficheros desde un boton propio. Aviso: si pasabasonClickleyendopointerType, usaonPress, que sigue dando la pulsacion unificada. - Idiomas enteros desde el proveedor.
AmsetProvideryLocaleProvideraceptancatalogs({ fr, 'pt-BR': ptBR }, un JSON deComponente.clave-> texto por etiqueta BCP-47) yAmsetProvideracepta ademasmessages. Se usa el catalogo dellocaleactivo (exacto, idioma o regional) y lo que falte cae en ingles clave a clave;messagesmanda sobrecatalogs. La plantilla con todas las claves se publica en@amset/ui/idiomas/es.jsonyen.json. En desarrollo el proveedor avisa una vez de un idioma sin catalogo y de claves que no existen, yvalidateCatalog(json)(con los tiposMessageCatalogs,MessageOverrides,MessageKeyyCatalogReport) lo comprueba en una prueba. Sin cambios para quien no pasacatalogs. - La raya de color de cada evento de la agenda vuelve a medir 3 px (AMS-1665).
--amset-agenda-slotestaba declarada dos veces en la misma regla y la segunda (el hueco entre el mes y la lista, 20 px) pisaba el grosor de la raya. El grosor pasa a su propia variable,--amset-agenda-stripe;--amset-agenda-slotqueda solo para el hueco. - Los campos agrupados dicen que estan apagados (AMS-1268).
NumberField,QuantityPicker,CodeFieldySignatureFieldponenaria-disableden surole="group"cuando estan desactivados: antes solo se apagaban los botones de dentro y un lector de pantalla no decia que el campo entero no se podia usar. - Licencia MIT (AMS-1688). El paquete declara
"license": "MIT"y trae el ficheroLICENSE(antesUNLICENSED). Sin cambios de código. - Textos que la biblioteca escribia en castellano aunque el idioma fuera ingles (AMS-1714). Ahora salen del catalogo de mensajes: «Antes» y «Ahorras un» del importe, «sube» y «baja» de la variacion del dato, «de 5» del resumen de valoraciones, el contador de la galeria, «con subniveles» del selector en cascada, «sin leer» de la bandeja, «Eventos del...» de la agenda, «Trabajando» de confirmar accion y «(se abre en otra pestaña)» del estado del pedido. 12 claves nuevas; en castellano no cambia nada.
- El
Selectanuncia que es obligatorio y que no es valido (AMS-1155). El disparador pasa derole="button"arole="combobox"(patron «select-only combobox» de la APG) y llevaaria-requiredyaria-invalidcuando toca. Un lector de pantalla dice ahora «cuadro combinado» en vez de «boton». Afecta a cada campo construido sobre el: telefono, selector de idioma y moneda, tipo de IVA, mes y filas por pagina. Si una prueba tuya busca ese control congetByRole('button'), pasa agetByRole('combobox'). Visualmente no cambia nada. - El Storybook entero sigue el idioma de la barra (AMS-1720). Antes iba a medias: con English elegido la barra lateral, la explicación de cada historia, *Primeros pasos* y los rótulos de Repaso y Fundamentos seguían en castellano, y la interfaz de Storybook (pestañas, ajustes, tabla de props, atajos) salía en inglés aunque se eligiera castellano. Ahora: la barra lateral usa
.storybook/menus.en.json(con los nombres que Storybook saca del export sin acentos corregidos enmenus.es.json); las 672 explicaciones de historias van en.storybook/historias.en.json; *Primeros pasos* son dos ficheros markdown; los rótulos del banco (Canvas,Matrixy las tablas de Fundamentos) van ensrc/banco/banco.en.jsonyCanvaspasa a seguir el idioma elegido (sin barra sigue siendo el castellano, así que las capturas de referencia no cambian); y lo que escribe Storybook por su cuenta se pone en castellano con.storybook/traductor-de-interfaz.ts. Una prueba (scripts/historias.test.ts) obliga a que cada nombre, explicación y rótulo tenga su versión en inglés y a que no sobre ninguna. Se apaga la guía de bienvenida y el aviso de novedades de Storybook, que son su publicidad y no se pueden traducir. Las descripciones de la pestaña *Controles*, que Storybook fija al preparar la historia y no repinta, se traducen en el propio DOM al elegir inglés (solo si la traducción lleva el mismo código en el mismo orden). SiteFootery@amset/ui/seo(AMS-1750).SiteFooteres el pie de página semántico: un<footer>(contentinfodentro deAppShell) con marca, columnas de enlaces —cada una un<nav>con su título como nombre—, redes solo con icono, franja legal y controles; los enlaces externos se abren en otra pestaña conrel="noopener noreferrer"y avisándolo al lector de pantalla. Hojapie-sitio.css, textosSiteFooter.*. La subruta nueva@amset/ui/seo(servidor, sin dependencias ni CSS) traepageMetadata(compatible con la Metadata API de Next y con validación que lanzaSeoError),checkSeo, los constructores de JSON-LD (organization,webSite,webPage,breadcrumbList,article,product,faqPage,localBusiness),<JsonLd>con escape seguro dentro de<script>,sitemapEntries/sitemapXmlyrobotsRules/robotsTxt(blockAllparapre). Aditivo.Tabsadmite pestañas sin panel (AMS-1634). Una pestaña sincontentno pinta panel ni llevaaria-controls: es un filtro o un modo de la misma pantalla. Sigue siendo una pestaña (flechas, foco, elegida) y se puede mezclar con las que si tienen panel.Tab.contentpasa a ser opcional; es aditivo. ElDialogya cubria el resto de huecos que el ticket media en la 0.8.0 (icono y color de cabecera, acciones en cabecera, incrustado, arriba, ancho numerico, sin relleno y en columna).- Un ejemplo de uso real por cada historia de Storybook, publicado en
@amset/ui/ejemplos.json. Sacado del propio codigo: si la historia traerender, es su cuerpo tal cual; si no, se sintetiza la etiqueta JSX con los args reales demetay de la historia. Un ejemplo al que le falte una prop obligatoria (porque su valor dependia de algo externo al fichero, o delmappingde un control de Storybook comoiconControl) se descarta en vez de publicarse incompleto.scripts/ejemplos.test.tscompila cada ejemplo sintetizado contra los tipos reales del componente.
0.10.2, del 15 de octubre de 2026
ToggleChip,ToggleChipGroupyOptionCard(AMS-1534). Los dos componentes que faltaban para migrar el asistente sin perder diseño.ToggleChipes un chip que se activa y se apaga (<button aria-pressed>) con color propio por chip (coloro--amset-chip-color),count,icon, tres tamaños y marca de verificación al activarse;ToggleChipGrouplos agrupa con nombre y salto de línea.OptionCardes una opción deRadioGrouppintada como tarjeta, conmedia,descriptiony la marca de la elegida: dentro del grupo se reparten en una rejilla (--amset-option-card-min). Teclado y foco son los de un radio nativo. Hojas propiaschip-alternable.cssytarjeta-opcion.css, ficha en Storybook en español e inglés y pruebas de comportamiento.
0.10.1, del 15 de octubre de 2026
- El campo de hora ya hidrata igual en inglés (AMS-1687). Con un reloj de 12 horas, el servidor (ICU de Node) escribía un espacio fino antes del «AM»/«PM» y Chrome un espacio normal: React tiraba el HTML y daba el error 418 en las fichas
TimeFieldyDateTimePickerde amset.tech/en/. Los separadores del campo se normalizan al espacio normal, y una prueba lo fija.
0.10.0, del 15 de octubre de 2026
- La documentación también en inglés (AMS-1687).
@amset/ui/fichas.jsonlleva en cada fichaen: { titulo, descripcion }y nuevo export@amset/ui/props.en.json(descripción de cada prop en inglés, por el texto en español). El extractor rompe el build si una ficha no tiene su versión ensrc/mensajes/fichas.en.json, y una prueba vigila la cobertura y que el castellano no haya cambiado desde que se tradujo (cada traducción guarda la huella de su origen). En Storybook, las páginas de documentación siguen el selector de idioma: con English, título, texto y descripciones de las props salen en inglés (1.758 descripciones, todas traducidas). Las historias sueltas (sus nombres y comentarios) y las páginas de Empezar siguen en castellano. TagsInputyCombobox(AMS-1698). Campo de etiquetas que se crean al escribir (Intro, coma, pegado o al salir), sin repetidas, conmax,validate,transformyname; yCombobox, el buscador con texto libre por defecto. TextosTagsInput.*en es y en.DateTimePickeryMonthPicker(AMS-1698). La fecha y la hora en un solo campo (2026-10-15T09:30, conmin,max,isDateUnavailableyname) y el selector de mes y año (2026-10, con los meses en el idioma activo yminYear/maxYear). Solo avisan con el valor entero. Hoja propia,fecha-hora.css, con--amset-datetime-gapy--amset-monthpicker-gap.- Cinco campos de gestión y cinco ganchos (AMS-1677). Nuevos:
MoneyField(dinero que entiende coma decimal, separador de miles y símbolo según el idioma y entrega un número),SocialSecurityField(NUSS con los dos dígitos de control comprobados),LicensePlateField(matrícula actual, provincial y de ciclomotor),CnaeField(CNAE-2009, con catálogo opcional) yVatRateSelect(21, 10, 4 y 0 %). Salen sueltos los validadores (validateSocialSecurity,validatePlate,validateCnae...) y las funciones de dinero (parseMoney,formatMoney,formatMoneyForEdit). Ganchos:useMediaQuery,useDisclosure,useClipboard,useHotkeysyuseAnnouncer. Textos en español e inglés, ficha en Storybook y pruebas de comportamiento (teclado y errores al salir del campo) para cada uno. Ver el roadmap endocs/roadmap-amset-ui.md. - Los textos de la biblioteca, en español e inglés (AMS-1687). Cada texto que un componente decía por su cuenta (etiquetas accesibles, avisos, errores, anuncios, resúmenes, plurales...) sale ahora de un catálogo por idioma (unas 560 claves, cada una en
esy enen), según elLocaleProvider. Un idioma sin catálogo cae en inglés; sin proveedor sigue el español, así que nada cambia para quien ya usa la biblioteca. Nuevos: la propmessagesdel proveedor,translatorFor,useMessages, y un último parámetro opcionalmsgen las funciones que escriben un texto. Los textos con cantidades usanIntl.PluralRules. Storybook trae un selector de idioma (es, en, ar). Tres pruebas vigilan que no haya claves sin usar, que el inglés no lleve acentos y que ningún componente vuelva a escribir un texto en español a mano. - El CSS entra por componente, no entero (AMS-1677). Cada módulo de
dist/importa ahora SU hoja (ybase.css, con los tokens y el reset), así que el empaquetador se lleva solo el CSS de lo que usas: unButtonen Vite pasa de 392 kB a 23 kB de CSS (4 kB gzip), y una pantalla de 20 componentes de 395 a 133 kB, con el mismo resultado al píxel que la hoja única. Nuevos exports:@amset/ui/estilos/base.cssy@amset/ui/estilos/<hoja>.css. Cambia al actualizar: (1) si sigues importando@amset/ui/estilos.csspagas el CSS dos veces: quítalo; (2) con Next Pages Router hace faltatranspilePackages: ['@amset/ui'](sin él,ERR_UNKNOWN_FILE_EXTENSIONal construir); (3) en Jest, unmoduleNameMapperpara.css$. Probado en Vite 8, webpack 5, y Next 16 con App Router y Pages Router. Los imports van en la misma línea que"use client"para no mover los sourcemaps. - Once componentes se pueden usar desde un Server Component (AMS-1677). Antes TODOS los componentes llevaban
"use client", así que en Next un layout de solo servidor se llevaba igualmente el JS deText,Title,Badge,Separator,EmptyState,VisuallyHidden,CompactGroup,DataList,List,Stack/Grid/Center/ContaineryBlockquote. Ya no lo llevan: no usan hooks, manejadores de eventos ni APIs del navegador. Comprobado importándolos y llamándolos con la condiciónreact-serverde Node, y una prueba nueva (componentes-servidor.test.ts) impide que un fichero sin la directiva empiece a necesitar el navegador. Sin cambios de API. Los componentes que dependen de un proveedor, de texto por idioma o de otro componente de cliente (PageHeader,ButtonGroup,Result) siguen marcados, a propósito.
0.9.6, del 15 de octubre de 2026
- Un tema claro dentro de una página oscura ya se ve claro. Los valores claros solo existían en
:root: un<div data-tema="light">dentro de una página en oscuro seguía oscuro, sin ningún error (la muestra de tema de amset.tech salía oscura con «Claro» marcado). Ahora[data-tema='light']existe en cualquier elemento, igual que el oscuro, y donde se anidan gana el de más dentro. Para que el texto y el fondo de la zona cambien, la zona lleva además la claseamset-canvas(o su propio color y fondo). - Los enlaces ya no van subrayados.
Linksale sin subrayar, en el color de acento y en negrita (el peso es la pista que no depende del color: axe sigue limpio con el enlace dentro de un párrafo).underline="always"lo subraya siempre yunderline="hover"al posar el ratón. Tampoco se subrayan ya al posar el ratón los títulos deCard, las migas, la descarga deFileList, el enlace deStat, el de los filtros, el deProductCardni los deGlobalAlert.baresigue existiendo y ahora solo le quita el peso.
0.9.5, del 15 de octubre de 2026
Repaso de calidad de doce componentes que Aitor señaló en amset.tech (AMS-1701). Todo medido en el navegador antes y después, con prueba que falla sin el arreglo.
DateRangePickerya deja elegir el segundo extremo en el otro mes. Con dos meses a la vista, el primer mes seguía al día con el foco, y un día recibe el foco al pulsar el ratón, antes de soltarlo: las rejillas se corrían un mes bajo el puntero y el clic caía en otro sitio. Ahora la vista solo se mueve cuando el foco sale de ella. De paso: cada mes lleva su nombre encima, «Mes siguiente» mira el último mes visible yDatePicker/DateRangePickersiguen suvalue(un atajo deQuickRangeya escribe en el campo).Countdowncon variantes:variant="roll"(por defecto, las cifras que cambian ruedan),"ring"(un anillo que se vacía de forma continua) y"text"(el aspecto de antes). Los anuncios para lector no cambian.ActivityMapcuadrado: el título de la tabla se metía como celda de la primera columna; ahora la columna de días mide lo que mide, las casillas son cuadradas, los meses van encima y la leyenda cuadra con la rejilla.LocalePickercon banderas (withFlags,LocaleOption.flag,localeFlag), y el hueco de unDatePickervacío sale con las letras de cada idioma (yyyy, jj/mm/aaaa, tt.mm.jjjj).PhoneFieldcon los 244 países y territorios de la UIT, cada uno con su bandera (SVG propios, geometría real) y su nombre en el idioma activo; se busca escribiendo el nombre («Fra»), el espacio sigue buscando, Re Pág/Av Pág saltan diez y la rueda del ratón ya baja la lista. Nueva propinitialCountry. Las opciones se anuncian «España (+34)».- La rueda no bajaba en NINGUNA lista flotante larga: el panel escuchaba también el desplazamiento de su propia lista y, al volver a medirse, la devolvía arriba. Arreglado en
flotante.tsx, para todas. ListOption.textValue(texto de búsqueda aparte de la etiqueta),CountryPrefix.areaCodes.
- La rueda no bajaba en NINGUNA lista flotante larga: el panel escuchaba también el desplazamiento de su propia lista y, al volver a medirse, la devolvía arriba. Arreglado en
Amountalineado: altura de línea propia, «/mes» pegado al precio, la insignia de descuento centrada con las cifras; tres tamaños en fila comparten línea base.Table: ordenar y filtrar son dos cajas iguales de 22 px; el filtro activo se marca con el color del acento y el embudo relleno, sin fondo de bloque. La cabecera de fila pegada ya no coge el gris de la cabecera.Tabssin barra de desplazamiento: un píxel de margen negativo desbordaba la lista y Windows enseñaba una barra con flechas. Si las pestañas no caben, la rueda las mueve de lado sin barra.orientation="vertical"ya se pinta en columna.SideNavcon el diseño del menú de AMSET (el del assistant): baldosa de icono, nombre y descripción, marca de la página actual, grupos desplegables con cuenta, plegado a carril con tooltip. API nueva retrocompatible:description, grupos (NavGroup,NavItem),onCollapsedChange,header/footercomo función de{ collapsed }. Anchos 13/16/19 rem.AppShellya no apila el menú encima del contenido en una pantalla estrecha: queda un carril al lado.CollapsibleyAccordioncerrados sin hueco fantasma: el panel cerrado seguía teniendo caja y se comía elgap.
0.9.4, del 15 de octubre de 2026
- Dos hidrataciones mas que se rompian con una web de verdad (AMS-1666). Salieron al barrer amset.tech en produccion, donde el HTML lo pinta un runner de CI en UTC y el navegador esta en otra zona y otro dia:
QuickRangecon un periodo puesto marcaba el atajo que cuadra («este mes») segun el dia que fuese al pintar: el servidor decia uno y el navegador otro. Ahora no marca ninguno hasta que se sabe el dia.RelativeTimeconwithFullDateescribia la fecha completa en la zona del servidor («15:45») y el navegador en la suya («17:45»). Ahora la primera vez se acepta lo del servidor y en cuanto se hidrata el nodo se vuelve a montar con la hora de quien mira (useInBrowser):suppressHydrationWarningcalla el aviso pero deja el texto del servidor.hidratacion.test.tsxpinta ahora el servidor en UTC y el navegador en Europe/Madrid, y comprueba que el texto acaba bien.
- La API de
Tablepasa a inglés, con los nombres de antes funcionando (AMS-1677). Era el único componente que mezclaba idiomas (onSortjunto aalSeleccionar,paginacion,enServidor...). Ahora:serverSide,selectedKeys,onSelectionChange,pagination,configurableColumns,onVisibleColumnsChange,resizableColumns,onRowPress,toolbar; en las columnassortable,filterable,numeric,hiddenByDefault,sticky,rowHeader; y el orden es{ column, direction }. Los nombres antiguos siguen funcionando con un aviso en la consola (una vez por nombre, nunca en producción) y se retiran en la próxima versión mayor.onSortentregadirectiony tambiénsentidomientras tanto, para no romper a quien lo lea. Tabla de equivalencias en el README, que además tenía la sección de Tabla escrita con una API anterior que ya no existía. - La API de la biblioteca, legible por agentes de IA (AMS-1677). El build escribe ahora
dist/api.jsoncon las props reales de cada componente (tipo, obligatoria o no, comentario), sacadas del compilador y no de una lista a mano; los atributos HTML nativos no se enumeran, se anota que se aceptan. De ahí salendist/AGENTS.md,dist/llms.txtydist/llms-full.txt, y el paquete lleva una skill,skills/amset-ui/SKILL.md, para quien construye con la biblioteca desde Claude Code, OpenCode u otro agente. Nuevos exports:@amset/ui/api.json,/AGENTS.md,/llms.txty/llms-full.txt. Sin cambios en la API de los componentes. - El job
verificarconstruye antes de probar. Los 8 tests del CLI de temas se saltaban siempre en CI porquedist/aún no existía; ahora corren.
0.9.3, del 14 de octubre de 2026
Timelineya no rompe la hidratacion por los espacios de la fecha (AMS-1666). El tramo de una barra («24 oct – 4 nov») lo escribeIntl.DateTimeFormat.formatRange, y con ICU 78 -el de Node 22.23- pone espacios finos (U+2009) a cada lado de la raya, mientras que Chrome pone espacios normales. Un HTML pintado en Node y hidratado en el navegador no casaba y React lanzaba el error 418. Ahora se normalizan al espacio normal. Se vio al quitar de la web de amset.tech el parche que esquivaba el cronograma: la prueba de hidratacion de la biblioteca no lo cogia porque corre en un solo ICU.
0.9.2, del 14 de octubre de 2026
useTabssale por el índice (AMS-1670). Es la lógica de las pestañas sin su ropa: para una pantalla que ya pinta su panel por su cuenta y solo necesita la lista con su teclado (las flechas dan la vuelta, Inicio y Fin, solo la activa entra en el tabulador, las apagadas se saltan, activación automática o manual). Salen tambiénTabSlot,TabsSetupyTabsPlumbing. Antes solo lo usabanTabsy la galería. Para filtrar una misma lista no son pestañas: esSegmentedGroup.- Ya no se rompe la hidratacion en una web con SSR o exportada a estatico (AMS-1666). Un componente que lee «hoy» o «ahora» mientras se pinta sale distinto en el servidor y en el navegador, y React tira el HTML y repinta (error 418 en la consola, medido en produccion en amset.tech). Pasaba en
Calendar,DatePicker,DateRangePicker,QuickRange,MonthAgenda,ShippingPickeryTimeline. Ahora el primer pintado -el del servidor y el de la hidratacion- no sabe el dia, y el navegador lo conoce justo despues, sin un fotograma de mas en una app que solo pinta en cliente:CalendaryMonthAgendasin un dia que los ancle (nivalue, nidefaultFocusedValue, niinitialDay) pintan solo su sitio, conaria-busy, hasta que llega «hoy»; con un dia dado se pintan enteros desde el servidor.- La marca del dia de hoy (
data-today) y la linea de hoy deTimelinesalen al llegar al navegador. ShippingPickerescribe el plazo de entrega al llegar al navegador, salvo que se le pasenow.- Hay una prueba que lo vigila en TODAS las fichas (
hidratacion.test.tsx): pinta en servidor un dia, hidrata otro y cuenta los desajustes. Una ficha nueva entra sola.
0.9.1, del 14 de octubre de 2026
- El texto de las 149 fichas, al día con el código (AMS-1659). Las fichas del banco (y desde ahora las de amset.tech) seguían hablando de React Aria, que ya no está, y usaban los nombres en castellano de la API de antes (
etiqueta,tamano,alNavegar,ProveedorIdioma…). Revisadas una a una contra el código: props y valores reales, teclado y accesibilidad como funcionan hoy, y fuera lo que ya no existe. @amset/ui/fichas.json: la ficha de cada componente, como dato (AMS-1659). Una entrada por componente con su nombre de importación, su módulo, su título en español, su familia, el texto de su ficha (qué es, cuándo usarlo y cuándo no, teclado, accesibilidad, aspecto fino; en markdown) y los ids de sus historias en el banco. Sale de las historias de Storybook al construir, así que la web de amset.tech deja de llevar una copia a mano que se quedaba vieja.- La
descriptiondelDialogadmite un nodo (una etiqueta de estado junto a la frase) y se sigue anunciando como descripción. Sin descripción ya no apunta conaria-describedbya un elemento que no existe. Dialogcubre ya lo que pedía un modal con cabecera de marca (AMS-1534, a petición de amset-assistant, que tenía 42 modales propios). Todo es opcional y lo que ya existía no cambia:titlepuede ser un nodo;iconpone un icono propio en lugar del del tono yaccentle da color (--amset-dialog-accent);headerActionslleva botones entre el título y el aspa.width(píxeles si es número, medida CSS si es texto) manda sobresize;placement="top"lo pega arriba, para una paleta que crece hacia abajo;hideHeaderno pinta la cabecera pero el título sigue siendo el nombre accesible.flushquita el relleno del cuerpo ybodyColumnlo hace columna flex, para contenido con su propio desplazamiento.inline: la misma caja EN la página, como una región con su título. Sin<dialog>, fondo, Escape ni trampa de foco, sin aspa y sin inventar un «Cerrar»: sirve para reutilizar como panel de pantalla lo que en otra es un diálogo.
0.9.0, del 14 de octubre de 2026
QRCode(AMS-1638). Un código QR en SVG sin dependencias:<QRCode value label level size margin onError />. Detrás hay un codificador propio (encodeQr, ISO 18004, modo bytes/UTF-8, versiones 1–40, niveles L/M/Q/H) comprobado módulo a módulo contra el paqueteqrcode(2.032 matrices idénticas con máscara forzada) y el SVG renderizado se decodifica con un lector independiente. Sale siempre negro sobre blanco, también en el tema oscuro: es lo que lee un escáner. No se hacenMentions,Watermark,Masonryni el visor de imágenes de antd: sin demanda medida.TreeSelect: un desplegable con el árbol dentro (AMS-1636). El campo es el delSelecty el panel es elTree: elegir una categoría o un departamento de una jerarquía sin tener el árbol siempre a la vista. Una (selection="single", cierra al elegir) o varias ("multiple", se queda abierto y lo elegido sale en el orden del árbol); cualquier nodo se puede elegir, rama u hoja, salvo los apagados. Las ramas que llevan a lo elegido se abren solas y el foco entra en la fila elegida.displayRender,name(un campo oculto por clave). Teclado el delTreemás Abajo/Arriba para abrir y Escape para cerrar. Sin búsqueda dentro del panel (elTreeno la tiene) y sin versión conectada propia: va conFormField.Tree: carga diferida y marcado en cascada (AMS-1637).loadData(clave)pide los hijos de una ramaisLeaf: falsela primera vez que se abre; el chevron deja paso a un anillo (aria-busy) y se anuncia «Cargando…». Si falla, la rama se queda como estaba y el chevron reintenta.cascade(conselection="multiple"): marcar una rama marca todo lo de dentro y una rama con solo una parte marcada se ve «a medias» (y el lector dice «elegida en parte»). El valor sale en el orden del árbol; lo apagado no se toca. Con carga diferida, una rama ya marcada hereda la marca en lo que llega.- Medido contra el ticket: el
Carouselya tenía autoplay (automatic, con botón de pausa, pausa al pasar el ratón o enfocar y respeto a «menos movimiento») y control desde fuera (index+onChange); no se tocó. Arrastrar nodos para moverlos queda fuera (pide una alternativa de teclado diseñada aparte).
Cascader: selector en cascada (AMS-1635). País > provincia > ciudad en un solo campo, con una columna por nivel, en vez de variosSelectencadenados que no comparten estado ni teclado. El valor es la ruta (['es', 'madrid', 'alcala']), controlada o no.changeOnSelectdeja elegir una rama sin bajar a una hoja;loadData(ruta)pide los hijos de una opciónisLeaf: falsela primera vez que se abre (la columna dice «Cargando…»);displayRender,levelLabels(el nombre de cada nivel para el lector de pantalla),name(viaja la clave de la hoja). Teclado: flechas arriba/abajo por la columna, derecha entra, izquierda vuelve, Intro elige, Escape cierra. Las ramas se anuncian «con subniveles».FormCascaderlo conecta al formulario. Sin búsqueda ni selección múltiple todavía.- Subir ficheros de verdad (AMS-1555).
FileUploadelegía y validaba, pero no subía nada: el progreso, el fallo y el reintento los reescribía cada proyecto. Ahora:Uploader: la zona de soltar, una barra por cada fichero que sube, el motivo y «Reintentar» si falla, y quitar uno que sube cancela la petición. Sube por HTTP conhttp={{ action, method, headers, data, fieldName, withCredentials }}o por donde digas concustomRequest({ file, onProgress, signal })(una URL firmada, un SDK). Cuántos suben a la vez:concurrency(3). Avisa cononChange,onUploadedyonFailed, y anuncia «factura.pdf subido» o el fallo.useFileUploads: el motor sin pintar nada (cola, progreso, fallo, reintento, cancelar,pending, yautoStart: false+start()para subir con un botón de «Enviar»), para montar otra pantalla.httpUpload,UploadError,isAbort: la petición por defecto, conXMLHttpRequest(es lo único que da progreso de subida;fetchno).FileUploadganamaxCount(los que no caben se rechazan con su motivo),directory(elegir una carpeta; soltar una carpeta encima no la recorre), pegar un fichero o una captura con Ctrl+V yhideList.- Sigue siendo cierto: el filtro de tipo y tamaño es comodidad, el servidor tiene que volver a comprobarlo.
- Campos coherentes entre sí y
NumberFieldvaciable (AMS-1625, AMS-1626).NumberFieldse puede vaciar: borrar el texto y salir del campo avisa cononChange(NaN)—el «sin valor» que ya escribían los consumidores— y deja el campo en blanco. Antes volvía el número anterior y se enviaba. Lo que no es un número (abc) sigue descartándose.QuantityPickerno cambia: una cantidad no puede estar vacía.FormNumberFieldlo traduce aundefined.DocumentFieldeIbanFieldadmitenonBlur.PhoneFieldadmitename(envía el número entero en E.164, no el trozo visible),QuantityPickeradmitenameySlideradmitename(cada tirador lo lleva); ya viajan en un formulario nativo.- El error de
MultiSelectlleva el icono de aviso, como el de los demás campos. Sliderya aceptabaonBlur(es un atributo de su<div>); el ticket decía que no.
- Armazón, pegado al desplazar y controles pegados (AMS-1558).
AppShell: cabecera, columna lateral, contenido y pie en una rejilla de CSS (elLayout,Header,SideryContentde antd en uno). El contenido es un<main id="contenido" tabindex="-1">, el destino de «saltar al contenido» delTopBar; la cabecera se queda fija y la columna se queda fija justo debajo de ella (mide su alto) con su propio desplazamiento. Por debajo de 48 rem la columna pasa encima del contenido y deja de ser fija.siderWidth,mainId,stickyHeader.Affix: un bloque que se queda pegado al desplazar, por arriba (offsetTop) o por abajo (offsetBottom), cononChange(pegado). Esposition: sticky: el contenedor tiene que ser más alto que el bloque y ningún ancestro puede teneroverflow: hiddenniauto.TableOfContentsganaoffset: con una cabecera fija de 64 px, pulsar una entrada deja la sección debajo de ella y la raya de lectura se cuenta desde su borde de abajo. Sinoffsetes lo de siempre.CompactGroup: un campo con su botón (o varios campos) pegados sin huecos y con las esquinas de unión cuadradas; elSpace.Compactde antd. Los campos de dentro llevanhiddenLabel. Vale con cualquier campo (texto, número, selector...) y conButton,IconButtonyButtonLink.- No hecho, con su ticket:
Cascader(AMS-1635),TreeSelect(AMS-1636), carga diferida y arrastre delTreey autoplay delCarousel(AMS-1637), y las piezas menores (AMS-1638).
@amset/ui/formulario: todos los campos conectados y la API en inglés (AMS-1557). Antes conectaba 8 campos y tenía los nombres en español (Formulario,CampoTextoForm,BotonEnviar,resolverDeEsquema), lo que dejaba dos idiomas en una misma importación.- Nombres en inglés:
Form(propsschema,initialValues,onSubmit,validateOn),SubmitButton,schemaResolvery los tiposFormProps,SubmitResponse,StandardSchema,StandardIssue. Los nombres de siempre siguen funcionando con sus mismas props —son alias obsoletos y se retirarán en una versión mayor—: no hay que migrar nada hoy. - Campos nuevos:
FormPasswordField,FormPhoneField,FormDocumentField,FormIbanField,FormCodeField,FormNumberField,FormQuantityPicker,FormSlider(un valor),FormRating,FormMultiSelect,FormSegmentedGroup(una opción),FormDateRangePicker,FormTimeField,FormColorPicker,FormCardField,FormAddressField,FormSignatureFieldyFormFileUpload. Los de valor compuesto (tarjeta, dirección) reparten el error del esquema a cada subcampo:tarjeta.numberfalla en el número, no en el bloque. FormField: conecta CUALQUIER otro campo con una función que recibevalue,onChange,onBluryerror. Es el equivalente delForm.Itemde antd, y cubre lo que no tenga versión propia o un componente tuyo.- Los campos conectados antiguos se llaman ahora
FormTextField,FormTextArea,FormSelect,FormSearchField,FormDatePicker,FormCheckbox,FormSwitchyFormRadioGroup. - Aviso:
ROOT_FIELDsigue valiendo'raiz'y no'root'; es la clave que ya leen los proyectos enformState.errors.
- Nombres en inglés:
- Tono de la barra, forma de los botones, tamaño del grupo y aviso de foto rota (AMS-1556).
Progressganatone(success,error,warning,info,neutral; sin tono, acento): una subida que ha fallado se pinta en rojo y una terminada, en verde. El color no es lo único que lo dice: ponlo también en el texto convalueLabel.Button,ButtonLinkeIconButtongananshape:'default'o'pill'(esquinas del todo redondeadas) y, solo enIconButton,'circle'. Dentro de unButtonGroupsolo se redondean las esquinas de fuera del grupo. Se declara el vocabularioShapeySHAPESenvariantes.ButtonGroupganasize: se escribe una vez y lo heredan losButton,IconButtonyButtonLinkde dentro que no dicen el suyo (uno con su propiosizelo conserva).AvatarganaonError(src): avisa de que la foto no ha cargado. El avatar ya se quedaba en iniciales por su cuenta; esto es para reintentar con otra dirección o dejar constancia.- No hacía falta:
ButtonGroupya teníaorientation="vertical".
- Tabla: filas desplegables, clase por fila, alto máximo y más (AMS-1554).
renderExpandedRow(row)convierte cada fila en desplegable: gana un botón conaria-expanded(que apunta a su detalle conaria-controlscuando está abierto) y el detalle sale debajo, de ancho completo.rowExpandable(row)deja sin botón a las filas que no tienen detalle. Se llega con el teclado (flecha derecha desde la fila, Intro) y desplegar no cuenta como pulsar la fila (alPulsarFila). Controlable conexpandedKeys/onExpandedChange, oinitialExpandedKeys.rowClassName(row, index)suma una clase a una fila concreta (una factura vencida en rojo).maxHeight('24rem','60vh'): el cuerpo se desplaza y la cabecera y el pie se quedan fijos. Era la variable--amset-table-height; ahora es prop.TableColumn.pegadaadmite'end'(la columna de acciones se queda al final al desplazar en horizontal);truey'start'siguen pegando al principio.TableColumn.ellipsiscorta el texto largo con «…» en una línea y deja el texto entero en eltitle.- Arreglado: con la tabla desplazada, la cabecera de fila pegada (
th) se pintaba encima de la cabecera de columna «Número», y el pie fijo quedaba por debajo de las columnas pegadas. - No hacía falta: los totales al pie (
TableColumn.footer) y la cabecera pegada ya existían. - Sigue sin existir:
colSpanpor celda,bordered,tableLayout,showHeader,caption.
- Funciones de pintado (AMS-1550). Hasta ahora una opción, una celda o un día eran texto más un icono: en cuanto hacía falta un avatar, dos líneas o un precio, el componente se descartaba. Ahora se puede pintar la pieza. La regla es la de
Chip:labeles lo que se lee y la función pinta lo que se ve, así que el nombre accesible no cambia y la pieza sigue siendo elegible con ratón y teclado.renderOption(option, { selected, disabled })enSelect,MultiSelect,SearchField,SelectableListyVirtualList(esta última solo la llama para las filas pintadas).renderValue(option)enSelect: lo elegido dentro del campo cerrado.renderTag(option)enMultiSelect: cada ficha de lo elegido.renderItem(item, { selected, disabled })enTransferList.renderDay(date, number)enCalendar: lo que va dentro de cada día. El nombre del día no cambia.renderPage(page, { current })enPagination: el contenido del botón de cada página (solo conshape="full").renderCrumb(crumb, { current })enBreadcrumbs.TableColumn.renderrecibe ahora tambiénindex, la posición de la fila en la página que se ve.- Se exportan los tipos
OptionRenderyOptionRenderState. - Todo es aditivo. Falta lo que antd llama
expandedRowRenderysummaryen la tabla: son de AMS-1554.
0.8.0, del 13 de octubre de 2026
- Quién cierra una capa y con qué se abre (AMS-1553).
DialogganaisDismissable(pulsar fuera cierra; enfalse, solo se va por sus botones, Escape oopen) eisKeyboardDismissDisabled, las mismas dos que ya teníaDrawer. Es elmaskClosablede antd.kind="alert"sigue sin cerrarse al pulsar fuera.TooltipganaopenOn('hover' | 'focus' | 'press', por defecto['hover', 'focus']). Con'press'se alterna al pulsar y se cierra al pulsar fuera: es lo que la hace visible en una pantalla táctil. UnonClickpropio del elemento se respeta.DropdownganaopenOn('press' | 'contextmenu'). Con'contextmenu'se abre con el clic derecho y el menú del navegador no sale. No hay'hover'a propósito: el panel es modal.- No hay
zIndexni contenedor de portal, y no es un olvido: las capas van en la capa superior del navegador, que queda por encima de cualquierz-index(medido con 2147483647) y funciona dentro de un shadow DOM. Un panel que se abre desde dentro del modal de otra librería vive en ese modal, así que sigue siendo pulsable.
- Estado de carga (AMS-1552).
loading(yloadingText, para lo que dice) llega a los componentes que no lo tenian:Switch,Select,MultiSelect,SearchField,SelectableList,VirtualList,DialogyDrawer.Switch: mientras espera la confirmacion NO cambia de valor ni avisa, pero sigue en el orden del tabulador (apagarlo sacaria el foco), con un girador en el tirador.Select,MultiSelect,SearchField: el campo se marca ocupado (aria-busy), lleva un anillo y el panel enseña una filaCargando…. Las opciones que ya hubiera se quedan.SelectableList,VirtualList: ocupadas, con la fila debajo (scroll infinito).Dialog,Drawer: el contenido se cambia por un esqueleto y se anuncia; el titulo y los botones se quedan.
Spinnerganadelay(no sale hasta pasado ese tiempo, para que una carga de 100 ms no parpadee) y puede envolver un bloque conchildren: lo deja en gris, inerte y con el girador encima.inputRefenTextField,PasswordFieldyTextArea: el nodo del<input>o del<textarea>de verdad, no el de la caja. Para enfocar un campo al abrir una edicion en linea, seleccionar un token que se va a copiar o devolver el foco a un buscador. Acepta objeto y funcion, y convive conautoFocus(el atributo sigue saliendo al DOM). Era lo que impedia migrar los campos de amset-assistant que se enfocan a mano.- El
idde un campo llega a su control (AMS-1488).TextArea,TimeField,DatePickeryDateRangePickerlo aceptaban y lo tiraban;MultiSelectySearchFieldlo dejaban en el<div>de fuera. Ahora, en un campo con<input>elforde su etiqueta lo apunta, y en uno cuyo control es una fila de segmentos (fecha, hora, rango) cae en el propio grupo. PhoneField,QuantityPicker,TimeField,DatePickeryDateRangePickeradmiteniden el tipo: en ejecucion ya lo reenviaban, pero el compilador no dejaba escribirlo.
0.7.0, del 13 de octubre de 2026
Todos los componentes dejan pasar los atributos de su elemento (AMS-1549). Antes lo hacían 14 de 158; ahora lo hacen todos los que pintan DOM. Es un cambio aditivo: medido contra la 0.6.2, no se ha quitado ni cambiado el tipo de ninguna prop, así que actualizar no debería romper la compilación de nadie.
### Qué se puede hacer ahora
<Tag id="estado-pago" title="Pagada el 12/10" data-testid="etiqueta" style={{ marginInlineStart: 8 }}>
Pagada
</Tag>Cualquier atributo del elemento que el componente pinta en su raíz: id, title, lang, data-*, aria-*, style y los manejadores de ratón y teclado. Dos reglas que conviene saber:
classNamese suma a las clases de la biblioteca ystylese mezcla con el del componente. Ninguno de los dos lo sustituye.- En un campo (
TextField,Select,NumberField…) los atributos van al control —ahí los espera quien escribe undata-testido unautoComplete—, yclassNameystylevan a la caja, que es lo que se ve.
También se exporta DomProps<'div'>, para tipar un envoltorio propio sin enumerar atributos a mano.
### Lo que NO se deja pasar, y por qué
Hay props del elemento que un componente pone él mismo, y una de fuera se aceptaría y se descartaría en silencio. En esos casos el tipo no las admite, así que te enteras al compilar en vez de comprobar que no pasa nada:
| Componente | No admite | Usa |
|---|---|---|
Dialog, Drawer, IdleWarning, CommandPalette | onCancel, onClose, onClick, role, aria-labelledby, aria-describedby | onOpenChange, title, description |
Dropdown, ConfirmAction, ShareButton, NotificationTray, ActionMenu | id, role, tabIndex, onKeyDown, onClick, aria-label, aria-labelledby, aria-describedby | title, onOpenChange |
Tooltip | id, role | — |
OverlayCloseButton | onClick | onPress |
Checkbox, Switch | onKeyDown, onKeyUp, onPointerEnter, onPointerLeave, onPointerDown | envolver en tu propio elemento |
Carousel | onFocus, onBlur | — |
Tree, SelectableList, VirtualList, SortableList | onKeyDown, onFocus, onBlur, role, tabIndex | — |
DocumentField, IbanField | onBlur | — |
### Otros cambios
- En un campo de texto, un
aria-describedbyde fuera se suma a la ayuda y al error en vez de pisarlos (TextField,TextArea,PasswordField). Cardsepara en el tipo lo que gobierna cada forma (estática, pulsable, enlace); no cambia lo que aceptaba.FieldRootPassThroughyFieldInputPassThroughaceptan ahorastyle.- Tres pruebas vigilan el resultado en la propia biblioteca: que el atributo llegue, que caiga en la raíz y no en una pieza interna, y que un manejador de fuera se llame.
Antes de 0.7.0
No había registro de cambios. Las versiones anteriores están en el historial de GitLab.
