ng-hub-ui-ds 22.11.4 → 22.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,74 @@ All notable changes to `ng-hub-ui-ds` are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [22.12.0] - 2026-09-23
9
+
10
+ ### Fixed
11
+
12
+ - **The `-emphasis` role is readable on a dark surface again.** Its lightness was capped at
13
+ `min(l, 0.45)`, a rule written for ink on paper, so on the dark theme it pushed every accent
14
+ down instead of up. The five accents `[data-theme='dark']` re-tints hid the damage; the four it
15
+ does not — `secondary`, `neutral`, `light`, `dark` — showed it. Measured over
16
+ `--hub-sys-surface-elevated` (`#1e1e1e`): `neutral-emphasis` 2.25:1 → **6.74:1**,
17
+ `secondary-emphasis` 2.25:1 → **6.74:1**, `light-emphasis` 2.24:1 → **7.06:1** (via `#f8f9fa`),
18
+ `dark-emphasis` 1.08:1 → **6.73:1**. The five re-tinted accents were failing too —
19
+ `primary-emphasis` 2.21:1, `danger-emphasis` 2.10:1, `info-emphasis` 2.32:1 — and now read
20
+ 6.90:1, 6.59:1 and 10.76:1. The `terminal` theme had the same nine failures, floor 2.26:1, and
21
+ now bottoms out at 6.89:1. Nothing changes on the light themes: their emphasis values are
22
+ byte-identical.
23
+ - **The focus ring is an indicator rather than a wash.** `rgba(13, 110, 253, 0.25)` composited
24
+ over the page at 1.41:1 where WCAG 1.4.11 asks 3:1, and the per-theme overrides were worse —
25
+ `sunset` 1.28:1, `mono` 1.41:1, `terminal` 2.89:1, `dark` 1.97:1. The ring is now the opaque
26
+ `--hub-sys-color-primary-emphasis` and measures 6.35:1 at its worst across the seven built-ins
27
+ (`forest`), 14.05:1 at its best (`mono`). Width drops from `0.25rem` to `0.125rem` so an opaque
28
+ ring does not read as a slab; 2px is the thickness WCAG 2.2 treats as the floor.
29
+ - **Links clear 4.5:1 on both surfaces of every theme.** `--hub-sys-link-color` was the raw
30
+ primary — 4.50:1 on `#ffffff` but **4.27:1** on `#f8f9fa`, so a link inside a card failed — and
31
+ `--hub-sys-link-hover-color` was a hard-coded `#0a58ca`, a blue that meant nothing in an orange
32
+ or a green theme. Both derive now: the link from `--hub-sys-color-primary-emphasis`, the hover
33
+ by walking that colour toward `--hub-sys-color-ink`, which darkens on a light theme and lightens
34
+ on a dark one. Worst link in the package is now 6.35:1 (`forest`), worst hover 7.47:1. The
35
+ `sunset` literal that cleared the bar by two hundredths (4.52:1) is gone with them.
36
+ - **`.focus-ring-{variant}` re-tints the ring opaquely.** The `focus-ring-color()` mixin mixed the
37
+ accent to 25% alpha, which put every one of those utilities near 1.3:1. It now reads the
38
+ variant's `-emphasis` role.
39
+
40
+ ### Added
41
+
42
+ - **`--hub-sys-border-color-strong`** — the boundary of an interactive control, which 1.4.11 wants
43
+ at 3:1. `--hub-sys-border-color-default` is the decorative hairline and measures 1.30:1 on white,
44
+ so a control drawn with it has no visible edge. Derived from the theme's own ink and surface, so
45
+ it needs no per-theme literal: 5.21:1 on the light themes, 3.42:1 at its tightest (`sunset`).
46
+ - **`--hub-sys-emphasis-lightness-min` / `--hub-sys-emphasis-lightness-max`** — the lightness
47
+ window the `-emphasis` role is steered into, `0`/`0.45` on a light theme and `0.72`/`1` on
48
+ `dark` and `terminal`. A custom dark theme sets these two and every accent in the open map,
49
+ including ones the consumer added, comes out readable.
50
+ - **`npm run check:contrast`** (`scripts/check-contrast.mjs`) — measures every accent, link, focus
51
+ ring and control border of the compiled sheet against both surfaces of every theme it finds, and
52
+ exits non-zero if a shipped default drops under its threshold. Themes and accents are discovered
53
+ from the CSS, so a new one is covered without touching the script. It runs on `prepublishOnly`.
54
+
55
+ ### Changed
56
+
57
+ - The `bootstrap`, `dark`, `sunset`, `forest` and `mono` themes no longer carry their own
58
+ `--hub-sys-link-color` / `--hub-sys-link-hover-color`, and none of the six themes carries its own
59
+ `--hub-sys-focus-ring-color`. What derives in their place is the same colour or a better one —
60
+ for `dark` it resolves to the very `#6ea8fe` the block used to spell out. `terminal` keeps its
61
+ cyan links, which are deliberate and measure 11.75:1.
62
+
63
+ ### Removed
64
+
65
+ - `--hub-focus-ring-opacity`. It existed to thin the ring, which is the defect.
66
+
67
+ ## [22.11.5] - 2026-09-20
68
+
69
+ ### Changed
70
+
71
+ - The npm keywords say that this package is the token layer — `tokens`, `css-custom-properties`,
72
+ `theming`, `themes`, `color-palette`, `spacing`, `typography` — and that it is
73
+ `framework-agnostic`, which is the reason somebody outside Angular would install it at all.
74
+ Metadata only: no token, variable or style changes.
75
+
8
76
  ## [22.11.4] - 2026-09-16
9
77
 
10
78
  ### Changed
@@ -55,14 +123,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
55
123
  inherited unreadable text through `.text-warning-emphasis` and through any component
56
124
  reading the role, and none of them could fix it, because the formula lives here.
57
125
 
58
- The luminosity is capped instead of mixed — `oklch(from <accent> min(l, .45) c h)` —
59
- which leaves an already-dark accent exactly where it was and pulls a light one down to
60
- where it can carry a letter, keeping its hue and its chroma. Measured on white across the
61
- nine defaults: nothing below 6.6:1 — info is the floor at 6.62, success next at 6.91 —
62
- and no role moves by more than a point unless it was failing. `dark` is untouched, since
63
- it already sat at L .26.
126
+ The luminosity is capped instead of mixed — `oklch(from <accent> min(l, .45) c h)` —
127
+ which leaves an already-dark accent exactly where it was and pulls a light one down to
128
+ where it can carry a letter, keeping its hue and its chroma. Measured on white across the
129
+ nine defaults: nothing below 6.6:1 — info is the floor at 6.62, success next at 6.91 —
130
+ and no role moves by more than a point unless it was failing. `dark` is untouched, since
131
+ it already sat at L .26.
132
+
133
+ Themes that assign `--hub-sys-color-*-emphasis` by hand are unaffected.
64
134
 
65
- Themes that assign `--hub-sys-color-*-emphasis` by hand are unaffected.
66
135
  ## [22.10.0] - 2026-09-08
67
136
 
68
137
  ### Added
package/README.es.md CHANGED
@@ -145,11 +145,11 @@ propios estilos:
145
145
 
146
146
  Los tokens siguen un sistema por capas:
147
147
 
148
- | Capa | Prefijo | Qué es | Ejemplos |
149
- | --------------- | ------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------- |
150
- | **Referencia** | `--hub-ref-*` | Valores crudos, sin contexto | `--hub-ref-color-blue-500`, `--hub-ref-space-3`, `--hub-ref-radius-md` |
151
- | **Sistema** | `--hub-sys-*` | Asignaciones con significado que consumen los componentes | `--hub-sys-color-primary`, `--hub-sys-surface-page`, `--hub-sys-text-primary` |
152
- | **Container** | `--hub-container-*` | Puente heredable de `sys` a contenedores/slots concretos — un **re-base hook** | `--hub-container-bg`, `--hub-container-padding-x`, `--hub-container-gap` |
148
+ | Capa | Prefijo | Qué es | Ejemplos |
149
+ | ---------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
150
+ | **Referencia** | `--hub-ref-*` | Valores crudos, sin contexto | `--hub-ref-color-blue-500`, `--hub-ref-space-3`, `--hub-ref-radius-md` |
151
+ | **Sistema** | `--hub-sys-*` | Asignaciones con significado que consumen los componentes | `--hub-sys-color-primary`, `--hub-sys-surface-page`, `--hub-sys-text-primary` |
152
+ | **Container** | `--hub-container-*` | Puente heredable de `sys` a contenedores/slots concretos — un **re-base hook** | `--hub-container-bg`, `--hub-container-padding-x`, `--hub-container-gap` |
153
153
  | **Base / shell** | `--hub-body-*` · `--hub-main-*` · `--hub-main-wrapper-*` | Layout del _application shell_: el wrapper exterior de la página, la región de contenido y su wrapper centrado. `--hub-body-*` **hereda los valores por defecto de `--hub-container-*`** | `--hub-body-padding-x`, `--hub-main-bg`, `--hub-main-wrapper-max-width` (`1200px`) |
154
154
 
155
155
  Regla de oro: **los componentes referencian solo tokens `sys`**. Los `sys`
@@ -189,13 +189,13 @@ defecto es `primary` · `secondary` · `success` · `danger` · `warning` · `in
189
189
  `light` · `dark`, y es **abierto**: cada variante que añadas obtiene la misma familia (ver
190
190
  [Cómo modificarlo](#️-cómo-modificarlo)).
191
191
 
192
- | Token | Uso típico |
193
- | ----------------------------------- | ---------------------------------------------- |
194
- | `--hub-sys-color-<v>` | Color sólido (acento, icono, borde fuerte) |
195
- | `--hub-sys-color-<v>-subtle` | Fondo tenue (banners, alertas) |
196
- | `--hub-sys-color-<v>-border-subtle` | Borde tenue sobre el fondo subtle |
197
- | `--hub-sys-color-<v>-emphasis` | Texto legible sobre el fondo subtle |
198
- | `--hub-sys-color-<v>-on` | Color de texto/icono con contraste **sobre** el acento |
192
+ | Token | Uso típico |
193
+ | ----------------------------------- | --------------------------------------------------------------------------------- |
194
+ | `--hub-sys-color-<v>` | Color sólido (acento, icono, borde fuerte) |
195
+ | `--hub-sys-color-<v>-subtle` | Fondo tenue (banners, alertas) |
196
+ | `--hub-sys-color-<v>-border-subtle` | Borde tenue sobre el fondo subtle |
197
+ | `--hub-sys-color-<v>-emphasis` | Texto legible sobre la superficie — los enlaces y el halo de foco también lo leen |
198
+ | `--hub-sys-color-<v>-on` | Color de texto/icono con contraste **sobre** el acento |
199
199
 
200
200
  Ejemplo de uso (un aviso a juego con el resto de la familia):
201
201
 
@@ -216,7 +216,7 @@ contenedor para tematizar una zona):
216
216
 
217
217
  ```html
218
218
  <html data-theme="dark">
219
- <!-- light (por defecto) · base · bootstrap · dark · sunset · forest · mono · terminal -->
219
+ <!-- light (por defecto) · base · bootstrap · dark · sunset · forest · mono · terminal -->
220
220
  </html>
221
221
  ```
222
222
 
@@ -232,9 +232,9 @@ Además de los tokens, el paquete incluye una capa de estilos opt-in — no se e
232
232
  **Hojas de utilidades** — los nombres de clase son **exactamente los de Bootstrap** y cada valor resuelve a los tokens canónicos. No cargues estas hojas Y Bootstrap globalmente en el mismo documento. Cada hoja tiene además su gemela `.css` compilada para apps sin Sass:
233
233
 
234
234
  ```scss
235
- @use 'ng-hub-ui-ds/styles/utilities/layout'; // display/flex (+ responsive), grid de 12 col + offsets, spacing, sizing, position, overflow, order, .vr, .ratio-*, .visually-hidden…
236
- @use 'ng-hub-ui-ds/styles/utilities/text'; // .fs-1…6, .h1…6, .fw-*, .lh-*, .text-truncate, colores de texto semánticos
237
- @use 'ng-hub-ui-ds/styles/utilities/surfaces'; // .bg-* (+ -subtle), .text-bg-*, .border*, .rounded*, .shadow*, .opacity-*
235
+ @use 'ng-hub-ui-ds/styles/utilities/layout'; // display/flex (+ responsive), grid de 12 col + offsets, spacing, sizing, position, overflow, order, .vr, .ratio-*, .visually-hidden…
236
+ @use 'ng-hub-ui-ds/styles/utilities/text'; // .fs-1…6, .h1…6, .fw-*, .lh-*, .text-truncate, colores de texto semánticos
237
+ @use 'ng-hub-ui-ds/styles/utilities/surfaces'; // .bg-* (+ -subtle), .text-bg-*, .border*, .rounded*, .shadow*, .opacity-*
238
238
  ```
239
239
 
240
240
  Los únicos nombres fuera de Bootstrap son los primitivos de layout que reflejan los mixins: `.stack`, `.cluster`, `.grid-auto`, `.center`.
@@ -250,10 +250,21 @@ Los únicos nombres fuera de Bootstrap son los primitivos de layout que reflejan
250
250
  ```scss
251
251
  @use 'ng-hub-ui-ds' as hub;
252
252
 
253
- .toolbar { @include hub.cluster($gap: 2); }
254
- .card__title { @include hub.font-size(4); @include hub.font-weight(semibold); }
255
- .card--brand { @include hub.text-bg(brand); @include hub.radius(lg); @include hub.shadow(sm); }
256
- .sr-label { @include hub.visually-hidden(); }
253
+ .toolbar {
254
+ @include hub.cluster($gap: 2);
255
+ }
256
+ .card__title {
257
+ @include hub.font-size(4);
258
+ @include hub.font-weight(semibold);
259
+ }
260
+ .card--brand {
261
+ @include hub.text-bg(brand);
262
+ @include hub.radius(lg);
263
+ @include hub.shadow(sm);
264
+ }
265
+ .sr-label {
266
+ @include hub.visually-hidden();
267
+ }
257
268
  ```
258
269
 
259
270
  Grupos: **tema** (`theme()` — tematización parcial en una llamada: pásale solo las escalas/acentos cambiados como mapas), **breakpoints** (`media-breakpoint-up/down` sobre el mapa `$hub-breakpoints` — de él se generan las variantes responsive `.d-md-*`, `.col-lg-*`, `.p-sm-*`…), **layout** (`stack`, `cluster`, `grid`, `grid-fixed`, `row`, `col`, `offset`, `center`), **tipografía** (`font-family`, `font-size`, `font-weight`, `line-height`, `text-color`, `link-color`, `text-truncate`, `text-break`), **superficies** (`bg`, `text-bg`, `border`, `border-color`, `radius`, `shadow`), **helpers** (`focus-ring`, `focus-ring-color`, `visually-hidden`, `stretched-link`, `ratio`, `clearfix`) y los **puentes** (`bridge-bootstrap` / `bridge-material` / `bridge-tailwind` / `bridge-open-props`).
@@ -331,12 +342,12 @@ $hub-accents: (
331
342
  primary: var(--hub-ref-color-blue-500, #0d6efd),
332
343
  secondary: var(--hub-ref-color-gray-600, #6c757d),
333
344
  success: var(--hub-ref-color-green-500, #198754),
334
- danger: var(--hub-ref-color-red-500, #dc3545),
345
+ danger: var(--hub-ref-color-red-500, #dc3545),
335
346
  warning: var(--hub-ref-color-yellow-500, #ffc107),
336
- info: var(--hub-ref-color-cyan-500, #0dcaf0),
347
+ info: var(--hub-ref-color-cyan-500, #0dcaf0),
337
348
  neutral: var(--hub-ref-color-gray-600, #6c757d),
338
- light: var(--hub-ref-color-gray-100, #f8f9fa),
339
- dark: var(--hub-ref-color-gray-900, #212529)
349
+ light: var(--hub-ref-color-gray-100, #f8f9fa),
350
+ dark: var(--hub-ref-color-gray-900, #212529)
340
351
  ) !default;
341
352
 
342
353
  // Override aditivo — pasa SOLO las variantes que añades o retocas, conserva las demás.
@@ -356,12 +367,29 @@ $hub-accents: map.merge($hub-accents, $hub-accents-extra);
356
367
  // obtiene su familia completa gratis.
357
368
  @mixin hub-color-derive() {
358
369
  @each $name in map.keys($hub-accents) {
359
- --hub-sys-color-#{$name}-subtle: color-mix(in oklch, var(--hub-sys-color-#{$name}) 12%, var(--hub-sys-surface-page, #fff));
360
- --hub-sys-color-#{$name}-border-subtle: color-mix(in oklch, var(--hub-sys-color-#{$name}) 35%, var(--hub-sys-surface-page, #fff));
361
- --hub-sys-color-#{$name}-emphasis: color-mix(in oklch, var(--hub-sys-color-#{$name}) 80%, var(--hub-sys-color-ink, #212529));
370
+ --hub-sys-color-#{$name}-subtle: color-mix(
371
+ in oklch,
372
+ var(--hub-sys-color-#{$name}) 12%,
373
+ var(--hub-sys-surface-page, #fff)
374
+ );
375
+ --hub-sys-color-#{$name}-border-subtle: color-mix(
376
+ in oklch,
377
+ var(--hub-sys-color-#{$name}) 35%,
378
+ var(--hub-sys-surface-page, #fff)
379
+ );
380
+ // La luminosidad se lleva a la ventana que fija el tema — 0…0,45 en un tema claro,
381
+ // 0,72…1 en uno oscuro — para que el texto de énfasis siga leyéndose vaya la
382
+ // superficie hacia donde vaya. Una mezcla por porcentaje no puede: nunca oscurece
383
+ // un tono pálido.
384
+ --hub-sys-color-#{$name}-emphasis: oklch(
385
+ from var(--hub-sys-color-#{$name})
386
+ clamp(var(--hub-sys-emphasis-lightness-min, 0), l, var(--hub-sys-emphasis-lightness-max, 1))
387
+ c
388
+ h
389
+ );
362
390
  // Volteo de contraste en escala de grises según la luminosidad del propio acento:
363
391
  // los acentos oscuros (L < .62) resuelven a texto blanco; los claros, a casi negro.
364
- --hub-sys-color-#{$name}-on: oklch(from var(--hub-sys-color-#{$name}) clamp(0, (0.62 - l) * 1000, 1) 0 h);
392
+ --hub-sys-color-#{$name}-on: oklch(from var(--hub-sys-color-#{$name}) clamp(0, (0.62 - l) * 1000, 1) 0 h);
365
393
  }
366
394
  }
367
395
 
@@ -378,18 +406,41 @@ $hub-accents: map.merge($hub-accents, $hub-accents-extra);
378
406
  > `-emphasis` / `-on` automáticamente. Los mapas + mixins son solo la mecánica
379
407
  > interna, útil si contribuyes al paquete o compilas tu propia variante de la paleta.
380
408
 
409
+ ### Escribir un tema oscuro propio
410
+
411
+ Fija la ventana de énfasis junto con las superficies, o cada acento se irá hacia el lado
412
+ equivocado:
413
+
414
+ ```css
415
+ [data-theme='midnight'] {
416
+ --hub-sys-surface-page: #0b0d10;
417
+ --hub-sys-surface-elevated: #14181d;
418
+ --hub-sys-text-primary: #eef1f5;
419
+ --hub-sys-color-ink: #eef1f5;
420
+ /* Sin estas dos, `-emphasis` oscurece en vez de aclarar y un botón ghost sobre
421
+ esta superficie se queda alrededor de 2:1. */
422
+ --hub-sys-emphasis-lightness-min: 0.72;
423
+ --hub-sys-emphasis-lightness-max: 1;
424
+ }
425
+ ```
426
+
427
+ Los enlaces, el halo de foco y `--hub-sys-border-color-strong` salen de ahí, así que no hay nada
428
+ más que declarar. Comprueba el resultado con `npm run check:contrast`, que mide cada acento,
429
+ enlace, halo y borde de control de la hoja compilada contra las dos superficies de cada tema que
430
+ encuentra, y falla si alguno baja de su umbral WCAG.
431
+
381
432
  ---
382
433
 
383
434
  ## 📋 Tabla de referencia rápida
384
435
 
385
- | Quiero… | Cómo |
386
- | -------------------------- | ----------------------------------------------------------- |
387
- | Usar la paleta | `@import '…/hub-tokens.css'` una vez |
388
- | Cambiar un color global | `:root { --hub-sys-color-primary: … }` |
389
- | Cambiar el modo oscuro | `[data-theme='dark'] { --hub-sys-… : … }` |
390
- | Añadir un acento propio | define `--hub-sys-color-<x>` (+ familia opcional) |
391
- | Crear un tema | `[data-theme='<nombre>'] { … }` y actívalo |
392
- | Ver todos los tokens | la página de tokens en [hubui.dev](https://hubui.dev/en/design-system/) |
436
+ | Quiero… | Cómo |
437
+ | ----------------------- | ----------------------------------------------------------------------- |
438
+ | Usar la paleta | `@import '…/hub-tokens.css'` una vez |
439
+ | Cambiar un color global | `:root { --hub-sys-color-primary: … }` |
440
+ | Cambiar el modo oscuro | `[data-theme='dark'] { --hub-sys-… : … }` |
441
+ | Añadir un acento propio | define `--hub-sys-color-<x>` (+ familia opcional) |
442
+ | Crear un tema | `[data-theme='<nombre>'] { … }` y actívalo |
443
+ | Ver todos los tokens | la página de tokens en [hubui.dev](https://hubui.dev/en/design-system/) |
393
444
 
394
445
  ---
395
446
 
@@ -414,9 +465,17 @@ Repositorio: https://github.com/hub-env/ng-hub-ui-ds
414
465
  ¿Te gusta este paquete? Puedes apoyar el proyecto invitándome a un café ☕:
415
466
  [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://buymeacoffee.com/carlosmorcillo)
416
467
 
468
+ ## 💼 Soporte comercial
469
+
470
+ Mantengo estas librerías yo mismo: soy [Carlos Morcillo Fernández](https://www.carlosmorcillo.com), arquitecto frontend autónomo, y trabajo con equipos que construyen y mantienen aplicaciones Angular.
471
+
472
+ Si tu equipo depende de Hub-UI y necesita más de lo que se resuelve en un hilo de incidencias, eso es a lo que me dedico: auditorías de arquitectura, sistemas de diseño, migraciones de Angular y mentoría de equipos. Cuando el proyecto pide además diseño y un equipo completo, lo llevo por [Frog Hub](https://froghub.es), mi estudio de desarrollo.
473
+
474
+ Aquí están [los servicios](https://www.carlosmorcillo.com/servicios/) y aquí puedes [contarme tu proyecto](https://www.carlosmorcillo.com/contacto/).
475
+
417
476
  ## 📄 Licencia
418
477
 
419
478
  Este proyecto está licenciado bajo la Licencia MIT.
420
479
 
421
- MIT © [Carlos Morcillo](https://www.carlosmorcillo.com)
480
+ MIT © [Carlos Morcillo Fernández](https://www.carlosmorcillo.com)
422
481
  </content>
package/README.md CHANGED
@@ -146,11 +146,11 @@ own styles too:
146
146
 
147
147
  The tokens follow a layered system:
148
148
 
149
- | Layer | Prefix | What it is | Examples |
150
- | -------------- | ------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------- |
151
- | **Reference** | `--hub-ref-*` | Raw, context-free values | `--hub-ref-color-blue-500`, `--hub-ref-space-3`, `--hub-ref-radius-md` |
152
- | **System** | `--hub-sys-*` | Meaningful assignments that components consume | `--hub-sys-color-primary`, `--hub-sys-surface-page`, `--hub-sys-text-primary` |
153
- | **Container** | `--hub-container-*` | Inheritable bridge from `sys` to concrete containers/slots — a **re-base hook** | `--hub-container-bg`, `--hub-container-padding-x`, `--hub-container-gap` |
149
+ | Layer | Prefix | What it is | Examples |
150
+ | ---------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
151
+ | **Reference** | `--hub-ref-*` | Raw, context-free values | `--hub-ref-color-blue-500`, `--hub-ref-space-3`, `--hub-ref-radius-md` |
152
+ | **System** | `--hub-sys-*` | Meaningful assignments that components consume | `--hub-sys-color-primary`, `--hub-sys-surface-page`, `--hub-sys-text-primary` |
153
+ | **Container** | `--hub-container-*` | Inheritable bridge from `sys` to concrete containers/slots — a **re-base hook** | `--hub-container-bg`, `--hub-container-padding-x`, `--hub-container-gap` |
154
154
  | **Base / shell** | `--hub-body-*` · `--hub-main-*` · `--hub-main-wrapper-*` | Application-shell layout: the outer page wrapper, the content region and its centered wrapper. `--hub-body-*` **inherits the matching `--hub-container-*` defaults** | `--hub-body-padding-x`, `--hub-main-bg`, `--hub-main-wrapper-max-width` (`1200px`) |
155
155
 
156
156
  Golden rule: **components reference `sys` tokens only**. The `sys` tokens point
@@ -190,13 +190,13 @@ Each semantic variant exposes a **uniform family** of five tokens. The default s
190
190
  `dark`, and it is **open**: every variant you add gets the same family (see
191
191
  [How to customise it](#️-how-to-customise-it)).
192
192
 
193
- | Token | Typical use |
194
- | ----------------------------------- | ---------------------------------------------- |
195
- | `--hub-sys-color-<v>` | Solid colour (accent, icon, strong border) |
196
- | `--hub-sys-color-<v>-subtle` | Faint background (banners, alerts) |
197
- | `--hub-sys-color-<v>-border-subtle` | Faint border over the subtle background |
198
- | `--hub-sys-color-<v>-emphasis` | Legible text over the subtle background |
199
- | `--hub-sys-color-<v>-on` | Contrast text/icon colour laid **on** the accent |
193
+ | Token | Typical use |
194
+ | ----------------------------------- | ----------------------------------------------------------------- |
195
+ | `--hub-sys-color-<v>` | Solid colour (accent, icon, strong border) |
196
+ | `--hub-sys-color-<v>-subtle` | Faint background (banners, alerts) |
197
+ | `--hub-sys-color-<v>-border-subtle` | Faint border over the subtle background |
198
+ | `--hub-sys-color-<v>-emphasis` | Legible text over the surface — links and focus rings read it too |
199
+ | `--hub-sys-color-<v>-on` | Contrast text/icon colour laid **on** the accent |
200
200
 
201
201
  Usage example (a callout that matches the rest of the family):
202
202
 
@@ -217,7 +217,7 @@ container to theme a region):
217
217
 
218
218
  ```html
219
219
  <html data-theme="dark">
220
- <!-- light (default) · base · bootstrap · dark · sunset · forest · mono · terminal -->
220
+ <!-- light (default) · base · bootstrap · dark · sunset · forest · mono · terminal -->
221
221
  </html>
222
222
  ```
223
223
 
@@ -234,9 +234,9 @@ Besides the tokens, the package ships an opt-in styling layer — nothing is emi
234
234
  **Utility sheets** — class names mirror Bootstrap's **exactly** and every value resolves to the canonical tokens. Do not load them AND Bootstrap globally in the same document. Each sheet also has a compiled `.css` twin for Sass-less apps:
235
235
 
236
236
  ```scss
237
- @use 'ng-hub-ui-ds/styles/utilities/layout'; // display/flex (+ responsive), 12-col grid + offsets, spacing, sizing, position, overflow, order, .vr, .ratio-*, .visually-hidden…
238
- @use 'ng-hub-ui-ds/styles/utilities/text'; // .fs-1…6, .h1…6, .fw-*, .lh-*, .text-truncate, semantic text colours
239
- @use 'ng-hub-ui-ds/styles/utilities/surfaces'; // .bg-* (+ -subtle), .text-bg-*, .border*, .rounded*, .shadow*, .opacity-*
237
+ @use 'ng-hub-ui-ds/styles/utilities/layout'; // display/flex (+ responsive), 12-col grid + offsets, spacing, sizing, position, overflow, order, .vr, .ratio-*, .visually-hidden…
238
+ @use 'ng-hub-ui-ds/styles/utilities/text'; // .fs-1…6, .h1…6, .fw-*, .lh-*, .text-truncate, semantic text colours
239
+ @use 'ng-hub-ui-ds/styles/utilities/surfaces'; // .bg-* (+ -subtle), .text-bg-*, .border*, .rounded*, .shadow*, .opacity-*
240
240
  ```
241
241
 
242
242
  The only non-Bootstrap names are the layout primitives that mirror the mixins: `.stack`, `.cluster`, `.grid-auto`, `.center`.
@@ -252,10 +252,21 @@ The only non-Bootstrap names are the layout primitives that mirror the mixins: `
252
252
  ```scss
253
253
  @use 'ng-hub-ui-ds' as hub;
254
254
 
255
- .toolbar { @include hub.cluster($gap: 2); }
256
- .card__title { @include hub.font-size(4); @include hub.font-weight(semibold); }
257
- .card--brand { @include hub.text-bg(brand); @include hub.radius(lg); @include hub.shadow(sm); }
258
- .sr-label { @include hub.visually-hidden(); }
255
+ .toolbar {
256
+ @include hub.cluster($gap: 2);
257
+ }
258
+ .card__title {
259
+ @include hub.font-size(4);
260
+ @include hub.font-weight(semibold);
261
+ }
262
+ .card--brand {
263
+ @include hub.text-bg(brand);
264
+ @include hub.radius(lg);
265
+ @include hub.shadow(sm);
266
+ }
267
+ .sr-label {
268
+ @include hub.visually-hidden();
269
+ }
259
270
  ```
260
271
 
261
272
  Groups: **theme** (`theme()` — partial theming in one call: pass only the changed scales/accents as maps), **breakpoints** (`media-breakpoint-up/down` over the `$hub-breakpoints` map — the responsive variants `.d-md-*`, `.col-lg-*`, `.p-sm-*`… are generated from it), **layout** (`stack`, `cluster`, `grid`, `grid-fixed`, `row`, `col`, `offset`, `center`), **typography** (`font-family`, `font-size`, `font-weight`, `line-height`, `text-color`, `link-color`, `text-truncate`, `text-break`), **surfaces** (`bg`, `text-bg`, `border`, `border-color`, `radius`, `shadow`), **helpers** (`focus-ring`, `focus-ring-color`, `visually-hidden`, `stretched-link`, `ratio`, `clearfix`) and the **bridges** (`bridge-bootstrap` / `bridge-material` / `bridge-tailwind` / `bridge-open-props`).
@@ -333,12 +344,12 @@ $hub-accents: (
333
344
  primary: var(--hub-ref-color-blue-500, #0d6efd),
334
345
  secondary: var(--hub-ref-color-gray-600, #6c757d),
335
346
  success: var(--hub-ref-color-green-500, #198754),
336
- danger: var(--hub-ref-color-red-500, #dc3545),
347
+ danger: var(--hub-ref-color-red-500, #dc3545),
337
348
  warning: var(--hub-ref-color-yellow-500, #ffc107),
338
- info: var(--hub-ref-color-cyan-500, #0dcaf0),
349
+ info: var(--hub-ref-color-cyan-500, #0dcaf0),
339
350
  neutral: var(--hub-ref-color-gray-600, #6c757d),
340
- light: var(--hub-ref-color-gray-100, #f8f9fa),
341
- dark: var(--hub-ref-color-gray-900, #212529)
351
+ light: var(--hub-ref-color-gray-100, #f8f9fa),
352
+ dark: var(--hub-ref-color-gray-900, #212529)
342
353
  ) !default;
343
354
 
344
355
  // Additive override — pass ONLY the variants you add or retune, keep the defaults.
@@ -358,12 +369,28 @@ $hub-accents: map.merge($hub-accents, $hub-accents-extra);
358
369
  // its full family for free.
359
370
  @mixin hub-color-derive() {
360
371
  @each $name in map.keys($hub-accents) {
361
- --hub-sys-color-#{$name}-subtle: color-mix(in oklch, var(--hub-sys-color-#{$name}) 12%, var(--hub-sys-surface-page, #fff));
362
- --hub-sys-color-#{$name}-border-subtle: color-mix(in oklch, var(--hub-sys-color-#{$name}) 35%, var(--hub-sys-surface-page, #fff));
363
- --hub-sys-color-#{$name}-emphasis: color-mix(in oklch, var(--hub-sys-color-#{$name}) 80%, var(--hub-sys-color-ink, #212529));
372
+ --hub-sys-color-#{$name}-subtle: color-mix(
373
+ in oklch,
374
+ var(--hub-sys-color-#{$name}) 12%,
375
+ var(--hub-sys-surface-page, #fff)
376
+ );
377
+ --hub-sys-color-#{$name}-border-subtle: color-mix(
378
+ in oklch,
379
+ var(--hub-sys-color-#{$name}) 35%,
380
+ var(--hub-sys-surface-page, #fff)
381
+ );
382
+ // Lightness steered into the window the theme owns — 0…0.45 on a light theme,
383
+ // 0.72…1 on a dark one — so emphasis text stays readable whichever way the
384
+ // surface goes. A percentage mix cannot do this: it never darkens a pale hue.
385
+ --hub-sys-color-#{$name}-emphasis: oklch(
386
+ from var(--hub-sys-color-#{$name})
387
+ clamp(var(--hub-sys-emphasis-lightness-min, 0), l, var(--hub-sys-emphasis-lightness-max, 1))
388
+ c
389
+ h
390
+ );
364
391
  // Grayscale contrast flip driven by the accent's own lightness: dark accents
365
392
  // (L < .62) resolve to white text, light accents to near-black.
366
- --hub-sys-color-#{$name}-on: oklch(from var(--hub-sys-color-#{$name}) clamp(0, (0.62 - l) * 1000, 1) 0 h);
393
+ --hub-sys-color-#{$name}-on: oklch(from var(--hub-sys-color-#{$name}) clamp(0, (0.62 - l) * 1000, 1) 0 h);
367
394
  }
368
395
  }
369
396
 
@@ -380,18 +407,40 @@ $hub-accents: map.merge($hub-accents, $hub-accents-extra);
380
407
  > `-on` automatically. The maps + mixins are just the internal mechanism, useful if
381
408
  > you contribute to the package or compile your own palette variant.
382
409
 
410
+ ### Writing a dark theme of your own
411
+
412
+ Set the emphasis window along with the surfaces, or every accent gets pushed the wrong way:
413
+
414
+ ```css
415
+ [data-theme='midnight'] {
416
+ --hub-sys-surface-page: #0b0d10;
417
+ --hub-sys-surface-elevated: #14181d;
418
+ --hub-sys-text-primary: #eef1f5;
419
+ --hub-sys-color-ink: #eef1f5;
420
+ /* Without these two, `-emphasis` darkens instead of lightening and a ghost
421
+ button on this surface lands around 2:1. */
422
+ --hub-sys-emphasis-lightness-min: 0.72;
423
+ --hub-sys-emphasis-lightness-max: 1;
424
+ }
425
+ ```
426
+
427
+ Links, the focus ring and `--hub-sys-border-color-strong` all follow from those, so there is
428
+ nothing else to declare. Check the result with `npm run check:contrast`, which measures every
429
+ accent, link, ring and control border of the compiled sheet against both surfaces of every theme
430
+ it finds and fails if one drops below its WCAG threshold.
431
+
383
432
  ---
384
433
 
385
434
  ## 📋 Quick reference table
386
435
 
387
- | I want to… | How |
388
- | -------------------------- | ----------------------------------------------------------- |
389
- | Use the palette | `@import '…/hub-tokens.css'` once |
390
- | Change a global colour | `:root { --hub-sys-color-primary: … }` |
391
- | Change dark mode | `[data-theme='dark'] { --hub-sys-… : … }` |
392
- | Add your own accent | define `--hub-sys-color-<x>` (+ optional family) |
393
- | Create a theme | `[data-theme='<name>'] { … }` and activate it |
394
- | See all the tokens | the tokens page at [hubui.dev](https://hubui.dev/en/design-system/) |
436
+ | I want to… | How |
437
+ | ---------------------- | ------------------------------------------------------------------- |
438
+ | Use the palette | `@import '…/hub-tokens.css'` once |
439
+ | Change a global colour | `:root { --hub-sys-color-primary: … }` |
440
+ | Change dark mode | `[data-theme='dark'] { --hub-sys-… : … }` |
441
+ | Add your own accent | define `--hub-sys-color-<x>` (+ optional family) |
442
+ | Create a theme | `[data-theme='<name>'] { … }` and activate it |
443
+ | See all the tokens | the tokens page at [hubui.dev](https://hubui.dev/en/design-system/) |
395
444
 
396
445
  ---
397
446
 
@@ -416,10 +465,18 @@ Repository: https://github.com/hub-env/ng-hub-ui-ds
416
465
  Do you like this package? You can support the project by buying a coffee ☕:
417
466
  [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://buymeacoffee.com/carlosmorcillo)
418
467
 
468
+ ## 💼 Commercial support
469
+
470
+ These libraries are maintained by [Carlos Morcillo Fernández](https://www.carlosmorcillo.com), a freelance frontend architect working with teams that build and maintain Angular applications.
471
+
472
+ If your team depends on Hub-UI and needs more than an issue thread can solve, that is my day job: architecture audits, design systems, Angular migrations and team mentoring. For projects that also need design and a full team, I run them through [Frog Hub](https://froghub.es), my development studio.
473
+
474
+ Have a look at [the services](https://www.carlosmorcillo.com/en/services/) or [tell me about your project](https://www.carlosmorcillo.com/en/contact/).
475
+
419
476
  ## 📄 License
420
477
 
421
478
  This project is licensed under the MIT License.
422
479
 
423
- MIT © [Carlos Morcillo](https://www.carlosmorcillo.com)
480
+ MIT © [Carlos Morcillo Fernández](https://www.carlosmorcillo.com)
424
481
  </content>
425
482
  </invoke>