Librería de Utilidades para Angular | ng-hub-ui-utils

Librería de utilidades para Angular con pipes, focus management, overlays, transiciones y helpers de traducción compartidos entre componentes reutilizables.

Última actualización 20 sept 2026

Visión General

Por qué los equipos buscan esta librería

Usa esta librería de utilidades para Angular para no reescribir las piezas de bajo nivel detrás de overlays, focus traps, helpers de traducción y utilidades comunes de plantilla.

Instalación

npm install ng-hub-ui-utils

Ir a

Ideal para

  • infraestructura UI compartida
  • sistemas overlay
  • focus management
  • helpers de plantilla

Sobre utils

ng-hub-ui-utils es la base compartida detrás de muchos patrones reutilizables de UI. Resulta útil cuando los equipos Angular quieren centralizar las piezas de bajo nivel que mantienen consistentes los sistemas de overlay, la gestión del foco, los pipes y los helpers de traducción entre proyectos.

Guías de uso

Gestión del foco

Atrapa y gestiona el foco del teclado dentro de elementos

Ejemplos:
Atrapado de foco

Mantén el foco dentro de un modal o diálogo usando hubFocusTrap().

Focus Trap Demo

Click "Enable Focus Trap" and try to Tab outside the blue box. Focus will stay trapped inside.

Focus Trap Area

Status: 🔓 Free

Focusable Elements

The selector FOCUSABLE_ELEMENTS_SELECTOR finds all focusable elements.

getFocusableBoundaryElements(container)→ [first, last] elements
Elementos enfocables

Obtén los elementos enfocables de los extremos con getFocusableBoundaryElements().

Focus Trap Demo

Click "Enable Focus Trap" and try to Tab outside the blue box. Focus will stay trapped inside.

Focus Trap Area

Status: 🔓 Free

Focusable Elements

The selector FOCUSABLE_ELEMENTS_SELECTOR finds all focusable elements.

getFocusableBoundaryElements(container)→ [first, last] elements

Internacionalización (i18n)

Servicios y pipes de traducción para soporte multiidioma

Ejemplos:
Servicio de traducción

HubTranslationService para gestionar traducciones

TranslatePipe - Basic usage

'welcome' | translate→ Welcome to ng-hub-ui

TranslatePipe - With parameters

'greeting' | translate:{ name: 'Carlos' }→ Hello, Carlos!

TranslatePipe - Nested keys

'buttons.save' | translate→ Save
'buttons.cancel' | translate→ Cancel

Programmatic access

translationService.getTranslation('buttons.delete')→ Delete
Pipe de traducción

TranslatePipe para traducciones en templates

TranslatePipe - Basic usage

'welcome' | translate→ Welcome to ng-hub-ui

TranslatePipe - With parameters

'greeting' | translate:{ name: 'Carlos' }→ Hello, Carlos!

TranslatePipe - Nested keys

'buttons.save' | translate→ Save
'buttons.cancel' | translate→ Cancel

Programmatic access

translationService.getTranslation('buttons.delete')→ Delete
Proveedor de traducción

provideHubTranslations() para la configuración de la app

TranslatePipe - Basic usage

'welcome' | translate→ Welcome to ng-hub-ui

TranslatePipe - With parameters

'greeting' | translate:{ name: 'Carlos' }→ Hello, Carlos!

TranslatePipe - Nested keys

'buttons.save' | translate→ Save
'buttons.cancel' | translate→ Cancel

Programmatic access

translationService.getTranslation('buttons.delete')→ Delete
Adaptador i18n externo

Configura un puente reactivo de traducciones en app.config.ts para todas las bibliotecas Hub UI.

Configura un puente reactivo de traducciones en app.config.ts para todas las bibliotecas Hub UI.

Código
Import:
Template:
Component:

Sistema de superposición

Crea overlays posicionados y elementos flotantes

Ejemplos:
Servicio de overlay

HubOverlayService para crear overlays de forma programática

A connected overlay positioned below the trigger, with a backdrop that closes it on outside click.

Referencia de overlay

OverlayRef para gestionar el ciclo de vida del overlay

A connected overlay positioned below the trigger, with a backdrop that closes it on outside click.

Posicionamiento

ConnectionPositionPair para un posicionamiento flexible

A connected overlay positioned below the trigger, with a backdrop that closes it on outside click.

Servicio popup

Gestión simplificada de popups

Ejemplos:
Creación de popup

HubPopupService para crear popups rápidamente

Nothing open.

Pipes

Pipes de utilidad para templates

Ejemplos:
Pipes de comprobación de tipo

IsStringPipe, IsObjectPipe, IsObservablePipe

GetPipe - Access nested properties

user | get:'profile.name'→ John Doe
user | get:'profile.address.city'→ New York

IsStringPipe - Type checking

'Hello' | isString→ true
123 | isString→ false

IsObjectPipe - Object detection

{ a: 1 } | isObject→ true
'string' | isObject→ false

UcfirstPipe - Capitalize first letter

'hello world' | ucfirst→ Hello world

UnwrapAsyncPipe - Observable or plain value, same template

status$ | unwrapAsync→ nothing emitted yet
'not an observable' | unwrapAsync→ not an observable
GetPipe

Accede a propiedades anidadas usando notación punto

GetPipe - Access nested properties

user | get:'profile.name'→ John Doe
user | get:'profile.address.city'→ New York

IsStringPipe - Type checking

'Hello' | isString→ true
123 | isString→ false

IsObjectPipe - Object detection

{ a: 1 } | isObject→ true
'string' | isObject→ false

UcfirstPipe - Capitalize first letter

'hello world' | ucfirst→ Hello world

UnwrapAsyncPipe - Observable or plain value, same template

status$ | unwrapAsync→ nothing emitted yet
'not an observable' | unwrapAsync→ not an observable
UcfirstPipe

Convierte en mayúscula la primera letra de un string

GetPipe - Access nested properties

user | get:'profile.name'→ John Doe
user | get:'profile.address.city'→ New York

IsStringPipe - Type checking

'Hello' | isString→ true
123 | isString→ false

IsObjectPipe - Object detection

{ a: 1 } | isObject→ true
'string' | isObject→ false

UcfirstPipe - Capitalize first letter

'hello world' | ucfirst→ Hello world

UnwrapAsyncPipe - Observable or plain value, same template

status$ | unwrapAsync→ nothing emitted yet
'not an observable' | unwrapAsync→ not an observable
UnwrapAsyncPipe

Desempaqueta observables y promesas en templates

GetPipe - Access nested properties

user | get:'profile.name'→ John Doe
user | get:'profile.address.city'→ New York

IsStringPipe - Type checking

'Hello' | isString→ true
123 | isString→ false

IsObjectPipe - Object detection

{ a: 1 } | isObject→ true
'string' | isObject→ false

UcfirstPipe - Capitalize first letter

'hello world' | ucfirst→ Hello world

UnwrapAsyncPipe - Observable or plain value, same template

status$ | unwrapAsync→ nothing emitted yet
'not an observable' | unwrapAsync→ not an observable

Funciones de utilidad

Funciones helper de propósito general

Ejemplos:
Type guards

isString(), isNumber(), isDefined(), isPromise()

Type Guards

isString('hello')→ true
isNumber(42)→ true
isDefined(null)→ false
isDefined('value')→ true

Deep Equality

equals({a:1}, {a:1})→ true
equals([1,2], [1,2])→ true

Object Access

getValue(user, 'profile.name')→ John Doe

String Interpolation

interpolateString('Hello {{name}}', {name: 'World'})→ Hello World

String Utilities

removeAccents('Ñoño café')→ Nono cafe
padNumber(5)→ 05
Utilidades de strings

removeAccents(), interpolateString(), regExpEscape()

Type Guards

isString('hello')→ true
isNumber(42)→ true
isDefined(null)→ false
isDefined('value')→ true

Deep Equality

equals({a:1}, {a:1})→ true
equals([1,2], [1,2])→ true

Object Access

getValue(user, 'profile.name')→ John Doe

String Interpolation

interpolateString('Hello {{name}}', {name: 'World'})→ Hello World

String Utilities

removeAccents('Ñoño café')→ Nono cafe
padNumber(5)→ 05
Utilidades de objetos

equals() para comparación profunda, getValue() para notación de puntos

Type Guards

isString('hello')→ true
isNumber(42)→ true
isDefined(null)→ false
isDefined('value')→ true

Deep Equality

equals({a:1}, {a:1})→ true
equals([1,2], [1,2])→ true

Object Access

getValue(user, 'profile.name')→ John Doe

String Interpolation

interpolateString('Hello {{name}}', {name: 'World'})→ Hello World

String Utilities

removeAccents('Ñoño café')→ Nono cafe
padNumber(5)→ 05
Utilidades del DOM

closest(), reflow(), getActiveElement()

closest()

Click anywhere inside; the helper walks up to the nearest panel.

outer
inner

closest(target, '[data-panel]') → —

reflow()

Replaying an animation means removing the class and adding it back. Without a forced reflow the browser coalesces both into no change at all.

replay me
getActiveElement()

Focus either control. The second one lives inside a shadow root.

document.activeElement → —

getActiveElement() → —

runInZone()

Both streams are created with runOutsideAngular; only one is piped through the operator.

plain subscriberisInAngularZone() → —
piped through runInZone(zone)isInAngularZone() → —
ticks0
Utilidades RxJS

Operador runInZone() para la integración con NgZone

closest()

Click anywhere inside; the helper walks up to the nearest panel.

outer
inner

closest(target, '[data-panel]') → —

reflow()

Replaying an animation means removing the class and adding it back. Without a forced reflow the browser coalesces both into no change at all.

replay me
getActiveElement()

Focus either control. The second one lives inside a shadow root.

document.activeElement → —

getActiveElement() → —

runInZone()

Both streams are created with runOutsideAngular; only one is piped through the operator.

plain subscriberisInAngularZone() → —
piped through runInZone(zone)isInAngularZone() → —
ticks0

Utilidades de scrollbar

Mide y compensa el ancho del scrollbar

Ejemplos:
Ancho del scrollbar

Funciones scrollbarWidth() y scrollbarPadding()

ScrollBar Service

The ScrollBar service helps manage scrollbar visibility and compensate for layout shifts when hiding scrollbars (e.g., when opening modals).

Use Case: Modal Body Lock

When opening a modal, you typically hide the body scrollbar. The ScrollBar.hide() method handles this automatically and returns a reverter function.

Status: 🔓 Normal scrolling

Code Example

import { inject } from '@angular/core';
import { ScrollBar } from 'ng-hub-ui-utils';

export class ModalService {
  private scrollBar = inject(ScrollBar);
  private revertScrollbar: (() => void) | null = null;

  openModal() {
    // Hide scrollbar and get reverter function
    this.revertScrollbar = this.scrollBar.hide();
  }

  closeModal() {
    // Restore scrollbar
    if (this.revertScrollbar) {
      this.revertScrollbar();
      this.revertScrollbar = null;
    }
  }
}

Transiciones

Helpers de transición CSS

Ejemplos:
Ejecutar transición

hubRunTransition() para transiciones CSS programáticas

transition: height 600ms · measured at 0 ms
  1. Run a transition to see when the observable completes.

Tooltip

Tooltips ligeros y tematizables para cualquier elemento mediante la directiva [hubTooltip].

Ejemplos:
Directiva Tooltip

Aplica [hubTooltip] con hubTooltipPlacement para mostrar una etiqueta posicionada al pasar el cursor o enfocar, tematizada con variables --hub-tooltip-*. La antigua [tooltip] sigue funcionando, pero está obsoleta: sus nombres de entrada sin prefijo chocan con otras directivas del mismo elemento.

Placements (hover the buttons)
Themed with --hub-tooltip-* variables

Color

Analiza, mide y convierte colores CSS sin necesidad del DOM

Ejemplos:
Análisis de color

parseColor(), toHex() e isValidColor() resuelven hex, rgb(), hsl(), oklch(), oklab() y los 148 colores con nombre, también en renderizado en servidor

Try one:

#0b6eff
parsed
parseColor(){ r: 11, g: 110, b: 255, a: 1 }
toHex()#0b6eff
rgbToOklch(){ l: 0.58, c: 0.23, h: 260 }
isValidColor()true
toRgb()already parsed — same object back
HUB_NAMED_COLORSnot a bareword — 148 in the table
Contraste y legibilidad

contrastRatio() (WCAG 2), contrastAPCA() y readableOn(), que elige la tinta que coincide con el token --hub-sys-color-*-on

readableOn(accent, metric)
Agrees with the design-system token on all nine
primary
#0d6efd

ink#ffffff
WCAG4.5:1
APCA-75.8
secondary
#6c757d

ink#ffffff
WCAG4.69:1
APCA-78
success
#198754

ink#ffffff
WCAG4.53:1
APCA-76.5
danger
#dc3545

ink#ffffff
WCAG4.53:1
APCA-75.4
warning
#ffc107

ink#000000
WCAG12.88:1
APCA76
info
#0dcaf0

ink#000000
WCAG10.72:1
APCA66.9
neutral
#6c757d

ink#ffffff
WCAG4.69:1
APCA-78
light
#f8f9fa

ink#000000
WCAG19.92:1
APCA102.4
dark
#212529

ink#ffffff
WCAG15.43:1
APCA-105

A highlighted border marks a chip whose ink differs from what --hub-sys-color-*-on paints in CSS.


Translucent ink has to be composited before it is measured

relativeLuminance() ignores alpha, because a translucent colour has no luminance of its own until something is behind it. Measure the ink as written and you are scoring solid black; compositeOver() blends it onto the surface first, which is what the eye is reading.

Sample text
relativeLuminance(surface)0.1833
contrastRatio(ink, surface)4.67:1
…with compositeOver(ink, surface)2.75:1
Conversiones OKLCh

rgbToOklch(), oklchToRgb(), maxSrgbChroma() y clampToSrgbGamut() para trabajar paletas en el espacio en el que mezcla el sistema de diseño

red 21°
0.232
amber 85°
0.118
green 157°
0.138
cyan 218°
0.104
blue 260°
0.232
purple 320°
0.277

clampToSrgbGamut()

Asking for chroma 0.35 on the amber hue at the current lightness.

clipped per channel#d24900
chroma reduced#9a7300
in gamut as asked: false
chroma kept: 0.118
Derivación de paleta

harmoniseSemantics() gira success, warning, danger e info hacia el tono de la marca como mucho 15°, y tintNeutrals() inclina la escala de grises igual, con el croma limitado a 0,015. La claridad no se toca, así que el contraste por el que se eligió cada rol se mantiene.

Primary
oklch(0.55 0.17 260)
success #00866bwas #198754
warning #e7cd16was #ffc107
danger #d8336bwas #dc3545
info #44c4ffwas #0dcaf0
100
200
300
400
500
600
700
800
900

Hue rotation capped at 15°; neutral chroma capped at 0.015. Success and danger stay 173° apart.

Características clave

Cambios recientes

Version 22.15.2 - 9/20/26, 12:00 AM

changed: The package manifest carries a description and a keyword list. It was the only one of the 26 with neither, while seventeen libraries depend on it, and both fields are what npm ranks a search on. Metadata only: no code, types or styles change.

Version 22.15.1 - 9/16/26, 12:00 AM

changed: Repository, issue and README links follow the move to the hub-env organization. Issues for every Hub UI package are now gathered in hub-env/hub-ui, and the repository and bugs fields of the manifest point at the new addresses. No code, types or styles change.

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

added: harmoniseSemantics() and tintNeutrals(), so a brand colour derives the whole palette in one place instead of once per product. The first rotates success, warning, danger and info towards the brand's hue; the second leans the grey ramp the same way. Both leave lightness exactly where the anchor had it, because lightness is what carries the contrast each role was chosen for. Two exported numbers decide the result: HUB_MAX_HUE_SHIFT (15°), which is what keeps success and danger more than 90° apart for a reader with deuteranopia — they start 135.8° apart and a brand hue between them closes the gap by up to twice the cap — and HUB_MAX_NEUTRAL_CHROMA (0.015), which sits between the design system's own gray-500 (0.0145) and gray-600 (0.0165), so a tinted ramp is never more colourful than the grey people already accept as grey.

fixed: The DOM helpers no longer reach for globals a server render does not have. ScrollBar.hide(), hubRunTransition(), getTransitionDurationMs(), reflow(), getActiveElement() and OverlayRef each read window or document straight out of scope, which is a ReferenceError on the way to a prerendered page. They now resolve the document and the view from what they already hold — the injected DOCUMENT, the application's injector, or the element's own ownerDocument.defaultView — which also fixes the same helpers inside an iframe, where the global was the wrong document all along.

changed: reflow() returns DOMRect | null and no longer falls back to document.body, and getActiveElement() accepts null as its root. Both are announced in BREAKING_CHANGES.md; no call that passes an element changes behaviour.

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

removed: TooltipDirective and its bare [tooltip] attribute, deprecated since 22.9.0. An unprefixed selector is a name in the application's namespace rather than the library's: Angular hands one attribute to every directive on the element that declares an input of that name, so nobody else could own a tooltip — not a consumer writing their own, not <hub-badge>, which declares a tooltip input and ended up drawing two, and not [hubDropdown], whose own placement is typed over eight values where a tooltip understands four. [hubTooltip] is the replacement, attribute for attribute: tooltip → hubTooltip, placement → hubTooltipPlacement, delay → hubTooltipDelay, offset → hubTooltipOffset. Breaking, and note that a template still writing tooltip="…" keeps compiling and silently shows nothing — see BREAKING_CHANGES.md.

added: A guard over the whole entry point: no directive of this package may claim a bare attribute. It reads Angular's own compiled definitions rather than a hand-kept list, so the rule covers the directive somebody adds next and not only the one just removed.

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

added: OverlayPosition.origin, the element the strategy is connected to. It was known to the strategy and to nobody else, and an overlay that has to keep up with its anchor needs to be able to ask which element that is.

fixed: A floating panel stayed behind the moment its trigger moved. The overlay recomputed its position on scroll and on resize, and both of those describe the page moving under an origin that stays put. Nothing covered the opposite, the origin moving inside a page nobody scrolled and no window resized: a section collapsing above it, an image landing late, a sidebar accordion animating shut. The panel then hung at the height the trigger used to have, cut loose from the thing it belongs to, and every connected overlay in the family opens through this service, so all of them had it. The origin is now watched for as long as the panel is open and the panel is re-placed whenever its box actually changes, every frame of an animated collapse rather than once at the start of one. The box is read per frame and nothing is written unless it moved, so an overlay whose trigger sits still never touches the DOM, and the loop runs outside Angular zone so a zone-based application does not run change detection while a menu is open.

Version 22.12.1 - 9/6/26, 12:00 AM

changed: Every row of FUNCTIONALITIES.md now points at a runnable demo. Fifteen features (the popup service, the transition helpers, UnwrapAsyncPipe, the DOM and RxJS helpers and five of the colour functions) were listed as having no example, which left the unit tests as the only executable use of them. Each of those now has a demo on the documentation site, so the table reports coverage instead of a wishlist. No API changed.

fixed: The tooltip and overlay stylesheets resolve at the path the documentation gives. The manifest declared no exports map, so ng-packagr synthesised one for the published package, and a synthesised map lists only "." and "./package.json". A package that declares exports closes every subpath outside that map, so @use "ng-hub-ui-utils/styles/tooltip"; resolved to nothing even though the sheet shipped in styles/. Both sheets are now named in the manifest, extensionless and with the .scss suffix, the way every sibling package in the family already does, and ng-packagr merges those entries into the map it generates instead of replacing them.

fixed: The tooltip is announced to assistive technology and can be dismissed without a mouse. The bubble was a bare span with no id and no role, and the host was never pointed at it, so on an icon-only button, the case the tooltip is written for, a screen reader had nothing to read and the only workaround was an aria-label repeating the same text. The bubble is now role="tooltip" with an id the host is aria-describedby while it is on screen, and the attribute is put back exactly as it was found, so a description a consumer wrote is neither replaced nor left behind. The same change closes WCAG 1.4.13 for it: Escape dismisses the label without moving the pointer or the focus, and the label waits out a short grace period and stays put once the pointer lands on it, which is the only way to read one longer than its box. styles/tooltip.scss therefore ships pointer-events: auto instead of none, and the controller disables them again the moment it starts fading, so an invisible bubble never catches a click meant for what is under it. It is reached through HubTooltipController, so it arrives at all four entry points at once: [hubTooltip], the deprecated [tooltip], [hubOverflowTooltip] and hubTooltipAdapter.

Version 22.12.0 - 9/3/26, 12:00 AM

added: Colour utilities — parseColor(), toHex(), isValidColor(), relativeLuminance(), contrastRatio(), contrastAPCA(), readableOn(), compositeOver() and the OKLCh helpers rgbToOklch(), oklchToRgb(), maxSrgbChroma(), isInSrgbGamut() and clampToSrgbGamut(). The parser resolves hex (3/4/6/8 digits), rgb(), hsl(), oklch(), oklab() and the 148 CSS named colours in both modern and legacy syntax, with no DOM involved, so it runs under server-side rendering. It returns null rather than throwing on anything it cannot resolve.

added: readableOn() picks black or white by OKLCh perceptual lightness, the same decision the --hub-sys-color-*-on token computes in CSS, so a component that resolves its ink in TypeScript cannot disagree with the stylesheet. The alternative metrics are available: maximising the WCAG 2 ratio puts black text on the design system's own blue, green and red accents, which is why it is not the default.

Version 22.11.1 - 9/1/26, 12:00 AM

changed: The homepage in the manifest points at this library's own documentation page rather than at the site root, so the link a registry shows beside the package lands on the reference for the package the reader was already looking at. Metadata only.

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

added: The overlay follows its origin. While attached it listens for scroll and resize in the capture phase and recomputes its position, coalesced into an animation frame. Before this it computed coordinates once and never again: a panel opened and then scrolled sat 122px away from the field it belonged to.

added: start and end are logical. They resolved to left and right whatever the direction, so an overlay opened from a field inside an RTL container hung off the wrong edge. The direction is read from the origin element; OverlayPosition.withDirection() overrides it.

added: OverlayRef.onKeydown(), and with it a document-level dispatcher that tells only the topmost open overlay. An overlay rarely holds focus, so a component listening on its own host never heard Escape and the panel that took over the screen could not be dismissed with the key everyone reaches for.

added: HUB_DROPDOWN_POSITIONS — the four-position fallback chain a dropdown wants, below the origin and flipping above when there is no room, expressed logically so one list serves both text directions.

fixed: Tearing an overlay down twice no longer throws. dispose() and detach() called removeChild on nodes something else may already have removed — a test teardown, a router navigation — and the DOMException took the whole destroy path with it.

Version 22.10.0 - 8/22/26, 12:00 AM

added: --hub-tooltip-white-space and --hub-tooltip-text-align, so how a label breaks and sits can be asked for per tooltip instead of through a global rule that changes every tooltip in the product. Both default to what was hard-coded, so nothing moves for anyone who says nothing.

Version 22.9.3 - 8/19/26, 12:00 AM

fixed: A tooltip whose stylesheet was never imported no longer moves the page. The element now takes position: absolute inline at creation — the same value the sheet ships — so it stops landing in normal flow at the end of the document and growing the page a scrollbar that appeared and vanished as the pointer crossed a label.

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

fixed: The stylesheets are published under styles/, so @use 'ng-hub-ui-utils/styles/tooltip' names a real path instead of reaching through the package's internal folder layout.

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

fixed: The published package declares its licence. An absent license field is not neutral — a registry reports it as unlicensed, which legally reads as all rights reserved. The intent was always MIT.

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

added: [hubTooltip], a tooltip directive that can share an element. Its inputs are hubTooltip, hubTooltipPlacement, hubTooltipDelay and hubTooltipOffset: an attribute named for its owner cannot be claimed by anyone else, which is what the bare names could not promise next to [hubDropdown] (its own placement is typed over eight values) or <hub-badge> (which declares a tooltip input and drew two).

deprecated: TooltipDirective / [tooltip]. Kept working, unchanged — both directives are thin shells over the same HubTooltipController. Migration is attribute for attribute: tooltip → hubTooltip, placement → hubTooltipPlacement, delay → hubTooltipDelay, offset → hubTooltipOffset.

Version 22.8.1 - 8/15/26, 12:00 AM

fixed: A content-sized overlay no longer clips its own content into invisibility. Created with no intrinsic size, it computed to a 0×0 box whenever its content was absolutely positioned — which is exactly what a connected-position dropdown is — and the stylesheet's overflow: auto then hid what the overlay existed to display. An overlay created without an explicit width or height now opts out of clipping.

Version 22.8.0 - 8/14/26, 12:00 AM

added: provideHubTranslationAdapter() — the application-wide reactive bridge from an external translation service (transloco, ngx-translate, i18next…) into HubTranslationService. Register it once at bootstrap and every ng-hub-ui library picks up the host dictionary, re-emitting on every language change. Supports optional namespacing and deliberate per-label reactive overrides.

added: HUB_TRANSLATION_PREFIX — injection token that scopes the lookups of a library to a collision-safe HUBUI.<LIBRARY>.* namespace. TranslatePipe resolves the prefixed key first and falls back to the bare key, so existing flat dictionaries keep working untouched.

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

fixed: Documentation links now point at the canonical localized URLs. The README linked to https://hubui.dev/<path> with no locale prefix and no trailing slash, and both forms are 301-redirected, so every reader arriving from npm or GitHub landed on a redirect instead of the canonical page.

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

fixed: --hub-overlay-zindex / --hub-overlay-backdrop-zindex actually work now: OverlayRef resolves its inline z-index through var(--hub-overlay-zindex, 1000) / var(--hub-overlay-backdrop-zindex, 999) instead of literal values, so re-stacking an overlay no longer requires !important. Defaults are unchanged.

added: OverlayConfig.zIndex — optional explicit layer for a single overlay instance; when set it takes precedence over the token.

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

added: resolveHubAccent(value) — the canonical "any colour" accent resolver shared across the ng-hub-ui family: barewords map to var(--hub-sys-color-<name>, <name>), literal #hex / rgb() / oklch() / var() values pass through unchanged, empty values yield null.

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

fixed: CSS variable fallbacks realigned to the ds light defaults (e.g. --hub-ref-font-family-base falls back to the system-ui stack instead of inherit); fallbacks only apply when ng-hub-ui-ds is not loaded.

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

added: HubOverflowTooltipDirective ([hubOverflowTooltip]) — shows a tooltip only while the label is actually truncated, with live tracking via ResizeObserver + MutationObserver. hubOverflowTooltipMeasure splits the two questions a chip asks separately: the tooltip covers the whole control, while a CSS selector resolved inside it names the inner box whose truncation decides whether it speaks.

added: Agnostic tooltip token — HUB_TOOLTIP_ADAPTER plus provideHubTooltip(adapter) let any tooltip implementation back [hubOverflowTooltip], app-wide or per subtree; defaults to the built-in hubTooltipAdapter.

Preguntas frecuentes

¿Qué es ng-hub-ui-utils y hace falta instalarlo?

Es la caja de herramientas común sobre la que está construido el resto de la familia, y no trae ningún componente visual. Diecisiete de las librerías lo declaran como peer obligatorio —board, modal, paginable, panels, calendar, forms y casi todas las demás—, así que si usas cualquiera de ellas ya lo tienes en el árbol, y npm instala los peers por su cuenta desde la versión 7. Instalarlo por separado también vale: ng-hub-ui-ds es su único peer opcional.

¿Qué hay dentro de ng-hub-ui-utils?

El núcleo nativo de drag and drop que comparten board, sortable y calendar —moveItemInArray, transferArrayItem, resolveDropPosition y una sesión de puntero que cubre el táctil—, además de ayudas de foco como hubFocusTrap y getFocusableBoundaryElements, ayudas de transición, un servicio de overlay con posicionamiento conectado, un kit de color que interpreta cualquier color CSS, convierte a OKLCh y mide el contraste, una directiva de tooltip, siete pipes y los guardas de tipo y las ayudas de objetos de siempre.

¿Puedo usarlo directamente en mi aplicación?

Sí, y para la mayor parte no hay que registrar nada: importa isString, HubGetPipe o hubFocusTrap y úsalos. Dos subsistemas sí necesitan configuración. HubTranslationService no tiene providedIn, así que provideHubTranslation() va primero o la inyección falla, y la directiva de tooltip necesita provideHubTooltip(hubTooltipAdapter). Sus hojas de estilo tampoco vienen empaquetadas: añade tú @use 'ng-hub-ui-utils/styles/tooltip' o el equivalente del overlay.

¿Es una alternativa al CDK de Angular?

No, y tampoco lo pretende. No hay abstracción de portal, ni estrategias de scroll, ni LiveAnnouncer ni FocusMonitor, ni scroll virtual ni observador de breakpoints, y del drag and drop están solo las primitivas: las directivas de handle, placeholder y preview se quedan dentro de cada librería que las necesita. La capa de traducción es un diccionario con búsqueda por ruta de puntos e interpolación con llaves dobles: sin plurales, sin ICU, sin carga diferida, y con un token de adaptador para delegar en una librería de i18n de verdad. Es el suelo sobre el que se apoya esta familia, no un kit de propósito general.