Librería de Formularios para Angular | ng-hub-ui-forms

Campos de formulario accesibles para Angular (input, textarea, slider, select, datepicker) con visualización automática de errores de control/grupo/form, formularios reactivos y variables CSS.

Última actualización 27 ago 2026

Visión General

Por qué los equipos buscan esta librería

Utiliza esta librería de formularios para Angular cuando necesites campos accesibles con visualización automática de errores, un ng-select forkeado, un datepicker reconstruido y theming runtime con variables CSS, sin Bootstrap.

Instalación

npm install ng-hub-ui-forms

Ir a

Ideal para

  • formularios de registro y checkout
  • páginas de ajustes
  • filtros y búsqueda
  • dashboards de administración

Sobre forms

ng-hub-ui-forms unifica cada campo bajo un único contrato: bíndalo con formularios reactivos y los errores aparecen automáticamente a nivel de control, grupo y formulario. Los campos son standalone, OnPush y signal-native; el select es un fork mantenido de ng-select tematizado con tokens hub; el datepicker está construido sobre Date nativo y el overlay del CDK de Angular. Todo se tematiza con variables CSS canónicas --hub-*, con dark mode en runtime.

Guías de uso

Campos de texto

El campo `hub-input` con vinculación a reactive forms y visualización de errores automática y sin cableado. Texto, email, contraseña, número y contador, además de etiquetas flotantes y addons de input-group.

Ejemplos:
Campos de texto y errores automáticos

Campos de texto y errores automáticos

We'll never share it.
Campos de contraseña: mostrar/ocultar, medidor de fortaleza y autocompletado

Campos de contraseña: mostrar/ocultar, medidor de fortaleza y autocompletado

While typing with Caps Lock on, a warning hint appears automatically under the field.

Número, contador, color, interruptor, casilla

Número, contador, color, interruptor, casilla

Grupos de entrada, archivo y etiqueta flotante

Grupos de entrada, archivo y etiqueta flotante

$.00
@
No file selected
Deprecated since 22.6.0 — use <hub-file-input> instead.
Iconos, botones y campos acoplados a un campo

Iconos, botones y campos acoplados a un campo

Pick one
From
a month
a week
Unassigned
Markdown
Máscaras de patrón (tarjeta, IBAN, teléfono, fecha)

Máscaras de patrón (tarjeta, IBAN, teléfono, fecha)

Form value: { "card": "", "expiry": "", "iban": "", "phone": "", "date": "" }

Entrada de código de un solo uso (OTP)

Entrada de código de un solo uso (OTP)

Paste the 6-digit code we sent you.

Form value: { "code": "", "pin": "", "coupon": "" }

Buscador con icono y typeahead

Buscador con icono y typeahead

Debounced term (after 300 ms):

  • Angular
  • React
  • Vue
  • Svelte
  • Solid
  • Qwik
  • Preact
  • Lit

Multilínea y rango

Textareas que crecen automáticamente con contadores de caracteres, y sliders de rango de un solo control o de doble control.

Ejemplos:
Área de texto con contador y autoajuste

Área de texto con contador y autoajuste

0 / 160
Grows as you type.
Slider y rango de doble control

Slider y rango de doble control

Dual-thumb range.
Estilo del slider (gradient y flush)

Estilo del slider (gradient y flush)

Selección

El motor ng-select bifurcado y tematizado con los tokens de hub, además de los formatos button, radio y checkbox que renderizan su propia UI para el mismo valor.

Ejemplos:
Control segmentado

Control segmentado

Single-select — the selected pill slides between options.
Multiple-select — the value is an array.

view: list · perks: wifi

Segmented: plantilla de opción personalizada

Segmented: plantilla de opción personalizada

Single-select — the template renders an icon, the label and a check on the selected segment.
Multiple-select — the same template contract; index comes from the context.

transport: bike · channels: email

Selección y selección múltiple

Selección y selección múltiple

Select a country
Pick some tags
Angular
Signals
Opciones agrupadas

Opciones agrupadas

Select a city
Typeahead y búsqueda

Typeahead y búsqueda

Type to filter…
Search and add…
Select: typeahead asíncrono

Select: typeahead asíncrono

Type at least 2 characters…
Async typeahead — each term is pushed to the Subject and the results come back from the 'server'.
Pick or type a new tag…
addTag creates an item from the search term when nothing matches.

city: · tags:

Plantillas de opción y etiqueta

Plantillas de opción y etiqueta

Pick a teammate
Botones, radio y formatos de casilla

Botones, radio y formatos de casilla

Select dentro de un modal

Select dentro de un modal

Addons y acciones acopladas

Addons y acciones acopladas

Pick a tier
/ month
Static addons at both ends, like an input group.
https://api.
Pick a region
A run of addons on one side reads as a single piece.
Pick a product
An action attached to the edge — it acts on whatever is selected.
Pick a plan
EUR
Addon and action together — the action is always the outermost.

Selección de fechas

Un selector de fecha o rango construido desde cero sobre Date nativo + CDK Overlay, con límites, navegación por teclado e i18n completa.

Ejemplos:
Selector de hora — una hora del día

Selector de hora — una hora del día

Required.
Between 08:00 and 22:00, in quarters.
Fed '09:30:00' — the seconds are trimmed instead of showing nothing.
FromUTC
Emitted value{ "opensAt": null, "closesAt": "09:00", "fromApi": "09:30:00", "shiftFrom": "09:00", "shiftTo": "18:00" } opensAt is required
Datepicker y rango

Datepicker y rango

No future dates.
Pick a start, then sweep to preview.
Datepicker con hora

Datepicker con hora

Each end carries its own time.
A single instant, minute precision.
Emitted value{ "window": null, "reminder": null }
Granularidad: del año al segundo

Granularidad: del año al segundo

2026
2026-09
2026-09-01
2026-09-01T09:00:00+02:00
2026-09-01T09:30:00+02:00
2026-09-01T09:30:45+02:00
Formatos de entrada, valor y visualización

Formatos de entrada, valor y visualización

Anything the enum does not cover — here, Unix seconds.
An Angular date pattern. The stored value is unaffected.
Seeded with '15/06/2026', which no built-in rule would read.
string — 15/06/2026
Un día con rango de horas

Un día con rango de horas

One day, two times — never crosses midnight.
Two instants — the ends may fall on different days.
Emitted value{ "meeting": null, "stay": null } meeting spans · stay spans

Subida de ficheros

Un campo de ficheros con arrastrar y soltar, pegado desde el portapapeles, restricciones de tipo y tamaño, previsualización y progreso de subida opcional por fichero.

Ejemplos:
Fichero único y validación

Fichero único y validación

Drag the file onto the box, paste it, or browse for it.

Selected: —

Arrastrar y soltar, límites y previsualización

Arrastrar y soltar, límites y previsualización

Progreso de subida, cancelar y reintentar

Progreso de subida, cancelar y reintentar

Uploads start automatically. Rename a file to include “fail” to see the retry button.

Contenedores y validación

`form[hubForm]`, `hub-fieldset` y `hub-legend` muestran automáticamente los errores a nivel de grupo y de formulario (entre campos), del mismo modo que los campos muestran los errores de control.

Ejemplos:
Contenedores y errores entre campos

Contenedores y errores entre campos

Password

De derecha a izquierda

Todos los campos se voltean con `dir="rtl"`. Casi todo sale gratis —las primitivas están escritas con propiedades lógicas de CSS—, pero el slider, el switch y el control segmentado necesitaron trato aparte, porque su geometría solo es CSS a medias.

Ejemplos:
De derecha a izquierda — todos los campos espejados

De derecha a izquierda — todos los campos espejados

Barcelona
The calendar button changes side.

Características clave

Cambios recientes

Version 22.24.0 - 8/25/26, 12:00 AM

added: hub-select and hub-datepicker honour labelType="floating", and every field that floats a label now looks identical doing it. The select had accepted the input for as long as floating labels existed and quietly ignored it, so a form that floated its text fields and put a select among them came out with one label inside the box and the next one above it. The geometry is now three shared tokens all of them read, rather than a copy each — which is precisely how the select had drifted from the input. At rest the label IS the placeholder: the field's own font size, the placeholder colour, centred in the box; measured against a plain field's placeholder it is 16px against 16px, the same muted colour, and zero deviation from the centre.

added: The library is right-to-left throughout. Thirty declarations across select, datepicker, input and textarea moved to logical properties, and the three components whose geometry is only half CSS were handled individually and measured in a browser in both directions: the slider (a native range genuinely mirrors, but its fill is a background image and background positions answer to nothing), the switch (its knob and the transition naming it had to move together — naming the physical property while the inset is logical animates nothing) and the segmented control, whose CSS was already correct but which never re-measured on a direction change, leaving the indicator up to 145px from the option it marked.

changed: A hub-select is 2px shorter and every floating field is 10px taller. --hub-select-min-height was a hard-coded 2.5rem that had drifted from the arithmetic every other field arrives at, so a select stood 2px taller than an input or a datepicker and an input attached to one was stretched to match; it is derived now, and the three land on the same pixel. See BREAKING_CHANGES.md — the compiler cannot warn about a change of height.

Version 22.23.0 - 8/23/26, 12:00 AM

added: hub-timepicker, a time of day as HH:MM. The family had a date and no hour, so a product needing one reached for a text field with an HH:MM pattern — which gives no numeric keyboard on a phone, offers nothing when the field is focused, and lets "8:00" through until submit rather than while it is typed. Built on the platform's input type="time", so the keyboard, the stepper and the reader's own 12- or 24-hour presentation come from the browser, while what the form holds is always HH:MM regardless of locale. An empty field publishes null rather than an empty string, because "no time" is an absence and a zero-length string sails past a required written as a null check; and a value carrying more than the hour — 09:30:00, a whole instant — is trimmed to what it can show instead of rendering nothing, which is what the native control does with anything it cannot parse.

Version 22.20.0 - 8/17/26, 12:00 AM

added: hubPrepend / hubAppend can now attach a FIELD, not only a button. A hub-select projected into a slot kept its own rounded leading corners and sat a padding-width away from the control, so the two read as a field and a loose control parked beside it — the opposite of attaching them. The slot's rules assumed they could reach the box they were squaring, which holds for a button, an anchor or a bare span, whose border and radius sit on the very element the slot selects; a field primitive holds neither on its host, because the box lives on the control inside it. The treatment is forwarded one level down now: the host gives up the border and padding it should never have taken, and the inner control takes the squaring — the leading radius of a projected select goes from 6px to 0, the host border from 1px to 0, and the overlap with the field is one border width, so the pair draws as a single line. hub-input, hub-select, hub-textarea and hub-datepicker are covered, as direct children of the template, on both slots. The case is ordinary rather than a curiosity: a price and the period it is a price of are one statement, and splitting "180 € a month" across two fields makes the reader reassemble it on every row.

Version 22.19.0 - 8/17/26, 12:00 AM

fixed: A range picker committed a range that had only one end. The first click was published straight away as { start, end: null }, so opening a picker that already held a complete span and clicking a new start destroyed the old value before the user had chosen anything — and dismissing the panel there left the control holding a half-open shape every consumer then has to defend against. The first pick is a question now: it is drawn and previewed, but nothing reaches the control until the second end lands, and dismissing half-way rolls the panel back to the last committed value so the field and the model never disagree. An explicit clear still publishes null, and day-time-range was never affected because picking the day settles both ends at once.

Version 22.18.1 - 8/16/26, 12:00 AM

fixed: A number field with no bounds wrote min="null" and max="null". [min] is a property binding and the IDL property stringifies whatever it is handed, so an unset input reached the DOM as the literal string. Browsers ignore it and typing was never affected, which is why it survived — but assistive technology reads the attributes as present and announces valuemin=0 valuemax=0, telling a screen-reader user that a price field accepting any amount had to be zero. Bound through [attr.min] / [attr.max] now, which removes the attribute instead.

fixed: A button projected into hubAppend or hubPrepend ignored its own variant and color. The slot doubles its class to beat chrome arriving from another package, and that weight also swallowed what the consumer asked for, so hubButton accepted two inputs and discarded them without a word. The fill is wrapped in :where() now — a default rather than a verdict: a bare span still gets a surface, anything that styles itself outranks it.

fixed: The border of an attached action vanished under the pointer. Attached actions overlap by a border width so a run reads as one line, which leaves the shared pixel to whichever paints last — the one further right, always. The action under the pointer or the focus ring is now raised above its neighbour, and only then: raising it permanently would stop DOM order drawing the run as one line at rest.

Version 22.18.0 - 8/16/26, 12:00 AM

added: hub-datepicker gains mode="day-time-range" — one day and two times within it. Booking a room, a slot or a shift is not two free instants; it is the day, from 09:00 to 11:00, and range mode could not say that because its two ends are free to land on different days. The value is still a HubDateRange, so serialization, min/max and valueFormat are untouched and back ends taking a span need no change; what the mode adds is the guarantee that both ends share a calendar day, enforced by a control that cannot express anything else rather than by a validator running afterwards. One click on the day settles the whole span, the two time strips carry the rest, and the input names the day once. The mode implies a time, so a granularity coarser than hour is raised to it.

added: hub-datepicker in range mode now previews the half it is still waiting for. Between the two picks the grid said nothing: the anchor was lit and every other cell was inert, so the days the range was about to swallow gave no sign as the pointer swept over them. The band the range would take is now drawn against the cell under the cursor — or the one keyboard navigation last moved to — painted with a new --hub-daterangepicker-preview-bg, half the tint of the committed band and derived from it so retinting the range moves the preview with it. Works in both directions, and a disabled day under the cursor is skipped rather than dropping the band.

fixed: The control repainted the edge it shares with attached content. Whatever a slot projects is pulled onto the field border by a negative inline margin, so the two borders land in the same pixel column and paint order decides which one you see. The select container is position: relative — the engine own rule — and the slot was static, so the control painted last and swallowed the attached border. It stayed hidden because both borders share a colour by default; it only surfaced once a consumer themed a projected button differently from the field. The slot is now positioned, with no z-index.

fixed: A hubAppend or hubPrepend slot that projects two elements drew a rounded corner in the middle of the strip. The rule that flattens a run of string addons tests position among the group children, and everything a slot projects lands inside a single __attached wrapper, so it never saw them — the strip only squared the edge it shares with the control. Each side now hands its inner corners over too, leaving the radius to whichever element is outermost. Fixed for input, select, datepicker and textarea alike.

fixed: The datepicker panel header capitalized wrong outside English. Intl renders the month and year as "agosto de 2026" and text-transform: capitalize raised every word — "Agosto De 2026" — which no consumer could undo, because component styles are injected after the global sheet. The header now raises only its initial. The weekday and period labels keep word casing on purpose: both render a single Intl token, and both are inline-flex boxes, which ::first-letter does not apply to.

Version 22.17.1 - 8/13/26, 12:00 AM

fixed: Every hub-select with an addon shipped 22.17.0 with both corners still round, drawing as separate boxes parked together. Widening the flattening selector to reach the datepicker nested input trimmed the select branch from .hub-select__control.ng-select .ng-select-container to a bare .ng-select-container — four classes down to two, under the three the select theme spends on that same corner from a stylesheet that loads later. The branch is spelled out to the engine class again, keeping the descendant combinator the datepicker needs. Reaching an element and winning it are different things, and only the second one paints.

Version 22.17.0 - 8/13/26, 12:00 AM

fixed: hub-datepicker kept its input fully rounded between an addon and an attached action, drawing three separate boxes where the other fields draw one. The flattening rule used a child combinator, and this field's input is a grandchild of the group — it sits inside the __trigger that serves as the overlay origin — so the rule read as though it covered every field while matching nothing on the nested ones. It is now descendant-scoped and reaches the control wherever a field nests it.

fixed: Content attached with [hubPrepend] / [hubAppend] collapsed to the width of its glyph unless it arrived pre-styled. A .btn brings its own inline padding and centres what it holds; a bare span around an icon brings neither, and rendered as an 18px sliver with the icon pinned to the top edge. Attached content now takes the field inline padding and centres its content, the same as the string addons — and nothing that already looked right moves, since that is the value .btn was already using.

changed: The fill moved from the labels to the actions, so every field with an addon changes appearance. A static prepend / append now shares the field's own surface and the group reads as one box with a unit written inside it; what can be operated carries the fill instead. The fill is the affordance and it was on the wrong thing — a grey box holding a glyph reads as a button whatever the glyph means. Two tokens carry it, wired to the design system's semantic palette so it follows a themed build: --hub-<field>-group-action-bg and --hub-<field>-group-action-color. Setting --hub-input-group-addon-bg back to --hub-sys-surface-elevated restores the previous look. A projected control that brings its own background still wins.

Version 22.16.1 - 8/13/26, 12:00 AM

fixed: hub-select ignored a placeholder set globally in NgSelectConfig — the last of the three inputs that overwrote the app's configuration with a value of its own, here an empty string, which is every bit as present as a sentence and just as effective at winning a ??. Nothing changes for an app that does not configure one: NgSelectConfig carries no placeholder default. The fallback now lives in this component rather than in the engine's template, which is re-synced from upstream. fixedPlaceholder and appendTo also differ from the engine's configured defaults and are left alone: both are deliberate and both say so in their JSDoc.

Version 22.16.0 - 8/13/26, 12:00 AM

added: [hubPrepend] and [hubAppend] attach an icon or a button to a field's edge — anything richer than the text a prepend / append string can carry. Available on every field that renders as a box with a value: hub-input, hub-select, hub-textarea, hub-datepicker and hub-timepicker. They compose with the string addons rather than replacing them: the strings render first, so projected content is always outermost on its side — a unit labels the field, the action sits beyond it. Whatever is projected wears the field's border, radius and height rather than its own.

added: hub-textarea and hub-datepicker gain prepend / append group addons, the contract hub-input and hub-select already had. No new tokens: the shared structure falls back to the input's --hub-input-* tokens, so a field only declares its own when it wants to differ.

changed: The group and addon structure of all four fields now comes from one shared SCSS mixin instead of a copy each. The input and the select had already drifted — the input on physical properties and adjacent-sibling selectors, the select on logical ones — and both of the bugs this cost (22.13.1 and 22.15.1) are now written into the single place that can prevent them. The input gains correct RTL behaviour as a side effect. Attached content is marked hub-<field>__attached; the input keeps __affix for the glyphs it positions inside the box, which is a different thing.

deprecated: [hubSelectSuffix] — use [hubAppend], which does the same on every field rather than only on the select. Shipped in 22.15.0 and superseded one release later: generalising the slot left the select with two names for one concept, and retiring the narrower one now costs less than documenting the difference forever. It keeps working and renders through the same slot; [hubAppend] wins if both are present.

fixed: hub-select rendered "No items found" and "Add item" in English whatever the app configured. NgSelectConfig is the one place an app translates the dropdown's own strings, and the engine reads it as a fallback — but a fallback only fires on a missing value, and this component handed the engine its own English default, so the config was unreachable from every select in the app. Both inputs now default to undefined. An explicit notFoundText / addTagText still wins, so call sites passing the text by hand to work around this keep working and can drop it.

fixed: A hub-select carrying only an attached action kept its trailing corner rounded under it. The select marked that case with --has-suffix while the shared flattening rule keys off --has-append, which it set from the string addons alone. The other three fields already read both sources; the select now does too, and --has-suffix is gone rather than left as a second name for the same state.

Version 22.15.1 - 8/12/26, 12:00 AM

fixed: An action attached with [hubSelectSuffix] did not wear the field's chrome: the seam was the action's own 1.5px dark border against the field's 1px light one, and the action stood about seven pixels taller than the control beside it. What gets projected brings chrome from its own package — hubButton sets a border width, a radius and vertical padding — through a single-class rule in a stylesheet that loads after this one, so a single class here tied on specificity and lost on order. The rule now doubles its own class to outrank that, and the action takes the field's border, radii and height, stretching into the row instead of setting its own. The existing specs stayed green through it because they assert that the action renders, stays out of the dropdown engine and follows the control in the DOM — all true the whole time, and none of them about how it looked.

Version 22.15.0 - 8/12/26, 12:00 AM

added: hub-select gains prepend and append group addons, the same contract hub-input already had: a string is one addon, an array is a run of them, and empties are dropped rather than drawn as an empty box. A currency, a unit or a protocol now reads identically whichever field carries it. Three new tokens chained to the input's: --hub-select-group-addon-bg, --hub-select-group-addon-color and --hub-select-group-addon-border-color.

added: [hubSelectSuffix] attaches an interactive control to the select's inline-end edge — a button that acts on whatever is selected. Deliberately not the same slot as an addon: an addon is a static label sharing the field's border, while this is focusable and sits outside the box, so it never competes for the corner the dropdown arrow and the clear cross already share. It is a template rather than projected content because the select's catch-all ng-content carries ng-option through to the engine and would otherwise swallow it; rendering from a template also keeps the action after the control in the DOM, so tabbing reaches the field before the button acting on it. Both mechanisms compose, and the action is always the outermost element.

Version 22.14.0 - 8/12/26, 12:00 AM

added: hub-datepicker gains a granularity axis: year, month, day, hour, minute and second. It also selects the panel — year and month render a 12-cell period grid, day the calendar, and anything finer adds a time strip of ARIA spinbuttons to it. From hour onwards the value is a full ISO 8601 timestamp carrying the reader's local wall clock and the offset of that very date (2026-09-01T09:00:00+02:00 in September, +01:00 for the same clock in January), so a validity window like "today from 9 to 21" no longer has to be rounded up to a whole day. The default is day, which emits the same bare YYYY-MM-DD string it always has.

added: Three independent format axes, where before only display was configurable: parse says how a value coming in is read (ISO of any width, Date and epoch millis are detected automatically; the input is the escape hatch for anything else, and it applies to min and max too), valueFormat says what the bound control holds (iso, date, timestamp, or a function), and displayFormat says what the user reads — now accepting an Angular pattern like dd/MM/yyyy HH:mm or a function, on top of the Intl options it already took.

added: minuteStep, secondStep, hourFormat and timeDisplayFormat, each with a global default in provideHubForms. hourFormat is derived from the locale unless forced, and it governs the input display as well as the panel. New translatable labels: done, hour, minute, second, meridiem, time, startTime and endTime — AM/PM comes from Intl, like month and weekday names.

changed: min and max now honour the time, not just the day. A day is only disabled when no instant of it is allowed, so min = 2026-09-01T14:00 leaves 1 September clickable and lets the time controls do the finer refusing. Range endpoints are ordered by instant rather than by day, which is what makes a same-day 21:00 → 09:00 pair reorder itself instead of being left in click order with an end before its start.

changed: HubDateRange and HubDateValue take an optional type parameter that defaults to string, so every existing annotation keeps compiling and keeps meaning what it meant. Consumers using valueFormat="date" write HubDateValue<Date>.

fixed: An input with prepend or append drew two boxes instead of one field. The control kept its four rounded corners and the addon drew its own rounded box right against it, so <hub-input append="€"> read as a field with a separate pill parked behind it rather than as an amount with its unit. The rules meant to flatten the joining corners hung off the adjacent-sibling combinator and had never once matched, because the prefix and suffix affix spans are rendered unconditionally — an addon is never the control's adjacent sibling. The flattening is now driven by hub-input__group--has-prepend / --has-append on the group, so it no longer depends on what sits between the addon and the control, and in a run of several addons only the outermost one rounds its outer corners.

Version 22.13.0 - 8/7/26, 12:00 AM

added: A read-only theme, applied from the field's own state: readonly reached the native attributes and stopped there, so a read-only field went on drawing the border, background and focus ring of something you can type in — and next to an editable neighbour there was nothing to tell the two apart. hub-input, hub-textarea, hub-select and hub-datepicker now reflect it as hub-field--readonly, with no class to remember at the call site. Deliberately not the disabled treatment: the text keeps full contrast and stays selectable, only the chrome that offers input goes (background, border, focus ring, the select caret and clear cross, the datepicker icon). The padding stays, so the box and baseline still line up with the editable fields beside it, and the error border survives — an invalid value is a different message, and one the user still has to see.

added: New CSS tokens: --hub-input-readonly-bg, --hub-input-readonly-border-color, --hub-input-readonly-color and --hub-input-readonly-cursor.

Version 22.12.2 - 8/7/26, 12:00 AM

fixed: A disabled select stayed fully usable: setDisabledState set the component's own disabled signal and the template spent it on a hub-field--disabled class, but the inner select was never told. The field greyed out while its panel still opened and a choice still wrote through to a control the form had explicitly disabled. The disabled state now reaches the inner select, so it refuses interaction like every other field.

Version 22.12.1 - 8/7/26, 12:00 AM

fixed: The slider's value bubble no longer gets cut in half at the ends of the rail: it was centred on the thumb with a flat translateX(-50%), which leaves half of it outside the component at 0 and at 100 — an ordinary scrollable form clips it. It now translates by the same percentage it is positioned at, pinning its left edge to the start of the rail and its right edge to the end. Both bubbles of a range slider follow the same rule.

Version 22.12.0 - 8/4/26, 12:00 AM

fixed: The password reveal toggle never worked: resolvedType is a computed() but the reveal flag was a plain class field, so toggling never re-evaluated the native type. The state is now the passwordRevealed two-way model and the toggle flips password/text as expected.

fixed: Readonly password fields no longer expose the secret: readonly used to force type="text", printing the password in clear. Password fields now stay masked when readonly (an explicit toggle click may still reveal); other formats keep the readonly → text behaviour.

added: The reveal toggle renders inside the input group as an integrated trailing addon (visually attached to the field), instead of a detached button.

added: passwordRevealed two-way model, passwordToggle (default true, hides the toggle when false), hideOnBlur (default true, re-masks a revealed password on blur), capsLockWarning (default true, hint under the field while Caps Lock is active), passwordStrength (default false, opt-in 4-segment strength meter with the exported scorePasswordStrength heuristic and a global strengthFn override clamped 0–4), and autocomplete for text-like formats.

added: password section in HubFormsConfig (HubPasswordLabels): toggle accessible names, Caps Lock hint, strength level labels and the optional strengthFn — all localizable via provideHubForms.

added: New CSS tokens: --hub-input-password-toggle-width, --hub-input-capslock-color, --hub-input-strength-{height,gap,track,1,2,3,4}.

removed: The broken public showPassword field. Migrate to the passwordRevealed model ([(passwordRevealed)]).

Version 22.11.1 - 7/30/26, 12:00 AM

fixed: ng-select-opened never reached the host in apps without a global tick (zoneless / OnPush islands), so the 22.11.0 caret flip did not engage: open() refreshes only its own template via a local detectChanges(), and host bindings apply during the parent view's refresh. The class is now reflected imperatively (synchronously from open()/close() plus an effect for [isOpen]-driven writes), the same renderer mechanism as ng-select-bottom. Regression spec toggles the select under zone-based AND zoneless CD with no manual tick.

Version 22.11.0 - 7/29/26, 12:00 AM

fixed: The select caret never rendered: the vendored engine ships .ng-arrow as a 0×0 span (CSS border-triangle) and the theme only published border-color — a colour on a borderless box. The full declaration (border-style + token-driven border-width) now ships, the wrapper gains inline clearance so the triangle does not touch the value, and the caret flips upwards while the panel is open. Regression spec asserts the complete closed AND open declarations.

added: --hub-select-arrow-size (5px — the border of the caret triangle) and --hub-select-arrow-gap (var(--hub-ref-space-2, 0.5rem) — the wrapper's inline clearance), so dense layouts that already tune --hub-select-font-size / -padding-x / -min-height can scale the caret on the same axis.

Version 22.10.0 - 7/28/26, 12:00 AM

changed: Accent resolution dedup: the private copy of resolveHubAccent was dropped for the canonical helper from ng-hub-ui-utils (new peer >=22.7.0, co-installed by ng add). No behaviour change.

Version 22.9.0 - 7/27/26, 12:00 AM

added: hub-select async/tagging passthrough (dropdown format): addTag (true or a term-mapping function, sync or Promise), addTagText, minTermLength, typeahead (a Subject<string> receiving term changes for server-side loading) and compareWith (custom item/value equality, applied only when provided).

added: hubSegmentedOption template — <hub-segmented> accepts a projected <ng-template hubSegmentedOption let-option let-selected="selected" let-index="index"> replacing each segment's content (icons, badges, rich markup) while the component keeps owning selection, keyboard navigation and ARIA. Exported as HubSegmentedOptionDirective + HubSegmentedOptionContext.

fixed: required is reflected to assistive technology on every field: hub-select forwards aria-required to the vendor's combobox search input (via inputAttrs), hub-segmented sets it on the radiogroup surface and hub-otp on each cell.

fixed: required derivation now also works with direct [formControl] binding — the Validators.required introspection previously only ran with a formControlName string.

Version 22.8.0 - 7/9/26, 12:00 AM

added: --hub-select-dropdown-zindex — canonical spelling of the select dropdown stacking hook (zindex without a hyphen, matching --hub-sys-zindex-*), read first at the consumption point so setting it anywhere in the cascade wins.

deprecated: --hub-select-dropdown-z-index (hyphenated spelling) keeps working but is scheduled for removal after one release cycle; migrate overrides to --hub-select-dropdown-zindex.

Version 22.7.0 - 7/9/26, 12:00 AM

added: <hub-file-input> dropzone chrome is themeable end to end. An icon medallion (--hub-file-input-icon-bg / -icon-chip-size / -icon-chip-radius), a browse action that can render as a button (--hub-file-input-browse-bg / -padding-* / -radius / -text-decoration, plus an optional leading glyph), a second invitation line (dropSubtext label + [dropSubtext] input, and [dropText] to override the first one per instance), and the hubFileDropzoneNotice slot for arbitrary markup above the glyph. Every default is unchanged, so no existing consumer moves a pixel.

Version 22.6.1 - 7/9/26, 12:00 AM

fixed: <hub-file-input> dropped the uploader's response body. HubFileUploadEvent carries a response on done — typically the record the server created — but the component discarded it, leaving the application unable to reference the file it had just uploaded. HubFileItem now exposes a response field, populated on done and reset to null when the upload is retried or cancelled.

Version 22.6.0 - 7/9/26, 12:00 AM

added: New <hub-file-input> control — a full field (extends HubFieldControl) for picking files: single or multiple, drag & drop, clipboard paste, accept / maxSize / minSize / maxTotalSize / maxFiles constraints enforced on drops and pastes (the native accept attribute only filters the OS dialog), duplicate detection, and list / grid previews with image thumbnails. The form value stays native (File | File[] | null), so it goes straight into a FormData; the rich per-file state lives in the files() signal.

added: Optional upload support: register a HubFileUploader with provideHubFileUploader() and the field drives per-file progress, cancel and retry. The event carries raw loaded/total bytes so an unknown total renders an indeterminate bar instead of a stalled 0%; cancel() unsubscribes, which aborts the underlying HttpClient request. The library ships the contract, never the transport.

added: Six exportable validators — hubAcceptedFiles, hubMaxFileSize, hubMinFileSize, hubMaxTotalSize, hubMaxFiles, hubMinFiles — with default messages wired into invalidFeedbackTemplateFn. The component inputs filter; the validators invalidate, which also catches a value patched in programmatically.

added: Customization: 66 --hub-file-input-* tokens (every icon is a swappable CSS mask), the hub-file-input-theme(...) one-call mixin, projected <ng-template hubFileIcon> / <ng-template hubFilePreview> templates, and localizable labels via provideHubForms({ fileInput: … }).

deprecated: hub-input: type="file" (with its accept, multiple and buttonLabel inputs) is deprecated in favour of <hub-file-input> and will be removed in the next major. It is a bare picker: no drag & drop, no size limits, no preview, no per-file removal, and its accept is not enforced on a drop.

Version 22.5.0 - 7/7/26, 12:00 AM

added: <hub-segmented> [color] now accepts ANY colour — a semantic accent name, a registered custom accent, or a literal #hex / rgb() / oklch(). It feeds a single --hub-segmented-accent slot and the selected pill derives a legible contrast text automatically.

added: hub-forms-theme(...) mixin — one-call theming for the shared field chrome (--hub-form-* focus ring, invalid/valid colours, disabled opacity, transition). @use 'ng-hub-ui-forms/styles' as *;

changed: BREAKING: SCSS now ships at ng-hub-ui-forms/styles (was src/lib/styles), so @use 'ng-hub-ui-forms/styles' resolves; the segmented per-variant @each is replaced by a --hub-segmented-accent-derived rule.

Version 22.4.0 - 7/5/26, 12:00 AM

added: New <hub-segmented> control — a segmented control field (extends HubFieldControl, so it carries label/labelType/formText + validation). Single-select (radiogroup) or multiple (role=group with aria-pressed, array value) via [multiple], horizontal or vertical via [vertical], keyboard navigation, themed through --hub-segmented-* tokens.

added: hub-slider: gradient-capable fill via the new --hub-slider-track-fill token (layers a full background image over the track, single + dual thumb), plus a flush labelless mode that collapses the value-bubble headroom (--hub-slider-value-space) when showValue is off.

fixed: hub-select: a dropdown opened inside a modal no longer hides behind it — the panel stacks above HubModal via the new --hub-select-dropdown-z-index token.

deprecated: hub-select: the non-dropdown formats (format="buttons"/"checkbox"/"radio") are deprecated in favour of <hub-segmented> and will be removed in the next major. Migrate: buttons → hub-segmented; checkbox → hub-segmented [multiple]; radio → hub-segmented [vertical].

Version 22.3.0 - 6/30/26, 12:00 AM

added: hub-input: in-field affixes via the hubInputPrefix / hubInputSuffix projection slots — project a <hub-icon> (any pack, pack:variant:name shorthand), RTL-aware via logical CSS properties. It themes the icon with its --hub-input-icon-* tokens.

added: hub-input: built-in clearable input — renders an internal ✕ button (swappable --hub-input-clear-icon glyph) that resets the control and emits an empty search term.

added: hub-input: debounced typeahead — new search output and debounceTime input for live filtering / autocomplete.

Version 21.0.0 - 6/13/26, 12:00 AM

added: Initial release: hub-input (10 formats), hub-textarea, hub-slider (single + range), hub-select (dropdown + buttons/radio/checkbox), hub-datepicker (single + range), hub-form/hub-fieldset/hub-legend containers.

added: Automatic validation-error display at control, group and form level, with provideHubForms() configuration.