@tensor_1/tokens 2.46.0 → 2.48.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.
Files changed (66) hide show
  1. package/build/css/components.css +1 -1
  2. package/build/css/styles.css +1 -1
  3. package/package.json +1 -1
  4. package/styles/components/alert.css +6 -4
  5. package/styles/components/app-shell.css +40 -0
  6. package/styles/components/arrival.css +7 -1
  7. package/styles/components/attachment.css +4 -1
  8. package/styles/components/audit-log.css +29 -0
  9. package/styles/components/breadcrumbs.css +2 -0
  10. package/styles/components/busy-region.css +55 -0
  11. package/styles/components/button.css +42 -6
  12. package/styles/components/card.css +10 -0
  13. package/styles/components/change-highlight.css +58 -0
  14. package/styles/components/checkbox.css +18 -0
  15. package/styles/components/code-display.css +43 -0
  16. package/styles/components/combobox.css +4 -1
  17. package/styles/components/command-palette.css +16 -2
  18. package/styles/components/contact-card.css +17 -6
  19. package/styles/components/count.css +23 -0
  20. package/styles/components/data-workspace.css +42 -0
  21. package/styles/components/date-picker.css +14 -1
  22. package/styles/components/detail-layout.css +9 -0
  23. package/styles/components/diff-viewer.css +5 -0
  24. package/styles/components/display-preferences.css +38 -0
  25. package/styles/components/editorial-home.css +24 -2
  26. package/styles/components/flow.css +5 -0
  27. package/styles/components/heartbeat.css +8 -0
  28. package/styles/components/icon.css +14 -0
  29. package/styles/components/inline-edit.css +19 -3
  30. package/styles/components/input.css +62 -0
  31. package/styles/components/lightbox.css +13 -0
  32. package/styles/components/list.css +2 -0
  33. package/styles/components/match-review.css +40 -0
  34. package/styles/components/media.css +4 -2
  35. package/styles/components/metric-input.css +3 -1
  36. package/styles/components/modal.css +4 -0
  37. package/styles/components/page-header.css +12 -0
  38. package/styles/components/pagination.css +2 -0
  39. package/styles/components/popover.css +4 -0
  40. package/styles/components/print-layout.css +177 -0
  41. package/styles/components/progress.css +7 -0
  42. package/styles/components/qr-code.css +30 -0
  43. package/styles/components/qr-scanner.css +61 -0
  44. package/styles/components/relationship-graph.css +43 -8
  45. package/styles/components/rule-builder.css +2 -1
  46. package/styles/components/save-status.css +5 -0
  47. package/styles/components/scheduling-calendar.css +82 -1
  48. package/styles/components/search-suggest.css +2 -0
  49. package/styles/components/segmented.css +10 -1
  50. package/styles/components/select.css +11 -0
  51. package/styles/components/sidebar.css +51 -1
  52. package/styles/components/sortable-list.css +42 -0
  53. package/styles/components/spinner.css +5 -0
  54. package/styles/components/stat-tile.css +36 -12
  55. package/styles/components/states.css +5 -0
  56. package/styles/components/status-pill.css +28 -6
  57. package/styles/components/steps.css +2 -0
  58. package/styles/components/table-toolbar.css +1 -1
  59. package/styles/components/table.css +18 -5
  60. package/styles/components/tabs.css +8 -3
  61. package/styles/components/tag-input.css +21 -0
  62. package/styles/components/textarea.css +3 -0
  63. package/styles/components/toast.css +12 -0
  64. package/styles/components/tree.css +3 -1
  65. package/styles/components/view-menu.css +3 -0
  66. package/styles/motion.css +18 -0
@@ -0,0 +1,30 @@
1
+ /* uix QR code (HAR-1251) — React QrCode renders an <svg class="uix-qr-code"> with a paper rect
2
+ and one path of dark modules; the four-module quiet zone is inside the viewBox.
3
+ Scanners read dark on light, so the symbol never follows the theme: it is print ink on print
4
+ paper in light, dark, print and forced colours alike, and it is printed as drawn. */
5
+ @layer uix.components {
6
+ .uix-qr-code {
7
+ display: block;
8
+ max-width: 100%;
9
+ height: auto;
10
+ aspect-ratio: 1;
11
+ border-radius: var(--uix-radius-sm);
12
+ forced-color-adjust: none;
13
+ -webkit-print-color-adjust: exact;
14
+ print-color-adjust: exact;
15
+ }
16
+ .uix-qr-code__paper { fill: var(--uix-print-paper); }
17
+ .uix-qr-code__ink { fill: var(--uix-print-ink); }
18
+
19
+ /* value too long for a QR code: a square that says so, never a broken symbol */
20
+ .uix-qr-code--error {
21
+ display: grid;
22
+ place-items: center;
23
+ padding: var(--uix-space-3);
24
+ border: 1px dashed var(--uix-border-strong);
25
+ background: var(--uix-bg-subtle);
26
+ color: var(--uix-text-muted);
27
+ font-size: var(--uix-text-meta);
28
+ text-align: center;
29
+ }
30
+ }
@@ -0,0 +1,61 @@
1
+ /* uix QR scanner (HAR-1254) — React QrScanner: a camera viewfinder with an explainer before the
2
+ browser's prompt, plain states for blocked / unsupported / no camera / no picture, and the
3
+ manual entry always below it. The frame is 4:3 while the camera runs; in every other state it
4
+ is a padded panel that grows with its text, so nothing is cut at 320 px.
5
+ No decorative scanning line: the target is still, and a read is answered once (the target
6
+ turns to the success colour for a moment; a status message says what was read). */
7
+ @layer uix.components {
8
+ .uix-qr-scanner { display: grid; gap: var(--uix-space-3); width: 100%; max-width: 32rem; min-width: 0; }
9
+
10
+ .uix-qr-scanner__frame {
11
+ position: relative;
12
+ display: grid;
13
+ place-items: center;
14
+ min-height: 12rem;
15
+ overflow: hidden;
16
+ border: 1px solid var(--uix-border);
17
+ border-radius: var(--uix-radius-lg);
18
+ background: var(--uix-bg-subtle);
19
+ }
20
+ .uix-qr-scanner[data-status="scanning"] .uix-qr-scanner__frame { aspect-ratio: 4 / 3; min-height: 0; background: var(--uix-print-ink); border-color: transparent; }
21
+ .uix-qr-scanner__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
22
+ .uix-qr-scanner__video[hidden] { display: none; }
23
+
24
+ /* the aiming square: four corners, still */
25
+ .uix-qr-scanner__target {
26
+ --target-ink: var(--uix-print-paper);
27
+ position: relative;
28
+ width: min(62%, 16rem);
29
+ aspect-ratio: 1;
30
+ border-radius: var(--uix-radius-md);
31
+ background:
32
+ linear-gradient(var(--target-ink), var(--target-ink)) top left / 24px 3px no-repeat,
33
+ linear-gradient(var(--target-ink), var(--target-ink)) top left / 3px 24px no-repeat,
34
+ linear-gradient(var(--target-ink), var(--target-ink)) top right / 24px 3px no-repeat,
35
+ linear-gradient(var(--target-ink), var(--target-ink)) top right / 3px 24px no-repeat,
36
+ linear-gradient(var(--target-ink), var(--target-ink)) bottom left / 24px 3px no-repeat,
37
+ linear-gradient(var(--target-ink), var(--target-ink)) bottom left / 3px 24px no-repeat,
38
+ linear-gradient(var(--target-ink), var(--target-ink)) bottom right / 24px 3px no-repeat,
39
+ linear-gradient(var(--target-ink), var(--target-ink)) bottom right / 3px 24px no-repeat;
40
+ box-shadow: 0 0 0 100vmax color-mix(in srgb, var(--uix-print-ink) 35%, transparent);
41
+ transition: opacity var(--uix-dur-fast) var(--uix-ease-out), transform var(--uix-dur-fast) var(--uix-ease-out);
42
+ }
43
+ /* did it work: the corners turn green and settle in, once */
44
+ .uix-qr-scanner__frame[data-read] .uix-qr-scanner__target { --target-ink: var(--uix-success-solid); transform: scale(.96); }
45
+
46
+ .uix-qr-scanner__panel { display: grid; justify-items: start; gap: var(--uix-space-3); width: 100%; padding: var(--uix-space-5); }
47
+ .uix-qr-scanner__title { margin: 0; font-weight: 600; font-size: var(--uix-text-h3); }
48
+ .uix-qr-scanner__body { margin: 0; color: var(--uix-text-muted); }
49
+ .uix-qr-scanner[data-status="denied"] .uix-qr-scanner__title { color: var(--uix-danger-text); }
50
+ .uix-qr-scanner__explainer { color: var(--uix-text); max-width: 60ch; }
51
+
52
+ .uix-qr-scanner__bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--uix-space-2); }
53
+ .uix-qr-scanner__message { flex: 1 1 12rem; margin: 0; color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
54
+ .uix-qr-scanner__message[data-hint] { color: var(--uix-warning-text); }
55
+ .uix-qr-scanner__manual { padding-top: var(--uix-space-3); border-top: 1px solid var(--uix-border); }
56
+
57
+ @media (prefers-reduced-motion: reduce) {
58
+ .uix-qr-scanner__target { transition: none; }
59
+ .uix-qr-scanner__frame[data-read] .uix-qr-scanner__target { transform: none; }
60
+ }
61
+ }
@@ -15,12 +15,22 @@
15
15
  .uix-relationship-graph__edge[data-typed] { stroke:var(--uix-graph-type-color, var(--uix-border-strong)); }
16
16
  .uix-relationship-graph__list { padding:var(--uix-space-3); border:1px solid var(--uix-border); border-radius:var(--uix-radius-lg); background:var(--uix-surface); }
17
17
  .uix-relationship-graph__list>ul { display:grid; gap:var(--uix-space-2); margin:var(--uix-space-2) 0 0; padding:0; list-style:none; }
18
- .uix-relationship-graph__list li { display:flex; justify-content:space-between; gap:var(--uix-space-2); padding:var(--uix-space-2); border-radius:var(--uix-radius-md); background:var(--uix-bg-subtle); }
19
- .uix-relationship-graph__list li>button:first-child { display:grid; gap:var(--uix-space-1); flex:1; border:0; background:transparent; color:var(--uix-text); text-align:left; } .uix-relationship-graph__list li>button span { color:var(--uix-text-muted); font-size:var(--uix-text-meta); }
18
+ .uix-relationship-graph__list>ul>li { display:flex; justify-content:space-between; gap:var(--uix-space-2); padding:var(--uix-space-2); border-radius:var(--uix-radius-md); background:var(--uix-bg-subtle); }
19
+ /* A row: the node's button (its name only), then its neighbours as a list of their own (HAR-1387). */
20
+ .uix-relationship-graph__list-main { display:grid; flex:1; gap:var(--uix-space-1); min-width:0; }
21
+ .uix-relationship-graph__list-main>button { display:grid; justify-items:start; gap:var(--uix-space-1); padding:0; border:0; background:transparent; color:var(--uix-text); font:inherit; text-align:left; cursor:pointer; }
22
+ .uix-relationship-graph__list-main>button span,.uix-relationship-graph__list-relations,.uix-relationship-graph__list-none { color:var(--uix-text-muted); font-size:var(--uix-text-meta); }
23
+ .uix-relationship-graph__list-none { margin:0; }
24
+ /* "Connected to", then the names wrapping beside it, comma after comma, then "Show all N" */
25
+ .uix-relationship-graph__list-relations { display:flex; flex-wrap:wrap; align-items:baseline; gap:0 var(--uix-space-1); }
26
+ .uix-relationship-graph__list-relations ul { display:flex; flex:1 1 12rem; flex-wrap:wrap; gap:0 var(--uix-space-1); min-width:0; margin:0; padding:0; list-style:none; }
27
+ .uix-relationship-graph__list-relations li { min-width:0; overflow-wrap:anywhere; }
28
+ .uix-relationship-graph__list-relations li:not(:last-child)::after { content:","; }
29
+ .uix-relationship-graph__list-more { font-size:inherit; }
20
30
  .uix-relationship-graph__list-actions { display:flex; flex-wrap:wrap; gap:var(--uix-space-1); }
21
31
  .uix-relationship-graph--compact .uix-relationship-graph__visual,.uix-relationship-graph--compact .uix-relationship-graph__visual svg { min-height:16rem; height:16rem; }
22
32
  .uix-relationship-graph--state { padding:var(--uix-space-8); border:1px dashed var(--uix-border-strong); border-radius:var(--uix-radius-lg); }
23
- @media (max-width:640px) { .uix-relationship-graph__list li { display:grid; } }
33
+ @media (max-width:640px) { .uix-relationship-graph__list>ul>li { display:grid; } }
24
34
 
25
35
  /* Layered mode (ADR-0002): HTML node buttons over an SVG edge layer, one transformed stage. */
26
36
  .uix-relationship-graph--layered .uix-relationship-graph__toolbar button:disabled { opacity:.5; cursor:not-allowed; }
@@ -29,11 +39,13 @@
29
39
  .uix-relationship-graph__stage { position:absolute; top:0; left:0; transform-origin:0 0; }
30
40
  .uix-relationship-graph__stage[data-animate] { transition:transform var(--uix-dur) var(--uix-ease-out); }
31
41
  .uix-relationship-graph__edges { position:absolute; top:0; left:0; overflow:visible; pointer-events:none; }
32
- .uix-relationship-graph__link path { fill:none; stroke:color-mix(in srgb, var(--uix-text) 28%, transparent); stroke-width:1.5px; }
33
- .uix-relationship-graph__link polygon { fill:color-mix(in srgb, var(--uix-text) 45%, transparent); }
42
+ /* Opaque, mixed into the map's own surface: fifty edges sharing a bus trunk stay one grey line
43
+ instead of stacking a translucent stroke to black (HAR-1387). */
44
+ .uix-relationship-graph__link path { fill:none; stroke:color-mix(in srgb, var(--uix-text) 28%, var(--uix-bg-subtle)); stroke-width:1.5px; }
45
+ .uix-relationship-graph__link polygon { fill:color-mix(in srgb, var(--uix-text) 45%, var(--uix-bg-subtle)); }
34
46
  .uix-relationship-graph__link text { fill:var(--uix-text-muted); font-family:var(--uix-font-sans); font-size:var(--uix-text-eyebrow); paint-order:stroke; stroke:var(--uix-bg-subtle); stroke-width:4px; stroke-linejoin:round; }
35
47
  .uix-relationship-graph__link[data-kind="lateral"] path, .uix-relationship-graph__link[data-kind="cycle"] path { stroke-dasharray:5 4; }
36
- .uix-relationship-graph__link[data-emphasis] path { stroke:color-mix(in srgb, var(--uix-text) 55%, transparent); }
48
+ .uix-relationship-graph__link[data-emphasis] path { stroke:color-mix(in srgb, var(--uix-text) 55%, var(--uix-bg-subtle)); }
37
49
  .uix-relationship-graph__link[data-active] path { stroke:var(--uix-accent); stroke-width:2.5px; }
38
50
  .uix-relationship-graph__link[data-active] polygon { fill:var(--uix-accent); }
39
51
  .uix-relationship-graph__link[data-active] text { fill:var(--uix-text); }
@@ -50,6 +62,8 @@
50
62
  .uix-relationship-graph__item[data-kind="cluster"] { border-style:dashed; background:var(--uix-surface-2); }
51
63
  .uix-relationship-graph__item-label { min-width:0; display:-webkit-box; overflow:hidden; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow-wrap:anywhere; font-size:var(--uix-text-body); line-height:1.25; }
52
64
  .uix-relationship-graph__item-meta { min-width:0; overflow:hidden; color:var(--uix-text-muted); font-size:var(--uix-text-meta); white-space:nowrap; text-overflow:ellipsis; }
65
+ /* The flag sits on the item's top-right border: the first line of the name keeps clear of it. */
66
+ .uix-relationship-graph__item[data-flag] .uix-relationship-graph__item-label { padding-right:3.25rem; }
53
67
  .uix-relationship-graph__item-flag { position:absolute; top:-9px; right:var(--uix-space-2); padding:0 var(--uix-space-1); border:1px solid var(--uix-border); border-radius:var(--uix-radius-pill); background:var(--uix-surface); color:var(--uix-text-muted); font-size:var(--uix-text-meta); line-height:1.4; }
54
68
  .uix-relationship-graph__collapse { position:absolute; height:22px; padding:0 var(--uix-space-2); border:1px dashed var(--uix-border-strong); border-radius:var(--uix-radius-pill); background:var(--uix-surface); color:var(--uix-text-muted); font-size:var(--uix-text-meta); cursor:pointer; }
55
69
  .uix-relationship-graph__collapse:focus-visible { outline:2px solid var(--uix-ring); outline-offset:2px; }
@@ -69,8 +83,10 @@
69
83
  .uix-relationship-graph__control:focus-visible { outline:2px solid var(--uix-ring); outline-offset:1px; }
70
84
  .uix-relationship-graph__control:disabled { opacity:.45; cursor:not-allowed; }
71
85
  .uix-relationship-graph__control svg { width:16px; height:16px; }
72
- .uix-relationship-graph__zoom-level { flex:0 1 auto; min-width:0; overflow:hidden; padding:0 var(--uix-space-1); border-radius:var(--uix-radius-sm); background:color-mix(in srgb, var(--uix-bg-subtle) 85%, transparent); color:var(--uix-text-muted); font-size:var(--uix-text-meta); font-variant-numeric:tabular-nums; white-space:nowrap; text-overflow:ellipsis; }
73
- .uix-relationship-graph__hint { flex:1 1 0; min-width:0; margin:0; overflow:hidden; color:var(--uix-text-muted); font-size:var(--uix-text-meta); white-space:nowrap; text-overflow:ellipsis; }
86
+ /* The hint and the zoom level sit on a surface of their own: the map scrolls under them, and a
87
+ node's name must not show through the words (HAR-1387). */
88
+ .uix-relationship-graph__zoom-level { flex:0 1 auto; min-width:0; overflow:hidden; padding:var(--uix-space-1) var(--uix-space-2); border:1px solid var(--uix-border); border-radius:var(--uix-radius-sm); background:var(--uix-surface); color:var(--uix-text-muted); font-size:var(--uix-text-meta); font-variant-numeric:tabular-nums; white-space:nowrap; text-overflow:ellipsis; }
89
+ .uix-relationship-graph__hint { flex:0 1000 auto; min-width:0; margin:0 auto 0 0; overflow:hidden; padding:var(--uix-space-1) var(--uix-space-2); border:1px solid var(--uix-border); border-radius:var(--uix-radius-sm); background:var(--uix-surface); color:var(--uix-text-muted); font-size:var(--uix-text-meta); white-space:nowrap; text-overflow:ellipsis; }
74
90
  /* A hint cut to a few words says nothing, so it goes; on the narrowest map so does the zoom level. */
75
91
  @container uix-relationship-graph-footer (max-width:24rem) { .uix-relationship-graph__hint { display:none; } }
76
92
  @container uix-relationship-graph-footer (max-width:17rem) { .uix-relationship-graph__zoom-level { display:none; } }
@@ -78,5 +94,24 @@
78
94
  .uix-relationship-graph--fullscreen { position:fixed; inset:0; z-index:var(--uix-z-overlay, 1000); display:flex; flex-direction:column; padding:var(--uix-space-4); background:var(--uix-bg-app, var(--uix-surface)); }
79
95
  .uix-relationship-graph--fullscreen .uix-relationship-graph__viewport { flex:1; min-height:0; height:auto; }
80
96
  @media (max-width:640px) { .uix-relationship-graph__hint { display:none; } }
97
+ /* ── Tones, variants and legends (HAR-1639) ────────────────────────────────────────────────
98
+ A tone is a colour a node or an edge shares with its legend item; it never stands alone (the
99
+ consumer says the same in text). A dashed edge is an expected relation that is missing. */
100
+ .uix-relationship-graph [data-tone="neutral"] { --uix-graph-tone:var(--uix-neutral-solid); }
101
+ .uix-relationship-graph [data-tone="info"] { --uix-graph-tone:var(--uix-info); }
102
+ .uix-relationship-graph [data-tone="success"] { --uix-graph-tone:var(--uix-success); }
103
+ .uix-relationship-graph [data-tone="warning"] { --uix-graph-tone:var(--uix-warning-text); }
104
+ .uix-relationship-graph [data-tone="danger"] { --uix-graph-tone:var(--uix-danger); }
105
+ .uix-relationship-graph [data-tone="accent"] { --uix-graph-tone:var(--uix-accent); }
106
+ .uix-relationship-graph__link[data-tone] path { stroke:var(--uix-graph-tone); stroke-width:2px; }
107
+ .uix-relationship-graph__link[data-tone] polygon { fill:var(--uix-graph-tone); }
108
+ .uix-relationship-graph__link[data-variant="dashed"] path,.uix-relationship-graph__edge[data-variant="dashed"] { stroke-dasharray:6 4; }
109
+ .uix-relationship-graph__edge[data-tone] { stroke:var(--uix-graph-tone); }
110
+ /* a toned node: its border and a bar on its leading edge */
111
+ .uix-relationship-graph__item[data-tone] { border-color:var(--uix-graph-tone); box-shadow:inset 4px 0 0 var(--uix-graph-tone); }
112
+ .uix-relationship-graph__legend li[data-tone]::before { border-color:var(--uix-graph-tone); }
113
+ .uix-relationship-graph__legend li:is([data-shape="edge"],[data-variant]):not([data-shape="node"])::before { width:1.5rem; height:0; border-width:2px 0 0; border-radius:0; }
114
+ .uix-relationship-graph__legend li[data-variant="dashed"]::before { border-top-style:dashed; }
115
+ .uix-relationship-graph__legend li[data-shape="node"]::before { width:1rem; height:.75rem; border-width:1px 1px 1px 4px; border-radius:var(--uix-radius-xs); }
81
116
  @media (forced-colors: active) { .uix-relationship-graph__link path { stroke:CanvasText; } .uix-relationship-graph__link polygon { fill:CanvasText; } .uix-relationship-graph__item { border-color:ButtonText; } }
82
117
  }
@@ -6,7 +6,8 @@
6
6
  /* a fixed combinator (HAR-1618): the Match choice as plain text, where the select was */
7
7
  .uix-rule-builder__match { font-size:var(--uix-text-meta); font-weight:400; color:var(--uix-text-muted); }
8
8
  .uix-rule-builder__rows { display:grid; gap:var(--uix-space-3); }
9
- .uix-rule-builder__condition,.uix-rule-builder__action { display:grid; grid-template-columns:minmax(9rem,1fr) minmax(9rem,1fr) minmax(10rem,1.4fr) auto; gap:var(--uix-space-2); align-items:start; padding:var(--uix-space-3); border-radius:var(--uix-radius-md); background:var(--uix-bg-subtle); }
9
+ /* a choice column grows to the label it shows before the value takes the rest, so "is greater than" is not cut off (HAR-1575) */
10
+ .uix-rule-builder__condition,.uix-rule-builder__action { display:grid; grid-template-columns:minmax(9rem,max-content) minmax(9rem,max-content) minmax(10rem,1fr) auto; gap:var(--uix-space-2); align-items:start; padding:var(--uix-space-3); border-radius:var(--uix-radius-md); background:var(--uix-bg-subtle); }
10
11
  .uix-rule-builder__nested { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:var(--uix-space-2); align-items:start; }
11
12
  .uix-rule-builder__nested .uix-rule-builder__group { border-left:3px solid var(--uix-accent); }
12
13
  .uix-rule-builder__row-actions,.uix-rule-builder__add { display:flex; flex-wrap:wrap; gap:var(--uix-space-1); }
@@ -16,3 +16,8 @@
16
16
  .uix-save-status[data-state="saving"] .uix-save-status__icon { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; }
17
17
  }
18
18
  }
19
+
20
+ /* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same pulse */
21
+ @layer uix.components {
22
+ @media (prefers-reduced-motion: no-preference) { :root[data-motion="reduce"] .uix-save-status[data-state="saving"] .uix-save-status__icon { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
23
+ }
@@ -170,7 +170,7 @@
170
170
  .uix-scheduling-calendar__legend-list { display:flex; flex-wrap:wrap; gap:var(--uix-space-2) var(--uix-space-3); margin:0; padding:0; list-style:none; }
171
171
  .uix-scheduling-calendar__legend-list > li { display:inline-flex; align-items:center; gap:var(--uix-space-1); margin:0; }
172
172
  .uix-scheduling-calendar__legend-caption { flex:1 1 100%; margin:0; }
173
- .uix-scheduling-calendar__swatch { flex:none; display:inline-flex; align-items:center; justify-content:center; width:1.5rem; height:0.875rem; border:1px solid var(--calendar-status-edge); border-radius:var(--uix-radius-xs); background:var(--calendar-status-surface); color:var(--calendar-status-text); }
173
+ .uix-scheduling-calendar__swatch { flex:none; display:inline-flex; align-items:center; justify-content:center; width:1.5rem; height:0.875rem; border:1px solid var(--calendar-status-edge); border-radius:var(--uix-radius-xs); background:var(--calendar-status-surface); color:var(--calendar-chrome-text); }
174
174
  .uix-scheduling-calendar__swatch > svg { width:0.75rem; height:0.75rem; }
175
175
  /* the built-in legend of the deprecated `state` values: the same shapes, no hue */
176
176
  .uix-scheduling-calendar__legend > span[data-state]::before { content:""; display:inline-block; width:0.625rem; height:0.625rem; margin-right:var(--uix-space-1); border:1px solid var(--calendar-status-edge-strong); border-radius:var(--uix-radius-xs); vertical-align:middle; }
@@ -313,6 +313,87 @@
313
313
  whatever theme the screen has). The calendar asks the browser to print its surfaces as they
314
314
  are; a browser may refuse, so no meaning rests on a fill or a pattern: each is an edge too.
315
315
  The first rule is the colour map again. */
316
+ /* ── Resource day (HAR-983): one column per resource on a slot axis ──────────────────────────
317
+ A free span is cut into bookable slots (a button each); a booking and time off stay whole;
318
+ time no span covers is "no schedule": hatched, with no button and no "free". No kind takes a
319
+ hue (the band alone does): free is open paper with a thin bar, a booking is filled with a heavy
320
+ bar and its words, time off is quiet italic words, no schedule is hatched. The grid scrolls
321
+ inside its own box, so the time gutter (left) and the heads (top) stay in view. */
322
+ .uix-scheduling-calendar__resources { --calendar-slot:1.75rem; --calendar-rs-column:9rem; position:relative; min-width:0; }
323
+ /* a refetch keeps every span readable and says it is loading in words, above the grid */
324
+ .uix-scheduling-calendar__rs-busy { display:block; margin-bottom:var(--uix-space-2); color:var(--calendar-chrome-text-quiet); font-size:var(--uix-text-meta); }
325
+ .uix-scheduling-calendar__rs-scroll { max-height:var(--uix-scheduling-calendar-rs-height,min(70vh,46rem)); overflow:auto; border:1px solid var(--calendar-chrome-line); border-radius:var(--uix-radius-sm); background:var(--calendar-chrome-surface); overscroll-behavior:contain; }
326
+ .uix-scheduling-calendar__rs-grid { display:grid; grid-template-columns:var(--calendar-gutter) repeat(var(--uix-scheduling-calendar-rs-columns,1),minmax(var(--calendar-rs-column),1fr)); grid-template-rows:auto calc(var(--uix-scheduling-calendar-rs-slots,1) * var(--calendar-slot)); min-width:calc(var(--calendar-gutter) + var(--uix-scheduling-calendar-rs-columns,1) * var(--calendar-rs-column)); }
327
+ .uix-scheduling-calendar__rs-corner,.uix-scheduling-calendar__rs-head { position:sticky; top:0; z-index:var(--uix-z-raised); padding:var(--uix-space-2); border-bottom:1px solid var(--calendar-chrome-line); background:var(--calendar-chrome-surface-quiet); }
328
+ .uix-scheduling-calendar__rs-corner { left:0; z-index:var(--uix-z-sticky); display:flex; align-items:flex-end; padding-inline:var(--uix-space-1); color:var(--calendar-chrome-text-quiet); font-size:var(--uix-text-eyebrow); overflow-wrap:anywhere; }
329
+ .uix-scheduling-calendar__rs-head { display:grid; align-content:end; gap:var(--calendar-gap); min-width:0; border-left:1px solid var(--calendar-chrome-line); }
330
+ .uix-scheduling-calendar__rs-name { font-weight:600; overflow-wrap:anywhere; }
331
+ .uix-scheduling-calendar__rs-description { display:block; color:var(--calendar-chrome-text-quiet); font-size:var(--uix-text-meta); font-weight:400; overflow-wrap:anywhere; }
332
+ .uix-scheduling-calendar__rs-gutter { position:sticky; left:0; z-index:var(--uix-z-raised); background:var(--calendar-chrome-surface); }
333
+ .uix-scheduling-calendar__rs-tick { position:absolute; top:calc(var(--uix-scheduling-calendar-rs-offset,0) * var(--calendar-slot)); right:var(--uix-space-1); padding-top:var(--calendar-gap); color:var(--calendar-chrome-text-quiet); font-size:var(--uix-text-meta); font-variant-numeric:tabular-nums; line-height:1; }
334
+ /* The column's own background is "no schedule" (hatched); a free span covers it with paper.
335
+ Lines mark the hours only, drawn over the paper; the slots are felt on hover, not drawn
336
+ (review of PR #166: per-slot boxes with a label each read as a form, not a day). A booking
337
+ is the time grid's own entry chip, at the time grid's own lanes. */
338
+ /* One hour line per hour, as a tile one hour tall: the column and every free slot draw it,
339
+ each shifted by its own offset, so the lines run unbroken over paper and hatch alike. The
340
+ column is its own stacking context: what is raised inside it never rises over the sticky
341
+ heads and gutter. */
342
+ .uix-scheduling-calendar__rs-column { --calendar-hour:var(--calendar-slot); --calendar-rs-hour:calc(var(--uix-scheduling-calendar-rs-per-hour,1) * var(--calendar-slot));
343
+ position:relative; isolation:isolate; min-width:0; border-left:1px solid var(--calendar-chrome-line);
344
+ background:linear-gradient(to bottom,var(--calendar-chrome-line) 0 1px,transparent 1px) 0 calc(var(--uix-scheduling-calendar-rs-hour-at,0) * var(--calendar-slot)) / 100% var(--calendar-rs-hour) repeat-y,repeating-linear-gradient(135deg,var(--calendar-chrome-line) 0 1px,transparent 1px 8px),var(--calendar-chrome-surface-quiet); }
345
+ /* until the first spans arrive nothing is known: no hatch that would say "no schedule" */
346
+ .uix-scheduling-calendar__resources[data-first-load] .uix-scheduling-calendar__rs-column { background:var(--calendar-chrome-surface); }
347
+ .uix-scheduling-calendar__rs-skeleton { position:absolute; top:calc(var(--uix-scheduling-calendar-rs-offset,0) * var(--calendar-slot) + 1px); left:var(--calendar-gap); right:var(--calendar-gap); height:calc(var(--uix-scheduling-calendar-rs-length,1) * var(--calendar-slot) - 2px); }
348
+ /* a free slot: exactly its slot, so a free span tiles into one sheet of paper */
349
+ .uix-scheduling-calendar__rs-item { --calendar-rs-paper:var(--calendar-chrome-surface); position:absolute; top:calc(var(--uix-scheduling-calendar-rs-offset,0) * var(--calendar-slot)); left:0; right:0; height:calc(var(--uix-scheduling-calendar-rs-length,1) * var(--calendar-slot)); display:flex; align-items:flex-start; min-width:0; padding:var(--calendar-gap) var(--uix-space-2); overflow:hidden; border:0; border-radius:0;
350
+ background:linear-gradient(to bottom,var(--calendar-chrome-line) 0 1px,transparent 1px) 0 calc((var(--uix-scheduling-calendar-rs-hour-at,0) - var(--uix-scheduling-calendar-rs-offset,0)) * var(--calendar-slot)) / 100% var(--calendar-rs-hour) repeat-y,var(--calendar-rs-paper); color:var(--calendar-chrome-text); font:inherit; font-size:var(--uix-text-meta); font-variant-numeric:tabular-nums; line-height:1.35; text-align:left; cursor:pointer; }
351
+ .uix-scheduling-calendar__rs-add { display:inline-flex; align-items:center; gap:var(--calendar-gap); visibility:hidden; white-space:nowrap; }
352
+ .uix-scheduling-calendar__rs-add > svg { flex:none; width:1em; height:1em; }
353
+ .uix-scheduling-calendar__rs-item:focus-visible,.uix-scheduling-calendar__rs-item[data-selected] { z-index:var(--uix-z-raised); --calendar-rs-paper:var(--calendar-chrome-ghost-surface); }
354
+ .uix-scheduling-calendar__rs-item:is(:focus-visible,[data-selected]) > .uix-scheduling-calendar__rs-add { visibility:visible; }
355
+ .uix-scheduling-calendar__rs-item:focus-visible { outline:2px solid var(--calendar-chrome-text); outline-offset:-2px; }
356
+ .uix-scheduling-calendar__rs-item[data-selected] { box-shadow:inset 0 0 0 2px var(--calendar-chrome-text); }
357
+ /* time off: quiet words on a quiet sheet, over the paper and under nothing interactive */
358
+ .uix-scheduling-calendar__rs-off { position:absolute; top:calc(var(--uix-scheduling-calendar-offset,0) * var(--calendar-slot)); left:0; right:0; height:calc(var(--uix-scheduling-calendar-length,1) * var(--calendar-slot)); padding:var(--calendar-gap) var(--uix-space-2); overflow:hidden; border-top:1px solid var(--calendar-chrome-line); background:var(--calendar-chrome-surface-quiet); color:var(--calendar-chrome-text-quiet); font-size:var(--uix-text-meta); font-style:italic; line-height:1.35; }
359
+ .uix-scheduling-calendar__rs-text { display:block; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
360
+ /* a booking: the entry chip (its plain edge), heavy only when it is the selected one */
361
+ .uix-scheduling-calendar__rs-booking { top:calc(var(--uix-scheduling-calendar-offset,0) * var(--calendar-slot) + 1px); height:max(calc(var(--uix-scheduling-calendar-length,1) * var(--calendar-slot) - 2px),0.875rem); padding:var(--calendar-gap) var(--uix-space-1); font:inherit; font-size:var(--uix-text-meta); }
362
+ .uix-scheduling-calendar__rs-booking[data-continues-before] { border-top-style:dashed; border-top-left-radius:0; border-top-right-radius:0; }
363
+ .uix-scheduling-calendar__rs-booking[data-continues-after] { border-bottom-style:dashed; border-bottom-left-radius:0; border-bottom-right-radius:0; }
364
+ .uix-scheduling-calendar__rs-booking[data-selected] { border-width:2px; border-color:var(--calendar-chrome-text); }
365
+ .uix-scheduling-calendar__rs-booking[data-selected] .uix-scheduling-calendar__title { font-weight:600; }
366
+ .uix-scheduling-calendar__rs-booking:focus-visible { z-index:var(--uix-z-raised); outline:2px solid var(--calendar-chrome-text); outline-offset:1px; }
367
+ .uix-scheduling-calendar__rs-row:focus-visible { outline:2px solid var(--calendar-chrome-text); outline-offset:1px; z-index:var(--uix-z-raised); }
368
+ .uix-scheduling-calendar__rs-row[data-selected] { box-shadow:inset 0 0 0 2px var(--calendar-chrome-text); }
369
+ @media (hover:hover) {
370
+ .uix-scheduling-calendar__rs-item:hover { --calendar-rs-paper:var(--calendar-chrome-ghost-surface); }
371
+ .uix-scheduling-calendar__rs-item:hover > .uix-scheduling-calendar__rs-add { visibility:visible; }
372
+ .uix-scheduling-calendar__rs-booking:hover { background:var(--calendar-chrome-surface-quiet); }
373
+ button.uix-scheduling-calendar__rs-row:hover { background:var(--calendar-chrome-ghost-surface); }
374
+ }
375
+ /* a finger needs a taller slot than a pointer (WCAG 2.5.8) */
376
+ @media (pointer:coarse) { .uix-scheduling-calendar__resources { --calendar-slot:2.75rem; } }
377
+ .uix-scheduling-calendar__rs-skeleton { display:block; }
378
+
379
+ /* the list: one resource after another, each time range on its own row */
380
+ .uix-scheduling-calendar__resources[data-layout="list"] { display:grid; gap:var(--uix-space-4); }
381
+ .uix-scheduling-calendar__rs-section { display:grid; gap:var(--uix-space-2); min-width:0; }
382
+ .uix-scheduling-calendar__rs-heading { margin:0; font-size:inherit; font-weight:600; overflow-wrap:anywhere; }
383
+ .uix-scheduling-calendar__rs-list { display:grid; gap:var(--calendar-gap); margin:0; padding:0; list-style:none; }
384
+ .uix-scheduling-calendar__rs-list > li { min-width:0; }
385
+ .uix-scheduling-calendar__rs-row { display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--uix-space-1) var(--uix-space-3); width:100%; min-height:2.75rem; padding:var(--uix-space-2) var(--uix-space-3); border:1px solid var(--calendar-chrome-line); border-radius:var(--uix-radius-xs); background:var(--calendar-chrome-surface); color:var(--calendar-chrome-text); font:inherit; text-align:left; }
386
+ button.uix-scheduling-calendar__rs-row { cursor:pointer; }
387
+ .uix-scheduling-calendar__rs-row[data-kind="free"] { border-left:3px solid var(--calendar-chrome-line-strong); }
388
+ .uix-scheduling-calendar__rs-row[data-kind="booked"] { border-color:var(--calendar-chrome-text); border-left-width:3px; background:var(--calendar-chrome-surface-quiet); }
389
+ .uix-scheduling-calendar__rs-row[data-kind="off"] { background:var(--calendar-chrome-surface-quiet); color:var(--calendar-chrome-text-quiet); font-style:italic; }
390
+ .uix-scheduling-calendar__rs-row[data-kind="none"] { background:repeating-linear-gradient(135deg,var(--calendar-chrome-line) 0 1px,transparent 1px 8px),var(--calendar-chrome-surface-quiet); color:var(--calendar-chrome-text-quiet); }
391
+ .uix-scheduling-calendar__rs-range { font-variant-numeric:tabular-nums; font-weight:600; white-space:nowrap; }
392
+ .uix-scheduling-calendar__rs-kind { color:var(--calendar-chrome-text-quiet); }
393
+ .uix-scheduling-calendar__rs-label { min-width:0; overflow-wrap:anywhere; }
394
+ .uix-scheduling-calendar__rs-section .uix-scheduling-calendar__rs-skeleton { position:static; height:6rem; }
395
+ .uix-scheduling-calendar__resources > .uix-scheduling-calendar__empty { position:static; border:0; }
396
+
316
397
  @media print {
317
398
  .uix-scheduling-calendar {
318
399
  --calendar-chrome-text: var(--uix-print-ink);
@@ -23,6 +23,8 @@
23
23
  .uix-search-suggest__clear:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
24
24
 
25
25
  .uix-search-suggest--lg .uix-search-suggest__input { height: calc(var(--uix-control-h) + 8px); padding-left: 44px; font-size: var(--uix-text-h3); }
26
+ /* free text (HAR-2465): a value field, not a search, so no magnifier and no room for one */
27
+ .uix-search-suggest--free-text .uix-search-suggest__input { padding-left: var(--uix-space-3); }
26
28
  .uix-search-suggest--lg .uix-search-suggest__lead { left: 14px; }
27
29
  .uix-search-suggest--lg .uix-search-suggest__lead svg { width: var(--uix-icon-lg); height: var(--uix-icon-lg); }
28
30
 
@@ -16,6 +16,15 @@
16
16
  }
17
17
  .uix-segmented__option:hover { color: var(--uix-text); }
18
18
  /* aria-checked: a `selection="radio"` group (HAR-1604) */
19
- .uix-segmented__option:is([aria-pressed="true"], [aria-checked="true"]) { background: var(--uix-surface); color: var(--uix-text); box-shadow: var(--uix-shadow-sm); }
19
+ /* selection="link" (HAR-1380): a <nav> around ul.uix-segmented of links; the current page lifts. */
20
+ ul.uix-segmented { margin: 0; list-style: none; }
21
+ ul.uix-segmented > li { display: flex; min-inline-size: 0; margin: 0; }
22
+ a.uix-segmented__option { display: inline-flex; align-items: center; text-decoration: none; }
23
+ a.uix-segmented__option[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
24
+ /* the selected segment is lifted AND ringed: the surface on the track is only 1.05:1 in dark and
25
+ 1.09:1 in light, so an inset --uix-text-muted hairline (4.9:1 light, 7:1 dark on the track) makes
26
+ it a 3:1 boundary (WCAG 1.4.11). No weight change: a bolder label would widen the option and
27
+ shift its neighbours on every selection. */
28
+ .uix-segmented__option:is([aria-pressed="true"], [aria-checked="true"], [aria-current="page"]) { background: var(--uix-surface); color: var(--uix-text); box-shadow: var(--uix-shadow-sm), inset 0 0 0 1px var(--uix-text-muted); }
20
29
  .uix-segmented__option:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
21
30
  }
@@ -14,6 +14,8 @@
14
14
  .uix-select:focus { outline: none; border-color: var(--uix-ring); box-shadow: 0 0 0 3px var(--uix-brand-muted); }
15
15
  .uix-select:disabled { opacity: .55; cursor: not-allowed; background-color: var(--uix-bg-subtle); }
16
16
  .uix-select--sm { height: 28px; font-size: var(--uix-text-meta); }
17
+ /* lg: 44 px, beside Input and Button lg on a public page (HAR-1812) */
18
+ .uix-select--lg { height: 44px; padding: 0 calc(var(--uix-space-8) + var(--uix-space-1)) 0 var(--uix-space-4); background-position: right 14px center; font-size: var(--uix-text-h3); }
17
19
 
18
20
  /* command-palette-style rich select: a trigger that opens a .uix-cmdk popover (search + options) */
19
21
  .uix-select-trigger { display: inline-flex; align-items: center; gap: var(--uix-space-2); height: var(--uix-control-h); padding: 0 var(--uix-space-3); min-width: 220px;
@@ -29,6 +31,15 @@
29
31
  .uix-select[aria-invalid="true"] { border-color: var(--uix-danger); }
30
32
  .uix-select[aria-invalid="true"]:focus, .uix-select[aria-invalid="true"][aria-expanded="true"] { box-shadow: 0 0 0 3px var(--uix-danger-bg); }
31
33
  .uix-select[aria-readonly="true"] { cursor: default; background-color: var(--uix-bg-subtle); background-image: none; padding-right: var(--uix-space-3); }
34
+ /* the trigger's chevron is the icon set's chevron-down through a mask, in the muted text colour of
35
+ the theme (HAR-1576). A native <select> cannot hold a pseudo-element: it keeps the image above,
36
+ a mid grey that clears 3:1 on the light and the dark surface. */
37
+ button.uix-select { position: relative; background-image: none; }
38
+ button.uix-select::after {
39
+ content: ""; position: absolute; inset-inline-end: var(--uix-space-3); top: 50%; width: var(--uix-icon-sm); height: var(--uix-icon-sm); translate: 0 -50%;
40
+ background: var(--uix-text-muted); pointer-events: none; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
41
+ }
42
+ button.uix-select[aria-readonly="true"]::after { content: none; }
32
43
  .uix-select__value { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: var(--uix-space-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
33
44
  .uix-select__text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
34
45
  .uix-select__icon { display: inline-flex; flex: none; color: var(--uix-text-muted); }
@@ -35,7 +35,8 @@
35
35
  .uix-navitem__badge { font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
36
36
  /* two-line row: a label plus a wrapping second line (HAR-1355). min-height above lets it grow. */
37
37
  .uix-navitem[data-multiline] { align-items: flex-start; padding-block: var(--uix-space-2); }
38
- .uix-navitem__text { flex: 1; min-width: 0; display: grid; }
38
+ /* label over description, --uix-space-1 apart: the kit's one title -> secondary rhythm (HAR-1577) */
39
+ .uix-navitem__text { flex: 1; min-width: 0; display: grid; gap: var(--uix-space-1); }
39
40
  .uix-navitem__desc { font-size: var(--uix-text-meta); line-height: 1.35; color: var(--uix-text-muted); overflow-wrap: anywhere; white-space: normal; font-weight: 400; }
40
41
  .uix-navitem[aria-current="page"] .uix-navitem__desc { color: var(--uix-text-hushed); }
41
42
  /* on the active tint, muted badge dips below AA — hushed composites darker on the tint */
@@ -142,3 +143,52 @@
142
143
  .uix-sidebar[data-nav="rail"] .uix-navitem { justify-content: center; align-items: center; padding: 0; gap: 0; }
143
144
  .uix-sidebar[data-nav="rail"] .uix-sidebar__head { justify-content: center; padding-inline: 0; }
144
145
  }
146
+
147
+ /* Head actions, the search row, the foot and AccountMenu (HAR-1371). Hover tints only where the
148
+ device can hover; nothing here moves, so a rail switch is instant. */
149
+ @layer uix.components {
150
+ .uix-sidebar__head .uix-sidebar__brand { flex: 1 1 auto; }
151
+ .uix-sidebar__actions { display: flex; align-items: center; gap: var(--uix-space-1); flex: none; }
152
+
153
+ .uix-sidebar__search {
154
+ display: flex; align-items: center; gap: var(--uix-space-2); width: 100%; min-height: 32px; margin-block-end: var(--uix-space-1);
155
+ padding: 0 var(--uix-space-2) 0 var(--uix-space-3); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md);
156
+ background: var(--uix-surface); color: var(--uix-text-muted); font: inherit; font-size: var(--uix-text-body); text-align: start; cursor: pointer;
157
+ }
158
+ .uix-sidebar__search:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: -2px; }
159
+ .uix-sidebar__search-icon { width: var(--uix-icon-sm); height: var(--uix-icon-sm); flex: none; }
160
+ .uix-sidebar__search-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
161
+ .uix-sidebar__search-keys { flex: none; }
162
+
163
+ .uix-sidebar__foot { margin-block-start: auto; padding-block-start: var(--uix-space-2); border-block-start: 1px solid var(--uix-border); }
164
+
165
+ .uix-account-menu {
166
+ display: flex; align-items: center; gap: var(--uix-space-3); width: 100%; min-height: 44px; padding: var(--uix-space-1) var(--uix-space-2);
167
+ border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text); font: inherit; text-align: start; cursor: pointer;
168
+ }
169
+ .uix-account-menu:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: -2px; }
170
+ .uix-account-menu[aria-expanded="true"] { background: var(--uix-bg-active); }
171
+ .uix-account-menu__avatar { flex: none; }
172
+ .uix-account-menu__text { display: grid; flex: 1 1 auto; gap: var(--uix-space-1); min-width: 0; } /* the kit's one title -> secondary rhythm (HAR-1577) */
173
+ .uix-account-menu__name { font-size: var(--uix-text-body); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
174
+ /* the second line is hushed, not muted: muted dips under 4.5:1 on the hover and open tints */
175
+ .uix-account-menu__sub { font-size: var(--uix-text-meta); color: var(--uix-text-hushed); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
176
+ .uix-account-menu__chevron { width: var(--uix-icon-sm); height: var(--uix-icon-sm); flex: none; color: var(--uix-text-muted); }
177
+
178
+ @media (hover: hover) {
179
+ .uix-sidebar__search:hover { border-color: var(--uix-border-strong); color: var(--uix-text-hushed); }
180
+ .uix-account-menu:hover { background: var(--uix-bg-hover); }
181
+ }
182
+
183
+ /* the rail: the search row and the account trigger become centred icon buttons; their words
184
+ stay as the accessible name (the clip pattern the nav labels use) */
185
+ :is(.uix-sidebar[data-collapsed], .uix-sidebar[data-nav="rail"]) .uix-sidebar__actions,
186
+ :is(.uix-sidebar[data-collapsed], .uix-sidebar[data-nav="rail"]) .uix-sidebar__search-keys,
187
+ .uix-account-menu--collapsed .uix-account-menu__chevron { display: none; }
188
+ :is(.uix-sidebar[data-collapsed], .uix-sidebar[data-nav="rail"]) .uix-sidebar__search { justify-content: center; min-height: 36px; padding: 0; }
189
+ :is(.uix-sidebar[data-collapsed], .uix-sidebar[data-nav="rail"]) .uix-sidebar__search-label,
190
+ .uix-account-menu--collapsed .uix-account-menu__text {
191
+ position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
192
+ }
193
+ .uix-account-menu--collapsed { justify-content: center; padding-inline: 0; }
194
+ }
@@ -0,0 +1,42 @@
1
+ /* uix sortable list — a list the person reorders (React `SortableList`, HAR-1375).
2
+ A row is grip · content · move up / move down. Depends only on --uix-* tokens. */
3
+ @layer uix.components {
4
+ .uix-sortable { display: grid; gap: var(--uix-space-2); min-width: 0; margin: 0; padding: 0; list-style: none; }
5
+ .uix-sortable__row {
6
+ display: flex; align-items: center; gap: var(--uix-space-2); min-width: 0; margin: 0;
7
+ padding: var(--uix-space-2) var(--uix-space-2) var(--uix-space-2) var(--uix-space-1);
8
+ border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface);
9
+ }
10
+ /* The dragged row lifts and its neighbours make room by reordering, frame by frame: no motion of its own. */
11
+ .uix-sortable__row[data-dragging] { position: relative; z-index: var(--uix-z-raised); border-color: var(--uix-ring); box-shadow: var(--uix-shadow-popover); }
12
+ .uix-sortable[data-dragging] { cursor: grabbing; user-select: none; }
13
+ .uix-sortable__content { flex: 1; min-width: 0; overflow-wrap: break-word; }
14
+ /* 24 × 32: the grip is a pointer target, wide enough without taking the content's room. */
15
+ .uix-sortable__grip {
16
+ display: grid; place-items: center; flex: none; align-self: stretch; width: 24px; min-height: 32px; padding: 0;
17
+ border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text-muted); cursor: grab; touch-action: none;
18
+ }
19
+ .uix-sortable__grip svg { width: var(--uix-icon-sm); height: var(--uix-icon-sm); }
20
+ .uix-sortable__moves { display: inline-flex; flex: none; gap: var(--uix-space-1); }
21
+ .uix-sortable__move {
22
+ display: inline-grid; place-items: center; width: 32px; height: 32px; padding: 0;
23
+ border: 1px solid var(--uix-border); border-radius: var(--uix-radius-sm); background: var(--uix-surface); color: var(--uix-text); cursor: pointer;
24
+ transition: background-color var(--uix-dur-fast) var(--uix-ease-out);
25
+ }
26
+ .uix-sortable__move svg { width: var(--uix-icon-sm); height: var(--uix-icon-sm); }
27
+ .uix-sortable__move:disabled { color: var(--uix-text-muted); opacity: .45; cursor: default; }
28
+ @media (hover: hover) and (pointer: fine) {
29
+ .uix-sortable__move:not(:disabled):hover, .uix-sortable__grip:hover { background: var(--uix-bg-hover); color: var(--uix-text); }
30
+ }
31
+ .uix-sortable__move:not(:disabled):active { background: var(--uix-bg-active); }
32
+ :is(.uix-sortable__grip, .uix-sortable__move):focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
33
+ .uix-sortable__row:focus-within { border-color: var(--uix-border-strong); }
34
+ /* On a phone Move up stacks over Move down, so the content keeps the width its words need (at
35
+ 320 px they broke inside every word). A viewport query, not a container: an inline-size
36
+ container has no width of its own, and the list would collapse in a popover or any
37
+ shrink-to-fit parent (it did in the docs preview). */
38
+ @media (max-width: 30rem) {
39
+ .uix-sortable__moves { flex-direction: column; }
40
+ .uix-sortable__move { width: 32px; height: 28px; }
41
+ }
42
+ }
@@ -12,3 +12,8 @@
12
12
  background: color-mix(in srgb, var(--uix-bg-app) 60%, transparent); backdrop-filter: blur(2px);
13
13
  }
14
14
  }
15
+
16
+ /* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same pulse */
17
+ @layer uix.components {
18
+ @media (prefers-reduced-motion: no-preference) { :root[data-motion="reduce"] .uix-spinner { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
19
+ }
@@ -2,21 +2,30 @@
2
2
  @layer uix.components {
3
3
  .uix-stat { background: var(--uix-surface); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-lg); padding: var(--uix-space-6); }
4
4
  .uix-stat__label { font-size: var(--uix-text-eyebrow); letter-spacing: var(--uix-tracking-eyebrow); text-transform: uppercase; color: var(--uix-text-muted); display: flex; align-items: center; justify-content: space-between; gap: var(--uix-space-2); }
5
- .uix-stat__label > span { min-width: 0; overflow-wrap: anywhere; }
5
+ /* break-word, not anywhere: a word wraps whole and only breaks when it alone is wider than the tile */
6
+ .uix-stat__label > span { min-width: 0; overflow-wrap: break-word; }
6
7
  .uix-stat__label svg { width: var(--uix-icon-md); height: var(--uix-icon-md); }
7
8
  .uix-stat__value { display: block; font-size: var(--uix-text-data-hero); font-weight: 600; line-height: var(--uix-leading-data-hero); margin-top: var(--uix-space-3); overflow-wrap: anywhere; }
8
9
  .uix-stat__meta { font-size: var(--uix-text-meta); color: var(--uix-text-muted); margin-top: var(--uix-space-2); display: flex; flex-wrap: wrap; align-items: center; gap: var(--uix-space-2); overflow-wrap: anywhere; }
9
- .uix-stat__trend { display: inline-flex; align-items: center; gap: var(--uix-space-1); font-size: var(--uix-text-meta); font-weight: 600; padding: var(--uix-space-1) var(--uix-space-2); border-radius: var(--uix-radius-pill); }
10
+ /* the chip is neutral unless the product says what the change means (HAR-1563): a rise can be
11
+ bad (breached SLAs) and a fall good (open incidents), so direction never picks the colour */
12
+ .uix-stat__trend { display: inline-flex; align-items: center; gap: var(--uix-space-1); font-size: var(--uix-text-meta); font-weight: 600; padding: var(--uix-space-1) var(--uix-space-2); border-radius: var(--uix-radius-pill); color: var(--uix-neutral-text); background: var(--uix-neutral-bg); }
10
13
  /* direction glyph inside the chip (UIX-A11Y-4) — sized to sit on the text baseline row */
11
14
  .uix-stat__trend svg { width: 10px; height: 10px; flex: none; }
12
- .uix-stat__trend--up { color: var(--uix-success-text); background: var(--uix-success-bg); }
13
- .uix-stat__trend--down { color: var(--uix-danger-text); background: var(--uix-danger-bg); }
15
+ /* the direction classes name the arrow, not a colour; their figures are tabular so deltas line up across tiles */
16
+ .uix-stat__trend:is(.uix-stat__trend--up, .uix-stat__trend--down) { font-variant-numeric: tabular-nums; }
17
+ .uix-stat__trend--positive { color: var(--uix-success-text); background: var(--uix-success-bg); }
18
+ .uix-stat__trend--negative { color: var(--uix-danger-text); background: var(--uix-danger-bg); }
14
19
 
15
- /* tones — an outlined tile whose value carries the tone; the label keeps its muted colour */
16
- .uix-stat--warning { border-color: var(--uix-warning-border); }
17
- .uix-stat--warning .uix-stat__value { color: var(--uix-warning-text); }
18
- .uix-stat--danger { border-color: var(--uix-danger-border); }
19
- .uix-stat--danger .uix-stat__value { color: var(--uix-danger-text); }
20
+ /* tones (HAR-1563) — the value carries the tone and a dot of its solid leads it; the border stays
21
+ neutral and the label keeps its muted colour, so a row of tiles has one quiet frame */
22
+ .uix-stat--warning { --stat-tone-text: var(--uix-warning-text); --stat-tone-solid: var(--uix-warning-solid); }
23
+ .uix-stat--danger { --stat-tone-text: var(--uix-danger-text); --stat-tone-solid: var(--uix-danger-solid); }
24
+ .uix-stat:is(.uix-stat--warning, .uix-stat--danger) .uix-stat__value { color: var(--stat-tone-text); }
25
+ .uix-stat:is(.uix-stat--warning, .uix-stat--danger) .uix-stat__value::before {
26
+ content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 50%;
27
+ margin-inline-end: var(--uix-space-2); vertical-align: middle; background: var(--stat-tone-solid);
28
+ }
20
29
 
21
30
  /* compact — for dense fact bands */
22
31
  .uix-stat--compact { padding: var(--uix-space-3) var(--uix-space-4); }
@@ -29,9 +38,7 @@
29
38
  display: flex; flex-direction: column; width: 100%; font: inherit; color: inherit; text-align: start; cursor: pointer;
30
39
  transition: background-color var(--uix-dur-fast) var(--uix-ease-out), border-color var(--uix-dur-fast) var(--uix-ease-out);
31
40
  }
32
- .uix-stat--interactive:hover:where(:not([aria-busy="true"])) { background: var(--uix-bg-hover); border-color: var(--uix-border-strong); }
33
- .uix-stat--interactive.uix-stat--warning:hover { border-color: var(--uix-warning-border); }
34
- .uix-stat--interactive.uix-stat--danger:hover { border-color: var(--uix-danger-border); }
41
+ @media (hover: hover) { .uix-stat--interactive:hover:where(:not([aria-busy="true"])) { background: var(--uix-bg-hover); border-color: var(--uix-border-strong); } }
35
42
  .uix-stat--interactive:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
36
43
  .uix-stat--interactive[aria-expanded="true"] { border-color: var(--uix-ring); }
37
44
  .uix-stat__chevron { display: inline-flex; flex: none; margin-inline-start: auto; color: var(--uix-text-muted); }
@@ -41,7 +48,24 @@
41
48
  .uix-stat--interactive[aria-busy="true"] { cursor: progress; opacity: .6; }
42
49
  /* link — the whole tile is an <a> to a filtered list or a record (HAR-1350) */
43
50
  .uix-stat--link { text-decoration: none; }
51
+ /* resting affordance (HAR-1803): a tile that goes somewhere looks it before it is touched, since
52
+ a tablet has no hover. A link carries a trailing chevron in its label row; an in-place action
53
+ (no popup, so no chevron) underlines its value. Both are shapes, never colour alone. */
54
+ .uix-stat__go { display: inline-flex; flex: none; margin-inline-start: auto; color: var(--uix-text-muted); }
55
+ .uix-stat__label .uix-stat__go svg { width: var(--uix-icon-sm); height: var(--uix-icon-sm); }
56
+ .uix-stat--action .uix-stat__value { text-decoration: underline dotted var(--uix-text-muted); text-decoration-thickness: from-font; text-underline-offset: .2em; }
44
57
  /* current: the ring outline only; a brand tint would drop the muted label below AA */
45
58
  .uix-stat--link[aria-current="page"] { border-color: var(--uix-ring); box-shadow: inset 0 0 0 1px var(--uix-ring); }
46
59
  @media (prefers-reduced-motion: reduce) { .uix-stat--interactive, .uix-stat__label .uix-stat__chevron svg { transition: none; } }
47
60
  }
61
+
62
+ /* Stat row (HAR-1803 review): tiles side by side while each keeps a readable width, then fewer per
63
+ row, down to one on a phone. A fixed six-column grid broke labels letter by letter at 320 px
64
+ ("AS/SI/GN/EE"). --stat-row-min sets the narrowest tile: 8.5rem, and 12rem with --hero (or once
65
+ a hero tile, with its 34 px value, is in the row); min(100%, …) keeps a single tile inside a
66
+ container narrower than that. */
67
+ @layer uix.components {
68
+ .uix-stat-row { --stat-row-auto: 8.5rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--stat-row-min, var(--stat-row-auto))), 1fr)); gap: var(--uix-space-3); min-width: 0; }
69
+ .uix-stat-row--hero, .uix-stat-row:has(> .uix-stat:not(.uix-stat--compact)) { --stat-row-auto: 12rem; }
70
+ }
71
+
@@ -22,3 +22,8 @@
22
22
  .uix-operation__description { color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
23
23
  .uix-operation__action { justify-self: start; }
24
24
  }
25
+
26
+ /* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same pulse */
27
+ @layer uix.components {
28
+ @media (prefers-reduced-motion: no-preference) { :root[data-motion="reduce"] .uix-skeleton { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
29
+ }