ng-hub-ui-ds 22.9.3 → 22.11.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,43 @@ 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.11.0 — 2026-09-08
9
+
10
+ ### Fixed
11
+
12
+ - **The `-emphasis` role could not carry text on a pale accent.** It was derived as
13
+ `color-mix(accent 80%, ink)`, and a percentage cannot darken a light hue: the shipped
14
+ amber came out at **2.5:1** on white and the shipped cyan at **2.9:1**. Every consumer
15
+ inherited unreadable text through `.text-warning-emphasis` and through any component
16
+ reading the role, and none of them could fix it, because the formula lives here.
17
+
18
+ The luminosity is capped instead of mixed — `oklch(from <accent> min(l, .45) c h)` —
19
+ which leaves an already-dark accent exactly where it was and pulls a light one down to
20
+ where it can carry a letter, keeping its hue and its chroma. Measured on white across the
21
+ nine defaults: nothing below 6.6:1 — info is the floor at 6.62, success next at 6.91 —
22
+ and no role moves by more than a point unless it was failing. `dark` is untouched, since
23
+ it already sat at L .26.
24
+
25
+ Themes that assign `--hub-sys-color-*-emphasis` by hand are unaffected.
26
+ ## [22.10.0] - 2026-09-08
27
+
28
+ ### Added
29
+
30
+ - **A theme can now flatten the boxes it draws.** `.border` carried its `1px` written into the
31
+ rule, so a theme could recolour a border but never remove or thicken one — which meant a
32
+ flat-surface theme was impossible to express through tokens no matter how the consumer wrote its
33
+ markup. The width now travels on `--hub-border-width`, and the single-side rules on
34
+ `--hub-border-side-width`. They are two chains on purpose: `.border` outlines a surface and a flat
35
+ theme wants that gone, while `.border-bottom` separates one row from the next and that is
36
+ structure. One variable for both would force a theme to choose between keeping its cards and
37
+ losing its separators. One consequence is worth reading before upgrading: a theme that had
38
+ already re-based `--hub-ref-border-width` now sees the border utilities follow it, where they
39
+ used to ignore it. See `BREAKING_CHANGES.md`.
40
+ - **`theme()` takes surfaces and borders by name.** `$surfaces` sets `--hub-sys-surface-page` and
41
+ `-elevated`; `$borders` sets the border colour and the two width chains above. Both were reachable
42
+ through `$tokens` already, but a visual theme is mostly surfaces and borders, and routing the main
43
+ case through the escape hatch made the API read as if it only did colour and spacing.
44
+
8
45
  ## [22.9.3] - 2026-09-08
9
46
 
10
47
  ### Changed
@@ -517,6 +517,8 @@ Cascade knobs read by the opt-in utility sheets and their mixin equivalents. The
517
517
  | `--hub-link-opacity` | `1` | Opacity multiplier for the `.link-*` text colour (set by `.link-opacity-{10,25,50,75,100}`) | `IN_USE` | `ds/styles/utilities/text.scss:199` |
518
518
  | `--hub-border-opacity` | `1` | Opacity multiplier applied by `.border` / `.border-*` colour utilities (set by `.border-opacity-{10,25,50,75,100}`) | `IN_USE` | `ds/styles/utilities/surfaces.scss:117` |
519
519
  | `--hub-focus-ring-opacity` | `0.25` | Alpha the `.focus-ring-*` utilities mix the accent with when re-tinting `--hub-sys-focus-ring-color` | `IN_USE` | `ds/styles/mixins/_helpers.scss:70` |
520
+ | `--hub-border-width` | `var(--hub-ref-border-width, 1px)` | Thickness of the box `.border` draws around a surface. A flat theme sets it to `0` and every boxed surface loses its outline at once, without a consumer rewriting a single rule | `IN_USE` | `ds/styles/mixins/_surfaces.scss:58` |
521
+ | `--hub-border-side-width` | `var(--hub-ref-border-width, 1px)` | Thickness of the single-side rules (`.border-top`, `-bottom`, `-start`, `-end`). Deliberately separate from `--hub-border-width`: those rules separate one row from the next, and that is structure a flat theme should not erase along with its cards | `IN_USE` | `ds/styles/utilities/surfaces.scss:93` |
520
522
 
521
523
  ## `container` Variables (Inheritable base)
522
524
 
@@ -1304,7 +1306,7 @@ The `detail` token set is defined as a single, standardized collection that supp
1304
1306
  | `--hub-badge-transition` | `var(--hub-sys-transition-base, all 0.2s ease-in-out)` | Badge transition | `IN_USE` | `badges/src/lib/components/badge/badge.component.scss:53` |
1305
1307
  | `--hub-badge-active-bg` | `var(--hub-badge-accent-emphasis)` | Active (pressed) background of an interactive badge (.hub-badge--active) | `IN_USE` | `badges/src/lib/components/badge/badge.component.scss:55` |
1306
1308
  | `--hub-badge-overlay-offset` | `-0.25rem` | Corner inset of the overlay status dot (dotOverlay) | `IN_USE` | `badges/src/lib/components/badge/badge.component.scss:57` |
1307
- | `--hub-chip-set-gap` | `var(--hub-ref-space-2, 0.5rem)` | Gap between chips inside a hub-chip-set | `IN_USE` | `badges/src/lib/components/chip-set/chip-set.component.scss:2` |
1309
+ | `--hub-chip-set-gap` | `var(--hub-ref-space-2, 0.5rem)` | Gap between chips inside a hub-chip-set | `IN_USE` | `badges/src/lib/components/chip-set/chip-set.component.scss:5` |
1308
1310
  | `--hub-chip-transition` | `var(--hub-sys-transition-base, all 0.2s ease-in-out)` | Chip transition | `IN_USE` | `badges/src/lib/components/chip/chip.component.scss:39` |
1309
1311
  | `--hub-chip-focus-ring-width` | `var(--hub-sys-focus-ring-width, 0.25rem)` | Chip focus ring width | `IN_USE` | `badges/src/lib/components/chip/chip.component.scss:38` |
1310
1312
  | `--hub-chip-border-radius` | `var(--hub-ref-radius-pill, 50rem)` | Chip shell radius | `IN_USE` | `badges/src/lib/components/chip/chip.component.scss:37` |
@@ -1339,12 +1341,12 @@ The `detail` token set is defined as a single, standardized collection that supp
1339
1341
  | `--hub-button-spinner-duration` | `0.7s` | Button loading spinner spin duration | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:14` |
1340
1342
  | `--hub-button-spinner` | `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' d='M12 3a9 9 0 1 0 9 9'/%3E%3C/svg%3E")` | Button loading spinner glyph (swappable SVG) | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:19` |
1341
1343
  | `--hub-button-transition` | `var(--hub-sys-transition-fast, all 0.15s ease-in-out)` | Button transition | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:12` |
1342
- | `--hub-btn-active-color` | `var(--hub-btn-accent-on)` | Pressed (:active) button text-color slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:52` |
1343
- | `--hub-btn-active-border` | `transparent` | Pressed (:active) button border-color slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:51` |
1344
- | `--hub-btn-active-bg` | `color-mix(in oklch, var(--hub-btn-accent) 70%, var(--hub-sys-color-ink, #212529))` | Pressed (:active) button background slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:50` |
1345
- | `--hub-btn-hover-color` | `var(--hub-btn-accent-emphasis)` | Overridable button hover text-color slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:49` |
1346
- | `--hub-btn-hover-border` | `transparent` | Overridable button hover border-color slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:48` |
1347
- | `--hub-btn-hover-bg` | `var(--hub-btn-accent-subtle)` | Overridable button hover background slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:47` |
1344
+ | `--hub-btn-active-color` | `var(--hub-btn-accent-on)` | Pressed (:active) button text-color slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:63` |
1345
+ | `--hub-btn-active-border` | `transparent` | Pressed (:active) button border-color slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:62` |
1346
+ | `--hub-btn-active-bg` | `color-mix(in oklch, var(--hub-btn-accent) 70%, var(--hub-sys-color-ink, #212529))` | Pressed (:active) button background slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:61` |
1347
+ | `--hub-btn-hover-color` | `var(--hub-btn-accent-emphasis)` | Overridable button hover text-color slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:60` |
1348
+ | `--hub-btn-hover-border` | `transparent` | Overridable button hover border-color slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:59` |
1349
+ | `--hub-btn-hover-bg` | `var(--hub-btn-accent-subtle)` | Overridable button hover background slot | `IN_USE` | `buttons/src/lib/components/btn/button.component.scss:58` |
1348
1350
  | `--hub-dropdown-item-border-radius` | `var(--hub-sys-radius-sm, 0.25rem)` | Dropdown item border radius | `IN_USE` | `buttons/src/lib/components/dropdown-item/dropdown-item.component.scss:8` |
1349
1351
  | `--hub-dropdown-item-disabled-opacity` | `0.45` | Dropdown item disabled opacity | `IN_USE` | `buttons/src/lib/components/dropdown-item/dropdown-item.component.scss:9` |
1350
1352
  | `--hub-dropdown-item-hover-bg` | `var(--hub-sys-color-surface-subtle, #f8f9fa)` | Dropdown item hover bg | `IN_USE` | `buttons/src/lib/components/dropdown-item/dropdown-item.component.scss:7` |
@@ -2188,7 +2190,7 @@ Neutral loading-placeholder tokens (no semantic colour variant). The base / high
2188
2190
  | `--hub-stepper-content-padding-y` | `0` | Content vertical padding | `IN_USE` | `stepper/src/lib/stepper/stepper.component.scss:60` |
2189
2191
  | `--hub-stepper-nav-padding-x` | `0` | Nav horizontal padding | `IN_USE` | `stepper/src/lib/stepper/stepper.component.scss:21` |
2190
2192
  | `--hub-stepper-nav-padding-y` | `0` | Nav vertical padding | `IN_USE` | `stepper/src/lib/stepper/stepper.component.scss:22` |
2191
- | `--hub-stepper-nav-title-max-width` | `12rem` | Max width of a step nav title before ellipsis | `IN_USE` | `stepper/src/lib/stepper/stepper.component.scss:387` |
2193
+ | `--hub-stepper-nav-title-max-width` | `12rem` | Max width of a step nav title before ellipsis | `IN_USE` | `stepper/src/lib/stepper/stepper.component.scss:403` |
2192
2194
 
2193
2195
  ### `layout`
2194
2196
 
@@ -2276,6 +2278,7 @@ Neutral loading-placeholder tokens (no semantic colour variant). The base / high
2276
2278
  | `--hub-nav-accent-emphasis` | `color-mix(in oklch, var(--hub-nav-accent) 80%, var(--hub-sys-color-ink, #212529))` | Accent emphasis role — the accent mixed over the theme ink, so accent text reads with enough contrast | `IN_USE` | `nav/src/styles/nav-tokens.scss:43` |
2277
2279
  | `--hub-nav-accent-on` | `oklch(from var(--hub-nav-accent) clamp(0, (0.62 - l) * 1000, 1) 0 h)` | Accent on-color — text sitting on the accent, a grayscale flip driven by the accent's own lightness | `IN_USE` | `nav/src/styles/nav-tokens.scss:47` |
2278
2280
  | `--hub-nav-bg` | `color-mix(in oklch, var(--hub-nav-accent) 5%, var(--hub-sys-surface-page, #fff))` | Navigation surface background (faint accent tint, re-based per variant) | `IN_USE` | `nav/src/styles/nav-tokens.scss:50` |
2281
+ | `--hub-nav-bg-image` | `none` | Optional `<image>` layered over `--hub-nav-bg`. A gradient substituted into `background-color` computes to an invalid value and drops the declaration, so it travels on its own property with the colour underneath as the fallback | `CONSUMED` | `nav/src/components/nav/nav.component.scss:15` |
2279
2282
  | `--hub-nav-border-color` | `var(--hub-sys-border-color-default, #dee2e6)` | Navigation border color | `IN_USE` | `nav/src/styles/nav-tokens.scss:52` |
2280
2283
  | `--hub-nav-border-style` | `solid` | Navigation border style | `IN_USE` | `nav/src/styles/nav-tokens.scss:54` |
2281
2284
  | `--hub-nav-border-width` | `1px` | Navigation border width | `IN_USE` | `nav/src/styles/nav-tokens.scss:53` |
@@ -2307,7 +2310,7 @@ Neutral loading-placeholder tokens (no semantic colour variant). The base / high
2307
2310
  | `--hub-nav-header-padding-y` | `var(--hub-ref-space-1, 0.25rem)` | Section header vertical padding | `IN_USE` | `nav/src/styles/nav-tokens.scss:114` |
2308
2311
  | `--hub-nav-height` | `3.5rem` | Navigation bar height | `IN_USE` | `nav/src/styles/nav-tokens.scss:9` |
2309
2312
  | `--hub-nav-horizontal-items-justify` | `center` | Horizontal items justification | `IN_USE` | `nav/src/styles/nav-tokens.scss:16` |
2310
- | `--hub-nav-collapsed-justify` | `space-between` | `justify-content` of items when the nav is collapsed (opt-in hook) | `IN_USE` | `nav/src/components/nav/nav.component.scss:69` |
2313
+ | `--hub-nav-collapsed-justify` | `space-between` | `justify-content` of items when the nav is collapsed (opt-in hook) | `IN_USE` | `nav/src/components/nav/nav.component.scss:75` |
2311
2314
  | `--hub-nav-horizontal-items-overflow-x` | `auto` | Horizontal items x-overflow | `IN_USE` | `nav/src/styles/nav-tokens.scss:17` |
2312
2315
  | `--hub-nav-horizontal-items-overflow-y` | `hidden` | Horizontal items y-overflow | `IN_USE` | `nav/src/styles/nav-tokens.scss:18` |
2313
2316
  | `--hub-nav-horizontal-padding-x` | `0` | Horizontal nav horizontal padding | `IN_USE` | `nav/src/styles/nav-tokens.scss:14` |
@@ -2562,11 +2565,11 @@ The navigation strip is **semantic-accent aware**: a single `--hub-panels-accent
2562
2565
  | `--hub-milestone-pending-color` | `var(--hub-sys-text-muted, #6c757d)` | Pending node text color | `IN_USE` | `milestones/src/lib/milestones.component.scss:21` |
2563
2566
  | `--hub-milestone-spacing` | `1.75rem` | Spacing between node and content | `IN_USE` | `milestones/src/lib/milestones.component.scss:27` |
2564
2567
  | `--hub-milestone-pulse-color` | `var(--hub-milestone-node-color)` | Color of the active-node pulse ring | `IN_USE` | `milestones/src/lib/milestones.component.scss:80` |
2565
- | `--hub-milestone-pulse-duration` | `1.6s` | Duration of the pulse animation | `IN_USE` | `milestones/src/lib/milestones.component.scss:204` |
2566
- | `--hub-milestone-pulse-spread` | `0.75rem` | Spread radius of the pulse ring | `IN_USE` | `milestones/src/lib/milestones.component.scss:187` |
2567
- | `--hub-milestone-reveal-duration` | `0.5s` | Duration of the connector reveal/fill animation | `IN_USE` | `milestones/src/lib/milestones.component.scss:245` |
2568
- | `--hub-milestone-reveal-stagger` | `0.14s` | Per-index delay of the staggered reveal | `IN_USE` | `milestones/src/lib/milestones.component.scss:246` |
2569
- | `--hub-milestone-index` | runtime (0-based) | Node position used to stagger the reveal delay; set per node by the parent | `INTERNAL` | `milestones/src/lib/milestone.component.ts:29` |
2568
+ | `--hub-milestone-pulse-duration` | `1.6s` | Duration of the pulse animation | `IN_USE` | `milestones/src/lib/milestones.component.scss:248` |
2569
+ | `--hub-milestone-pulse-spread` | `0.75rem` | Spread radius of the pulse ring | `IN_USE` | `milestones/src/lib/milestones.component.scss:231` |
2570
+ | `--hub-milestone-reveal-duration` | `0.5s` | Duration of the connector reveal/fill animation | `IN_USE` | `milestones/src/lib/milestones.component.scss:289` |
2571
+ | `--hub-milestone-reveal-stagger` | `0.14s` | Per-index delay of the staggered reveal | `IN_USE` | `milestones/src/lib/milestones.component.scss:290` |
2572
+ | `--hub-milestone-index` | runtime (0-based) | Node position used to stagger the reveal delay; set per node by the parent | `INTERNAL` | `milestones/src/lib/milestone.component.ts:54` |
2570
2573
 
2571
2574
  ### `icons`
2572
2575
 
@@ -2587,26 +2590,26 @@ Agnostic icon renderer (`ng-hub-ui-icons`). One token set themes any icon pack (
2587
2590
 
2588
2591
  | Token | Initial value | Usage | Status | Source |
2589
2592
  | ----- | ------------- | ----- | ------ | ------ |
2590
- | `--hub-progress-track-bg` | `color-mix(in oklch, var(--hub-progress-accent) 16%, transparent)` | Progress track background (subtle accent tint) | `IN_USE` | `metrics/src/lib/components/progress/progress.component.scss:11` |
2591
- | `--hub-progress-indicator-bg` | `var(--hub-progress-accent)` | Filled progress indicator colour | `IN_USE` | `metrics/src/lib/components/progress/progress.component.scss:12` |
2592
- | `--hub-progress-height` | `var(--hub-ref-space-2, 0.5rem)` | Progress bar thickness (sm/md/lg override it) | `IN_USE` | `metrics/src/lib/components/progress/progress.component.scss:13` |
2593
- | `--hub-progress-radius` | `var(--hub-ref-radius-pill, 50rem)` | Progress bar corner radius | `IN_USE` | `metrics/src/lib/components/progress/progress.component.scss:14` |
2594
- | `--hub-progress-value` | `0%` | Internal runtime width of the indicator (set from the value input) | `INTERNAL` | `metrics/src/lib/components/progress/progress.component.scss:41` |
2595
- | `--hub-meter-track-bg` | `color-mix(in oklch, var(--hub-sys-color-neutral, #6c757d) 16%, transparent)` | Meter track background | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:11` |
2596
- | `--hub-meter-low-bg` | `var(--hub-sys-color-danger, #dc3545)` | Meter fill when the value is below target | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:12` |
2597
- | `--hub-meter-optimum-bg` | `var(--hub-sys-color-success, #198754)` | Meter fill when the value is on target | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:13` |
2598
- | `--hub-meter-high-bg` | `var(--hub-sys-color-warning, #ffc107)` | Meter fill when the value is above target | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:14` |
2599
- | `--hub-meter-height` | `var(--hub-ref-space-2, 0.5rem)` | Meter bar thickness | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:15` |
2600
- | `--hub-meter-radius` | `var(--hub-ref-radius-pill, 50rem)` | Meter bar corner radius | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:16` |
2601
- | `--hub-meter-fill` | `var(--hub-meter-optimum-bg)` | Internal band-selected meter fill colour | `INTERNAL` | `metrics/src/lib/components/meter/meter.component.scss:18` |
2602
- | `--hub-meter-value` | `0%` | Internal runtime width of the meter fill (set from the value input) | `INTERNAL` | `metrics/src/lib/components/meter/meter.component.scss:37` |
2603
- | `--hub-ring-size` | `4rem` | Ring outer diameter (overridden per-instance from size) | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:12` |
2604
- | `--hub-ring-thickness` | `var(--hub-ref-space-2, 0.5rem)` | Ring stroke width (overridden per-instance from thickness) | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:13` |
2605
- | `--hub-ring-track` | `color-mix(in oklch, var(--hub-sys-color-neutral, #6c757d) 20%, transparent)` | Ring track stroke colour | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:14` |
2606
- | `--hub-ring-indicator` | `var(--hub-sys-color-primary, #0d6efd)` | Ring indicator stroke colour (neutral band) | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:15` |
2607
- | `--hub-ring-caption-color` | `var(--hub-sys-text-primary, #212529)` | Ring centre caption colour | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:16` |
2608
- | `--hub-ring-low-color` | `var(--hub-sys-color-danger, #dc3545)` | Ring stroke below the low threshold | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:17` |
2609
- | `--hub-ring-high-color` | `var(--hub-sys-color-success, #198754)` | Ring stroke at/above the high threshold | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:18` |
2593
+ | `--hub-progress-track-bg` | `color-mix(in oklch, var(--hub-progress-accent) 16%, transparent)` | Progress track background (subtle accent tint) | `IN_USE` | `metrics/src/lib/components/progress/progress.component.scss:15` |
2594
+ | `--hub-progress-indicator-bg` | `var(--hub-progress-accent)` | Filled progress indicator colour | `IN_USE` | `metrics/src/lib/components/progress/progress.component.scss:16` |
2595
+ | `--hub-progress-height` | `var(--hub-ref-space-2, 0.5rem)` | Progress bar thickness (sm/md/lg override it) | `IN_USE` | `metrics/src/lib/components/progress/progress.component.scss:17` |
2596
+ | `--hub-progress-radius` | `var(--hub-ref-radius-pill, 50rem)` | Progress bar corner radius | `IN_USE` | `metrics/src/lib/components/progress/progress.component.scss:18` |
2597
+ | `--hub-progress-value` | `0%` | Internal runtime width of the indicator (set from the value input) | `INTERNAL` | `metrics/src/lib/components/progress/progress.component.scss:45` |
2598
+ | `--hub-meter-track-bg` | `color-mix(in oklch, var(--hub-sys-color-neutral, #6c757d) 16%, transparent)` | Meter track background | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:15` |
2599
+ | `--hub-meter-low-bg` | `var(--hub-sys-color-danger, #dc3545)` | Meter fill when the value is below target | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:16` |
2600
+ | `--hub-meter-optimum-bg` | `var(--hub-sys-color-success, #198754)` | Meter fill when the value is on target | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:17` |
2601
+ | `--hub-meter-high-bg` | `var(--hub-sys-color-warning, #ffc107)` | Meter fill when the value is above target | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:18` |
2602
+ | `--hub-meter-height` | `var(--hub-ref-space-2, 0.5rem)` | Meter bar thickness | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:19` |
2603
+ | `--hub-meter-radius` | `var(--hub-ref-radius-pill, 50rem)` | Meter bar corner radius | `IN_USE` | `metrics/src/lib/components/meter/meter.component.scss:20` |
2604
+ | `--hub-meter-fill` | `var(--hub-meter-optimum-bg)` | Internal band-selected meter fill colour | `INTERNAL` | `metrics/src/lib/components/meter/meter.component.scss:22` |
2605
+ | `--hub-meter-value` | `0%` | Internal runtime width of the meter fill (set from the value input) | `INTERNAL` | `metrics/src/lib/components/meter/meter.component.scss:41` |
2606
+ | `--hub-ring-size` | `4rem` | Ring outer diameter (overridden per-instance from size) | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:16` |
2607
+ | `--hub-ring-thickness` | `var(--hub-ref-space-2, 0.5rem)` | Ring stroke width (overridden per-instance from thickness) | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:17` |
2608
+ | `--hub-ring-track` | `color-mix(in oklch, var(--hub-sys-color-neutral, #6c757d) 20%, transparent)` | Ring track stroke colour | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:18` |
2609
+ | `--hub-ring-indicator` | `var(--hub-sys-color-primary, #0d6efd)` | Ring indicator stroke colour (neutral band) | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:19` |
2610
+ | `--hub-ring-caption-color` | `var(--hub-sys-text-primary, #212529)` | Ring centre caption colour | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:20` |
2611
+ | `--hub-ring-low-color` | `var(--hub-sys-color-danger, #dc3545)` | Ring stroke below the low threshold | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:21` |
2612
+ | `--hub-ring-high-color` | `var(--hub-sys-color-success, #198754)` | Ring stroke at/above the high threshold | `IN_USE` | `metrics/src/lib/components/ring/ring.component.scss:22` |
2610
2613
 
2611
2614
  ### `signature`
2612
2615
 
@@ -2614,17 +2617,17 @@ The signature field reuses the `.hub-field__*` shell from `ng-hub-ui-forms`, so
2614
2617
 
2615
2618
  | Token | Initial value | Usage | Status | Source |
2616
2619
  | ----- | ------------- | ----- | ------ | ------ |
2617
- | `--hub-signature-bg` | `var(--hub-input-bg, var(--hub-sys-surface, #fff))` | Drawing-surface background | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:2` |
2618
- | `--hub-signature-color` | `var(--hub-input-color, var(--hub-sys-text-primary, #212529))` | Field text colour | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:3` |
2619
- | `--hub-signature-border-color` | `var(--hub-input-border-color, var(--hub-sys-border, #ced4da))` | Drawing-surface border colour | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:4` |
2620
- | `--hub-signature-border-width` | `var(--hub-input-border-width, 1px)` | Drawing-surface border thickness | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:5` |
2621
- | `--hub-signature-border-radius` | `var(--hub-input-border-radius, var(--hub-ref-radius-md, 0.375rem))` | Drawing-surface corner radius | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:6` |
2622
- | `--hub-signature-focus-border-color` | `var(--hub-input-focus-border-color, var(--hub-sys-color-primary, #0d6efd))` | Border colour while the surface holds keyboard focus | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:7` |
2623
- | `--hub-signature-focus-shadow` | `var(--hub-input-focus-box-shadow, 0 0 0 0.25rem rgb(13 110 253 / 25%))` | Focus ring around the drawing surface | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:8` |
2624
- | `--hub-signature-label-color` | `var(--hub-label-color, var(--hub-sys-text-primary, #212529))` | Field label colour | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:9` |
2625
- | `--hub-signature-label-font-size` | `var(--hub-label-font-size, var(--hub-ref-font-size-sm, 0.875rem))` | Field label font size | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:10` |
2626
- | `--hub-signature-font-size` | `var(--hub-input-font-size, 1rem)` | Field font size | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:11` |
2627
- | `--hub-signature-actions-gap` | `var(--hub-ref-space-2, 0.5rem)` | Gap between the clear / undo / redo actions | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:12` |
2620
+ | `--hub-signature-bg` | `var(--hub-input-bg, var(--hub-sys-surface, #fff))` | Drawing-surface background | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:7` |
2621
+ | `--hub-signature-color` | `var(--hub-input-color, var(--hub-sys-text-primary, #212529))` | Field text colour | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:8` |
2622
+ | `--hub-signature-border-color` | `var(--hub-input-border-color, var(--hub-sys-border, #ced4da))` | Drawing-surface border colour | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:9` |
2623
+ | `--hub-signature-border-width` | `var(--hub-input-border-width, 1px)` | Drawing-surface border thickness | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:10` |
2624
+ | `--hub-signature-border-radius` | `var(--hub-input-border-radius, var(--hub-ref-radius-md, 0.375rem))` | Drawing-surface corner radius | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:11` |
2625
+ | `--hub-signature-focus-border-color` | `var(--hub-input-focus-border-color, var(--hub-sys-color-primary, #0d6efd))` | Border colour while the surface holds keyboard focus | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:12` |
2626
+ | `--hub-signature-focus-shadow` | `var(--hub-input-focus-box-shadow, 0 0 0 0.25rem rgb(13 110 253 / 25%))` | Focus ring around the drawing surface | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:13` |
2627
+ | `--hub-signature-label-color` | `var(--hub-label-color, var(--hub-sys-text-primary, #212529))` | Field label colour | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:14` |
2628
+ | `--hub-signature-label-font-size` | `var(--hub-label-font-size, var(--hub-ref-font-size-sm, 0.875rem))` | Field label font size | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:15` |
2629
+ | `--hub-signature-font-size` | `var(--hub-input-font-size, 1rem)` | Field font size | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:16` |
2630
+ | `--hub-signature-actions-gap` | `var(--hub-ref-space-2, 0.5rem)` | Gap between the clear / undo / redo actions | `IN_USE` | `signature/src/lib/components/signature/signature.component.scss:17` |
2628
2631
 
2629
2632
  ### `loading`
2630
2633
 
@@ -2632,17 +2635,17 @@ One accent drives all five indicator variants, so recolouring the spinner recolo
2632
2635
 
2633
2636
  | Token | Initial value | Usage | Status | Source |
2634
2637
  | ----- | ------------- | ----- | ------ | ------ |
2635
- | `--hub-loading-accent` | `var(--hub-sys-color-primary, #0d6efd)` | Indicator colour, shared by all five variants | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:7` |
2636
- | `--hub-loading-size` | `2.5rem` | Indicator diameter; the `sm` / `lg` modifiers re-declare it | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:8` |
2637
- | `--hub-loading-thickness` | `calc(var(--hub-ref-border-width, 1px) * 3)` | Stroke weight of the `spinner` and `ring` variants | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:9` |
2638
- | `--hub-loading-speed` | `0.9s` | Period of one animation loop, for every variant | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:10` |
2639
- | `--hub-loading-gap` | `var(--hub-sys-gap-2, var(--hub-ref-space-2, 0.5rem))` | Space between indicator, message and projected content | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:11` |
2640
- | `--hub-loading-text-color` | `var(--hub-sys-text-primary, var(--hub-ref-color-gray-900, #212529))` | Message colour | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:12` |
2641
- | `--hub-loading-font-size` | `var(--hub-ref-font-size-sm, 0.875rem)` | Message font size; the `sm` / `lg` modifiers re-declare it | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:13` |
2642
- | `--hub-loading-backdrop-bg` | `color-mix(in srgb, var(--hub-sys-surface-page, #ffffff) 72%, transparent)` | Scrim painted behind the `overlay` and `fullscreen` modes | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:16` |
2643
- | `--hub-loading-backdrop-blur` | `2px` | Blur radius applied to whatever the scrim covers | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:17` |
2644
- | `--hub-loading-z-index` | `var(--hub-sys-zindex-modal, 1055)` | Stacking order of the `fullscreen` overlay | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:18` |
2645
- | `--hub-loading-image-size` | `var(--hub-loading-size)` | Size of the branding image supplied through `image` | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:19` |
2638
+ | `--hub-loading-accent` | `var(--hub-sys-color-primary, #0d6efd)` | Indicator colour, shared by all five variants | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:11` |
2639
+ | `--hub-loading-size` | `2.5rem` | Indicator diameter; the `sm` / `lg` modifiers re-declare it | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:12` |
2640
+ | `--hub-loading-thickness` | `calc(var(--hub-ref-border-width, 1px) * 3)` | Stroke weight of the `spinner` and `ring` variants | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:13` |
2641
+ | `--hub-loading-speed` | `0.9s` | Period of one animation loop, for every variant | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:14` |
2642
+ | `--hub-loading-gap` | `var(--hub-sys-gap-2, var(--hub-ref-space-2, 0.5rem))` | Space between indicator, message and projected content | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:15` |
2643
+ | `--hub-loading-text-color` | `var(--hub-sys-text-primary, var(--hub-ref-color-gray-900, #212529))` | Message colour | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:16` |
2644
+ | `--hub-loading-font-size` | `var(--hub-ref-font-size-sm, 0.875rem)` | Message font size; the `sm` / `lg` modifiers re-declare it | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:17` |
2645
+ | `--hub-loading-backdrop-bg` | `color-mix(in srgb, var(--hub-sys-surface-page, #ffffff) 72%, transparent)` | Scrim painted behind the `overlay` and `fullscreen` modes | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:20` |
2646
+ | `--hub-loading-backdrop-blur` | `2px` | Blur radius applied to whatever the scrim covers | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:21` |
2647
+ | `--hub-loading-z-index` | `var(--hub-sys-zindex-modal, 1055)` | Stacking order of the `fullscreen` overlay | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:22` |
2648
+ | `--hub-loading-image-size` | `var(--hub-loading-size)` | Size of the branding image supplied through `image` | `IN_USE` | `loading/src/lib/components/loading/loading.component.scss:23` |
2646
2649
 
2647
2650
  ### `loading-bar`
2648
2651
 
@@ -2650,21 +2653,21 @@ The page-progress strip shares the library's philosophy but not its tokens: a ba
2650
2653
 
2651
2654
  | Token | Initial value | Usage | Status | Source |
2652
2655
  | ----- | ------------- | ----- | ------ | ------ |
2653
- | `--hub-loading-bar-accent` | `var(--hub-sys-color-primary, #0d6efd)` | Fill colour of the bar | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:9` |
2654
- | `--hub-loading-bar-height` | `3px` | Thickness of the strip | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:10` |
2655
- | `--hub-loading-bar-track-bg` | `transparent` | Unfilled track; transparent so an idle bar draws no permanent line under the navbar | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:13` |
2656
- | `--hub-loading-bar-radius` | `0` | Corner radius of the strip and its fill | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:14` |
2657
- | `--hub-loading-bar-speed` | `200ms` | How long the fill takes to catch up with a new value | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:15` |
2658
- | `--hub-loading-bar-fade` | `300ms` | Fade in and out of the whole strip | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:16` |
2659
- | `--hub-loading-bar-easing` | `linear` | Easing of the fill; linear reads as steady progress rather than as a flourish | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:17` |
2660
- | `--hub-loading-bar-glow-color` | `var(--hub-loading-bar-accent)` | Colour of the glow trailing the leading edge | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:18` |
2661
- | `--hub-loading-bar-glow-blur` | `10px` | Blur radius of that glow | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:19` |
2662
- | `--hub-loading-bar-glow-spread` | `1px` | Spread radius of that glow | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:20` |
2663
- | `--hub-loading-bar-indeterminate-speed` | `1.6s` | Period of one `indeterminate` sweep; calmed under `prefers-reduced-motion` | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:21` |
2664
- | `--hub-loading-bar-offset` | `0px` | Distance from the edge the `overlay` and `fixed` modes attach to | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:24` |
2665
- | `--hub-loading-bar-z-index` | `var(--hub-sys-zindex-sticky, 1020)` | Stacking order of the positioned modes, at chrome level | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:27` |
2666
- | `--hub-loading-bar-progress` | `0%` | Internal runtime fill, written by the component from its resolved value | `INTERNAL` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:29` |
2667
- | `--hub-loading-bar-sweep-direction` | `1` | Internal runtime sign flipping the `indeterminate` sweep under `[dir='rtl']` | `INTERNAL` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:31` |
2656
+ | `--hub-loading-bar-accent` | `var(--hub-sys-color-primary, #0d6efd)` | Fill colour of the bar | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:13` |
2657
+ | `--hub-loading-bar-height` | `3px` | Thickness of the strip | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:14` |
2658
+ | `--hub-loading-bar-track-bg` | `transparent` | Unfilled track; transparent so an idle bar draws no permanent line under the navbar | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:17` |
2659
+ | `--hub-loading-bar-radius` | `0` | Corner radius of the strip and its fill | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:18` |
2660
+ | `--hub-loading-bar-speed` | `200ms` | How long the fill takes to catch up with a new value | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:19` |
2661
+ | `--hub-loading-bar-fade` | `300ms` | Fade in and out of the whole strip | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:20` |
2662
+ | `--hub-loading-bar-easing` | `linear` | Easing of the fill; linear reads as steady progress rather than as a flourish | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:21` |
2663
+ | `--hub-loading-bar-glow-color` | `var(--hub-loading-bar-accent)` | Colour of the glow trailing the leading edge | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:22` |
2664
+ | `--hub-loading-bar-glow-blur` | `10px` | Blur radius of that glow | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:23` |
2665
+ | `--hub-loading-bar-glow-spread` | `1px` | Spread radius of that glow | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:24` |
2666
+ | `--hub-loading-bar-indeterminate-speed` | `1.6s` | Period of one `indeterminate` sweep; calmed under `prefers-reduced-motion` | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:25` |
2667
+ | `--hub-loading-bar-offset` | `0px` | Distance from the edge the `overlay` and `fixed` modes attach to | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:28` |
2668
+ | `--hub-loading-bar-z-index` | `var(--hub-sys-zindex-sticky, 1020)` | Stacking order of the positioned modes, at chrome level | `IN_USE` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:31` |
2669
+ | `--hub-loading-bar-progress` | `0%` | Internal runtime fill, written by the component from its resolved value | `INTERNAL` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:33` |
2670
+ | `--hub-loading-bar-sweep-direction` | `1` | Internal runtime sign flipping the `indeterminate` sweep under `[dir='rtl']` | `INTERNAL` | `loading/src/lib/components/loading-bar/loading-bar.component.scss:35` |
2668
2671
 
2669
2672
  ## Appendix
2670
2673
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ng-hub-ui-ds",
3
- "version": "22.9.3",
3
+ "version": "22.11.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "build:tokens": "sass styles/tokens/hub-tokens.scss styles/tokens/hub-tokens.css --style=expanded --no-source-map",
@@ -48,11 +48,17 @@
48
48
  color: var(--hub-sys-color-#{$variant}-on);
49
49
  }
50
50
 
51
- /// Solid border on all sides, token-driven colour. The colour is emitted through
52
- /// `--hub-border-opacity` (default 1), so `.border-opacity-*` fades it.
53
- /// @param {Number} $width Border width.
51
+ /// Solid border on all sides, token-driven colour AND width. The colour is emitted
52
+ /// through `--hub-border-opacity` (default 1), so `.border-opacity-*` fades it; the
53
+ /// width goes through `--hub-border-width`, which is what lets a theme flatten every
54
+ /// boxed surface at once. Before this, the 1px was written into the rule, so a theme
55
+ /// could recolour a border but never remove or thicken one.
56
+ /// @param {Number} $width Border width; defaults to the themable width chain.
54
57
  /// @param {String} $color Any CSS colour; defaults to the canonical border token.
55
- @mixin border($width: 1px, $color: var(--hub-sys-border-color-default)) {
58
+ @mixin border(
59
+ $width: var(--hub-border-width, var(--hub-ref-border-width, 1px)),
60
+ $color: var(--hub-sys-border-color-default)
61
+ ) {
56
62
  border: $width solid color-mix(in srgb, #{$color} calc(var(--hub-border-opacity, 1) * 100%), transparent);
57
63
  }
58
64
 
@@ -32,6 +32,13 @@
32
32
  /// @param {Map} $gap Gap step → value (`--hub-sys-gap-*`) — only to decouple gap from the spacing scale.
33
33
  /// @param {Map} $radius Radius step (none…xxl|pill) → value (`--hub-ref-radius-*`).
34
34
  /// @param {Map} $shadow Shadow size (sm|md|lg|inset) → value (`--hub-sys-shadow-*`).
35
+ /// @param {Map} $surfaces Surface role (page|elevated) → colour (`--hub-sys-surface-*`). Every accent
36
+ /// role family re-derives against it, so changing the page surface repaints
37
+ /// the tints without listing a single one of them.
38
+ /// @param {Map} $borders Border role → value. `color` sets `--hub-sys-border-color-default`; `width`
39
+ /// and `side-width` set the two chains the utilities read — the box drawn
40
+ /// around a surface and the rule that separates one row from the next. A flat
41
+ /// theme sets `width: 0` and leaves `side-width` alone.
35
42
  /// @param {Map} $font-family Family (base|display|mono) → stack (`--hub-ref-font-family-*`).
36
43
  /// @param {Map} $font-size Size step (xs|sm|base|lg) → value (`--hub-ref-font-size-*`; the fs-1…6 heading scale re-derives from base).
37
44
  /// @param {Map} $font-weight Weight (light|base|medium|semibold|bold) → value (`--hub-ref-font-weight-*`).
@@ -43,6 +50,8 @@
43
50
  $gap: (),
44
51
  $radius: (),
45
52
  $shadow: (),
53
+ $surfaces: (),
54
+ $borders: (),
46
55
  $font-family: (),
47
56
  $font-size: (),
48
57
  $font-weight: (),
@@ -64,6 +73,16 @@
64
73
  @each $size, $value in $shadow {
65
74
  --hub-sys-shadow-#{$size}: #{$value};
66
75
  }
76
+ @each $role, $value in $surfaces {
77
+ --hub-sys-surface-#{$role}: #{$value};
78
+ }
79
+ @each $role, $value in $borders {
80
+ @if $role == color {
81
+ --hub-sys-border-color-default: #{$value};
82
+ } @else {
83
+ --hub-border-#{$role}: #{$value};
84
+ }
85
+ }
67
86
  @each $family, $value in $font-family {
68
87
  --hub-ref-font-family-#{$family}: #{$value};
69
88
  }
@@ -255,7 +255,7 @@
255
255
 
256
256
  -subtle = 12% accent over surface (tinted background)
257
257
  -border-subtle = 35% accent over surface (hairline border)
258
- -emphasis = 80% accent over ink (text / strong)
258
+ -emphasis = accent with L capped at .45 (text / strong)
259
259
  -on = grayscale contrast flip (text on the accent)
260
260
  ============================================================ */
261
261
  /* ============================================================
@@ -286,10 +286,24 @@
286
286
  var(--hub-sys-color-primary) 35%,
287
287
  var(--hub-sys-surface-page, #fff)
288
288
  );
289
- --hub-sys-color-primary-emphasis: color-mix(
290
- in oklch,
291
- var(--hub-sys-color-primary) 80%,
292
- var(--hub-sys-color-ink, #212529)
289
+ /**
290
+ * The emphasis role is what text is painted with, so its luminosity is capped
291
+ * rather than mixed.
292
+ *
293
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
294
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
295
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
296
+ * or a warning outline button inherited unreadable text — and no theme could fix
297
+ * it, because the formula lives here.
298
+ *
299
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
300
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
301
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
302
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
303
+ * was failing. `dark` is untouched, since it already sat at L .26.
304
+ */
305
+ --hub-sys-color-primary-emphasis: oklch(
306
+ from var(--hub-sys-color-primary) min(l, 0.45) c h
293
307
  );
294
308
  --hub-sys-color-primary-on: oklch(from var(--hub-sys-color-primary) clamp(0, (0.62 - l) * 1000, 1) 0 h);
295
309
  --hub-sys-color-secondary-subtle: color-mix(
@@ -302,10 +316,24 @@
302
316
  var(--hub-sys-color-secondary) 35%,
303
317
  var(--hub-sys-surface-page, #fff)
304
318
  );
305
- --hub-sys-color-secondary-emphasis: color-mix(
306
- in oklch,
307
- var(--hub-sys-color-secondary) 80%,
308
- var(--hub-sys-color-ink, #212529)
319
+ /**
320
+ * The emphasis role is what text is painted with, so its luminosity is capped
321
+ * rather than mixed.
322
+ *
323
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
324
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
325
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
326
+ * or a warning outline button inherited unreadable text — and no theme could fix
327
+ * it, because the formula lives here.
328
+ *
329
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
330
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
331
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
332
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
333
+ * was failing. `dark` is untouched, since it already sat at L .26.
334
+ */
335
+ --hub-sys-color-secondary-emphasis: oklch(
336
+ from var(--hub-sys-color-secondary) min(l, 0.45) c h
309
337
  );
310
338
  --hub-sys-color-secondary-on: oklch(from var(--hub-sys-color-secondary) clamp(0, (0.62 - l) * 1000, 1) 0 h);
311
339
  --hub-sys-color-success-subtle: color-mix(
@@ -318,10 +346,24 @@
318
346
  var(--hub-sys-color-success) 35%,
319
347
  var(--hub-sys-surface-page, #fff)
320
348
  );
321
- --hub-sys-color-success-emphasis: color-mix(
322
- in oklch,
323
- var(--hub-sys-color-success) 80%,
324
- var(--hub-sys-color-ink, #212529)
349
+ /**
350
+ * The emphasis role is what text is painted with, so its luminosity is capped
351
+ * rather than mixed.
352
+ *
353
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
354
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
355
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
356
+ * or a warning outline button inherited unreadable text — and no theme could fix
357
+ * it, because the formula lives here.
358
+ *
359
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
360
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
361
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
362
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
363
+ * was failing. `dark` is untouched, since it already sat at L .26.
364
+ */
365
+ --hub-sys-color-success-emphasis: oklch(
366
+ from var(--hub-sys-color-success) min(l, 0.45) c h
325
367
  );
326
368
  --hub-sys-color-success-on: oklch(from var(--hub-sys-color-success) clamp(0, (0.62 - l) * 1000, 1) 0 h);
327
369
  --hub-sys-color-danger-subtle: color-mix(
@@ -334,10 +376,24 @@
334
376
  var(--hub-sys-color-danger) 35%,
335
377
  var(--hub-sys-surface-page, #fff)
336
378
  );
337
- --hub-sys-color-danger-emphasis: color-mix(
338
- in oklch,
339
- var(--hub-sys-color-danger) 80%,
340
- var(--hub-sys-color-ink, #212529)
379
+ /**
380
+ * The emphasis role is what text is painted with, so its luminosity is capped
381
+ * rather than mixed.
382
+ *
383
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
384
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
385
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
386
+ * or a warning outline button inherited unreadable text — and no theme could fix
387
+ * it, because the formula lives here.
388
+ *
389
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
390
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
391
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
392
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
393
+ * was failing. `dark` is untouched, since it already sat at L .26.
394
+ */
395
+ --hub-sys-color-danger-emphasis: oklch(
396
+ from var(--hub-sys-color-danger) min(l, 0.45) c h
341
397
  );
342
398
  --hub-sys-color-danger-on: oklch(from var(--hub-sys-color-danger) clamp(0, (0.62 - l) * 1000, 1) 0 h);
343
399
  --hub-sys-color-warning-subtle: color-mix(
@@ -350,10 +406,24 @@
350
406
  var(--hub-sys-color-warning) 35%,
351
407
  var(--hub-sys-surface-page, #fff)
352
408
  );
353
- --hub-sys-color-warning-emphasis: color-mix(
354
- in oklch,
355
- var(--hub-sys-color-warning) 80%,
356
- var(--hub-sys-color-ink, #212529)
409
+ /**
410
+ * The emphasis role is what text is painted with, so its luminosity is capped
411
+ * rather than mixed.
412
+ *
413
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
414
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
415
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
416
+ * or a warning outline button inherited unreadable text — and no theme could fix
417
+ * it, because the formula lives here.
418
+ *
419
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
420
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
421
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
422
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
423
+ * was failing. `dark` is untouched, since it already sat at L .26.
424
+ */
425
+ --hub-sys-color-warning-emphasis: oklch(
426
+ from var(--hub-sys-color-warning) min(l, 0.45) c h
357
427
  );
358
428
  --hub-sys-color-warning-on: oklch(from var(--hub-sys-color-warning) clamp(0, (0.62 - l) * 1000, 1) 0 h);
359
429
  --hub-sys-color-info-subtle: color-mix(
@@ -366,10 +436,24 @@
366
436
  var(--hub-sys-color-info) 35%,
367
437
  var(--hub-sys-surface-page, #fff)
368
438
  );
369
- --hub-sys-color-info-emphasis: color-mix(
370
- in oklch,
371
- var(--hub-sys-color-info) 80%,
372
- var(--hub-sys-color-ink, #212529)
439
+ /**
440
+ * The emphasis role is what text is painted with, so its luminosity is capped
441
+ * rather than mixed.
442
+ *
443
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
444
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
445
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
446
+ * or a warning outline button inherited unreadable text — and no theme could fix
447
+ * it, because the formula lives here.
448
+ *
449
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
450
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
451
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
452
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
453
+ * was failing. `dark` is untouched, since it already sat at L .26.
454
+ */
455
+ --hub-sys-color-info-emphasis: oklch(
456
+ from var(--hub-sys-color-info) min(l, 0.45) c h
373
457
  );
374
458
  --hub-sys-color-info-on: oklch(from var(--hub-sys-color-info) clamp(0, (0.62 - l) * 1000, 1) 0 h);
375
459
  --hub-sys-color-neutral-subtle: color-mix(
@@ -382,10 +466,24 @@
382
466
  var(--hub-sys-color-neutral) 35%,
383
467
  var(--hub-sys-surface-page, #fff)
384
468
  );
385
- --hub-sys-color-neutral-emphasis: color-mix(
386
- in oklch,
387
- var(--hub-sys-color-neutral) 80%,
388
- var(--hub-sys-color-ink, #212529)
469
+ /**
470
+ * The emphasis role is what text is painted with, so its luminosity is capped
471
+ * rather than mixed.
472
+ *
473
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
474
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
475
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
476
+ * or a warning outline button inherited unreadable text — and no theme could fix
477
+ * it, because the formula lives here.
478
+ *
479
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
480
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
481
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
482
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
483
+ * was failing. `dark` is untouched, since it already sat at L .26.
484
+ */
485
+ --hub-sys-color-neutral-emphasis: oklch(
486
+ from var(--hub-sys-color-neutral) min(l, 0.45) c h
389
487
  );
390
488
  --hub-sys-color-neutral-on: oklch(from var(--hub-sys-color-neutral) clamp(0, (0.62 - l) * 1000, 1) 0 h);
391
489
  --hub-sys-color-light-subtle: color-mix(
@@ -398,10 +496,24 @@
398
496
  var(--hub-sys-color-light) 35%,
399
497
  var(--hub-sys-surface-page, #fff)
400
498
  );
401
- --hub-sys-color-light-emphasis: color-mix(
402
- in oklch,
403
- var(--hub-sys-color-light) 80%,
404
- var(--hub-sys-color-ink, #212529)
499
+ /**
500
+ * The emphasis role is what text is painted with, so its luminosity is capped
501
+ * rather than mixed.
502
+ *
503
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
504
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
505
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
506
+ * or a warning outline button inherited unreadable text — and no theme could fix
507
+ * it, because the formula lives here.
508
+ *
509
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
510
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
511
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
512
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
513
+ * was failing. `dark` is untouched, since it already sat at L .26.
514
+ */
515
+ --hub-sys-color-light-emphasis: oklch(
516
+ from var(--hub-sys-color-light) min(l, 0.45) c h
405
517
  );
406
518
  --hub-sys-color-light-on: oklch(from var(--hub-sys-color-light) clamp(0, (0.62 - l) * 1000, 1) 0 h);
407
519
  --hub-sys-color-dark-subtle: color-mix(
@@ -414,10 +526,24 @@
414
526
  var(--hub-sys-color-dark) 35%,
415
527
  var(--hub-sys-surface-page, #fff)
416
528
  );
417
- --hub-sys-color-dark-emphasis: color-mix(
418
- in oklch,
419
- var(--hub-sys-color-dark) 80%,
420
- var(--hub-sys-color-ink, #212529)
529
+ /**
530
+ * The emphasis role is what text is painted with, so its luminosity is capped
531
+ * rather than mixed.
532
+ *
533
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
534
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
535
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
536
+ * or a warning outline button inherited unreadable text — and no theme could fix
537
+ * it, because the formula lives here.
538
+ *
539
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
540
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
541
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
542
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
543
+ * was failing. `dark` is untouched, since it already sat at L .26.
544
+ */
545
+ --hub-sys-color-dark-emphasis: oklch(
546
+ from var(--hub-sys-color-dark) min(l, 0.45) c h
421
547
  );
422
548
  --hub-sys-color-dark-on: oklch(from var(--hub-sys-color-dark) clamp(0, (0.62 - l) * 1000, 1) 0 h);
423
549
  --hub-sys-color-ink: var(--hub-ref-color-gray-900, #212529);
@@ -294,7 +294,7 @@ $hub-gap-steps: (0, 1, 2, 3, 4, 5) !default;
294
294
 
295
295
  -subtle = 12% accent over surface (tinted background)
296
296
  -border-subtle = 35% accent over surface (hairline border)
297
- -emphasis = 80% accent over ink (text / strong)
297
+ -emphasis = accent with L capped at .45 (text / strong)
298
298
  -on = grayscale contrast flip (text on the accent)
299
299
  ============================================================ */
300
300
 
@@ -402,10 +402,24 @@ $hub-accents-terminal: (
402
402
  var(--hub-sys-color-#{$name}) 35%,
403
403
  var(--hub-sys-surface-page, #fff)
404
404
  );
405
- --hub-sys-color-#{$name}-emphasis: color-mix(
406
- in oklch,
407
- var(--hub-sys-color-#{$name}) 80%,
408
- var(--hub-sys-color-ink, #212529)
405
+ /**
406
+ * The emphasis role is what text is painted with, so its luminosity is capped
407
+ * rather than mixed.
408
+ *
409
+ * This used to be `color-mix(accent 80%, ink)`, and a percentage cannot darken a
410
+ * pale hue: the default amber `#ffc107` came out at 2.5:1 on white and the default
411
+ * cyan at 2.9:1. Both are shipped colours, so every product using `.text-warning`
412
+ * or a warning outline button inherited unreadable text — and no theme could fix
413
+ * it, because the formula lives here.
414
+ *
415
+ * `min(l, 0.45)` leaves an already-dark accent exactly where it was and pulls a
416
+ * light one down to where it can carry a letter, keeping hue and chroma. Measured
417
+ * on white across the nine defaults: nothing below 6.6:1 — info is the floor at
418
+ * 6.62, success next at 6.91 — and no role moves by more than a point unless it
419
+ * was failing. `dark` is untouched, since it already sat at L .26.
420
+ */
421
+ --hub-sys-color-#{$name}-emphasis: oklch(
422
+ from var(--hub-sys-color-#{$name}) min(l, 0.45) c h
409
423
  );
410
424
  // Contrast / on-color: a grayscale flip driven by the accent's own lightness.
411
425
  // Dark accents (L < .62) resolve to white text; light accents to near-black.
@@ -198,9 +198,19 @@
198
198
  color: var(--hub-sys-color-dark-on);
199
199
  }
200
200
 
201
- /* ── Borders — additive sides ── */
201
+ /* ── Borders — additive sides ──
202
+ *
203
+ * Width travels on two separate chains on purpose. `.border` draws the box around a
204
+ * surface, and a flat theme wants that gone; the single-side utilities draw the rule
205
+ * that separates one row from the next, and that is structure the theme has no
206
+ * business erasing. One variable for both would force a theme to choose between
207
+ * keeping its boxes and losing its separators.
208
+ *
209
+ * --hub-border-width the box → set to 0 for a theme with no cards
210
+ * --hub-border-side-width the rule → left alone by a flat theme
211
+ */
202
212
  .border {
203
- border: 1px solid color-mix(in srgb, var(--hub-sys-border-color-default) calc(var(--hub-border-opacity, 1) * 100%), transparent);
213
+ border: var(--hub-border-width, var(--hub-ref-border-width, 1px)) solid color-mix(in srgb, var(--hub-sys-border-color-default) calc(var(--hub-border-opacity, 1) * 100%), transparent);
204
214
  }
205
215
 
206
216
  .border-0 {
@@ -208,7 +218,7 @@
208
218
  }
209
219
 
210
220
  .border-top {
211
- border-block-start: 1px solid var(--hub-sys-border-color-default);
221
+ border-block-start: var(--hub-border-side-width, var(--hub-ref-border-width, 1px)) solid var(--hub-sys-border-color-default);
212
222
  }
213
223
 
214
224
  .border-top-0 {
@@ -216,7 +226,7 @@
216
226
  }
217
227
 
218
228
  .border-bottom {
219
- border-block-end: 1px solid var(--hub-sys-border-color-default);
229
+ border-block-end: var(--hub-border-side-width, var(--hub-ref-border-width, 1px)) solid var(--hub-sys-border-color-default);
220
230
  }
221
231
 
222
232
  .border-bottom-0 {
@@ -224,7 +234,7 @@
224
234
  }
225
235
 
226
236
  .border-start {
227
- border-inline-start: 1px solid var(--hub-sys-border-color-default);
237
+ border-inline-start: var(--hub-border-side-width, var(--hub-ref-border-width, 1px)) solid var(--hub-sys-border-color-default);
228
238
  }
229
239
 
230
240
  .border-start-0 {
@@ -232,7 +242,7 @@
232
242
  }
233
243
 
234
244
  .border-end {
235
- border-inline-end: 1px solid var(--hub-sys-border-color-default);
245
+ border-inline-end: var(--hub-border-side-width, var(--hub-ref-border-width, 1px)) solid var(--hub-sys-border-color-default);
236
246
  }
237
247
 
238
248
  .border-end-0 {
@@ -74,7 +74,17 @@ $_accents: primary, secondary, success, danger, warning, info, neutral, light, d
74
74
  }
75
75
  }
76
76
 
77
- /* ── Borders — additive sides ── */
77
+ /* ── Borders — additive sides ──
78
+ *
79
+ * Width travels on two separate chains on purpose. `.border` draws the box around a
80
+ * surface, and a flat theme wants that gone; the single-side utilities draw the rule
81
+ * that separates one row from the next, and that is structure the theme has no
82
+ * business erasing. One variable for both would force a theme to choose between
83
+ * keeping its boxes and losing its separators.
84
+ *
85
+ * --hub-border-width the box → set to 0 for a theme with no cards
86
+ * --hub-border-side-width the rule → left alone by a flat theme
87
+ */
78
88
  .border {
79
89
  @include surfaces.border();
80
90
  }
@@ -82,25 +92,29 @@ $_accents: primary, secondary, success, danger, warning, info, neutral, light, d
82
92
  border: 0;
83
93
  }
84
94
  .border-top {
85
- border-block-start: 1px solid var(--hub-sys-border-color-default);
95
+ border-block-start: var(--hub-border-side-width, var(--hub-ref-border-width, 1px)) solid
96
+ var(--hub-sys-border-color-default);
86
97
  }
87
98
  .border-top-0 {
88
99
  border-block-start: 0;
89
100
  }
90
101
  .border-bottom {
91
- border-block-end: 1px solid var(--hub-sys-border-color-default);
102
+ border-block-end: var(--hub-border-side-width, var(--hub-ref-border-width, 1px)) solid
103
+ var(--hub-sys-border-color-default);
92
104
  }
93
105
  .border-bottom-0 {
94
106
  border-block-end: 0;
95
107
  }
96
108
  .border-start {
97
- border-inline-start: 1px solid var(--hub-sys-border-color-default);
109
+ border-inline-start: var(--hub-border-side-width, var(--hub-ref-border-width, 1px)) solid
110
+ var(--hub-sys-border-color-default);
98
111
  }
99
112
  .border-start-0 {
100
113
  border-inline-start: 0;
101
114
  }
102
115
  .border-end {
103
- border-inline-end: 1px solid var(--hub-sys-border-color-default);
116
+ border-inline-end: var(--hub-border-side-width, var(--hub-ref-border-width, 1px)) solid
117
+ var(--hub-sys-border-color-default);
104
118
  }
105
119
  .border-end-0 {
106
120
  border-inline-end: 0;