@tensor_1/tokens 2.47.0 → 2.49.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 (77) hide show
  1. package/build/css/components.css +1 -1
  2. package/build/css/styles.css +1 -1
  3. package/build/css/tokens.css +4 -0
  4. package/build/ts/tokens.d.ts +1 -1
  5. package/build/ts/tokens.js +8 -0
  6. package/package.json +1 -1
  7. package/styles/components/alert.css +6 -4
  8. package/styles/components/app-shell.css +40 -0
  9. package/styles/components/arrival.css +7 -1
  10. package/styles/components/attachment.css +4 -1
  11. package/styles/components/audit-log.css +29 -0
  12. package/styles/components/breadcrumbs.css +2 -0
  13. package/styles/components/busy-region.css +10 -2
  14. package/styles/components/button.css +7 -0
  15. package/styles/components/card.css +22 -0
  16. package/styles/components/change-highlight.css +58 -0
  17. package/styles/components/checkbox.css +18 -0
  18. package/styles/components/code-display.css +43 -0
  19. package/styles/components/combobox.css +4 -1
  20. package/styles/components/command-palette.css +16 -2
  21. package/styles/components/contact-card.css +17 -6
  22. package/styles/components/count.css +23 -0
  23. package/styles/components/data-workspace.css +40 -0
  24. package/styles/components/date-picker.css +14 -1
  25. package/styles/components/detail-layout.css +9 -0
  26. package/styles/components/diff-viewer.css +5 -0
  27. package/styles/components/display-preferences.css +38 -0
  28. package/styles/components/drag.css +63 -0
  29. package/styles/components/drawer.css +6 -5
  30. package/styles/components/editorial-home.css +24 -2
  31. package/styles/components/flow.css +5 -0
  32. package/styles/components/heartbeat.css +8 -0
  33. package/styles/components/icon.css +14 -0
  34. package/styles/components/inline-edit.css +19 -3
  35. package/styles/components/input.css +62 -0
  36. package/styles/components/kanban.css +10 -1
  37. package/styles/components/kbd.css +25 -0
  38. package/styles/components/lightbox.css +13 -0
  39. package/styles/components/list-motion.css +27 -0
  40. package/styles/components/list.css +2 -0
  41. package/styles/components/match-review.css +40 -0
  42. package/styles/components/media.css +4 -2
  43. package/styles/components/metric-input.css +3 -1
  44. package/styles/components/modal.css +10 -5
  45. package/styles/components/page-header.css +12 -0
  46. package/styles/components/pagination.css +2 -0
  47. package/styles/components/peek.css +6 -5
  48. package/styles/components/popover.css +14 -10
  49. package/styles/components/print-layout.css +177 -0
  50. package/styles/components/progress.css +7 -0
  51. package/styles/components/qr-code.css +30 -0
  52. package/styles/components/qr-scanner.css +61 -0
  53. package/styles/components/relationship-graph.css +71 -15
  54. package/styles/components/rule-builder.css +2 -1
  55. package/styles/components/save-status.css +5 -0
  56. package/styles/components/scheduling-calendar.css +82 -1
  57. package/styles/components/search-suggest.css +8 -6
  58. package/styles/components/segmented.css +31 -1
  59. package/styles/components/select.css +20 -2
  60. package/styles/components/sidebar.css +51 -1
  61. package/styles/components/sortable-list.css +42 -0
  62. package/styles/components/spinner.css +5 -0
  63. package/styles/components/stat-tile.css +36 -12
  64. package/styles/components/states.css +50 -0
  65. package/styles/components/status-pill.css +28 -6
  66. package/styles/components/steps.css +2 -0
  67. package/styles/components/table-toolbar.css +1 -1
  68. package/styles/components/table.css +48 -28
  69. package/styles/components/tabs.css +32 -3
  70. package/styles/components/tag-input.css +21 -0
  71. package/styles/components/textarea.css +3 -0
  72. package/styles/components/toast.css +23 -8
  73. package/styles/components/tooltip.css +7 -3
  74. package/styles/components/tree.css +3 -1
  75. package/styles/components/view-menu.css +3 -0
  76. package/styles/motion.css +23 -4
  77. package/tokens/base/effect.json +6 -0
@@ -8,6 +8,11 @@
8
8
  .uix-diff-viewer__versions { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); background:var(--uix-border); }
9
9
  .uix-diff-viewer__versions>div { min-width:0; padding:var(--uix-space-3); border-right:1px solid var(--uix-border); background:var(--uix-surface); } .uix-diff-viewer__versions>div:last-child { border-right:0; } .uix-diff-viewer__versions strong { color:var(--uix-text-muted); font-size:var(--uix-text-meta); }
10
10
  .uix-diff-viewer__versions pre { max-height:14rem; overflow:auto; margin-top:var(--uix-space-2); color:var(--uix-text); font-family:var(--uix-font-mono); white-space:pre-wrap; overflow-wrap:anywhere; }
11
+ /* HAR-1369: an entry named for people (labelFor), values shown by the product (renderValue), and
12
+ where each version came from (provenance), quieter, under its value */
13
+ .uix-diff-viewer__name { min-width:0; overflow-wrap:anywhere; }
14
+ .uix-diff-viewer__value { margin-top:var(--uix-space-2); color:var(--uix-text); overflow-wrap:anywhere; }
15
+ .uix-diff-viewer__provenance { margin-top:var(--uix-space-2); color:var(--uix-text-muted); font-size:var(--uix-text-meta); overflow-wrap:anywhere; }
11
16
  /* controlSize: entry rows are controls too; md follows --uix-control-h */
12
17
  .uix-diff-viewer[data-control-size="md"] .uix-diff-viewer__entry summary { min-height:var(--uix-control-h); }
13
18
  .uix-diff-viewer[data-control-size="lg"] .uix-diff-viewer__entry summary { min-height:44px; }
@@ -0,0 +1,38 @@
1
+ /* uix display preferences (HAR-1372) — the attributes useDisplayPreferences, DisplayPreferences and
2
+ displayPreferencesScript write on <html>, as re-pointed existing tokens (no new --uix-* names):
3
+ data-contrast="more" stronger secondary text and borders (3:1 hairlines), both themes
4
+ data-text-scale="sm|lg|xl" the rem scale every type token is in (md writes nothing)
5
+ data-density="compact|comfortable" table rows and controls
6
+ data-motion="reduce" the reduced-motion policy (motion.css), without the OS setting
7
+ data-theme="light|dark" is the kit's existing theme switch. Depends only on --uix-* tokens. */
8
+ @layer uix.tokens {
9
+ /* mixes of the theme's own text and surface, so one rule serves light and dark */
10
+ :root[data-contrast="more"] {
11
+ --uix-text-hushed: color-mix(in srgb, var(--uix-text) 86%, transparent);
12
+ --uix-text-muted: color-mix(in srgb, var(--uix-text) 74%, var(--uix-surface));
13
+ --uix-border: color-mix(in srgb, var(--uix-text) 45%, var(--uix-surface));
14
+ --uix-border-strong: color-mix(in srgb, var(--uix-text) 60%, var(--uix-surface));
15
+ }
16
+
17
+ /* every --uix-text-* size is in rem, so the root size scales the whole type ramp */
18
+ :root[data-text-scale="sm"] { font-size: 93.75%; }
19
+ :root[data-text-scale="lg"] { font-size: 112.5%; }
20
+ :root[data-text-scale="xl"] { font-size: 125%; }
21
+
22
+ :root[data-density="compact"] { --uix-row-h: var(--uix-row-h-compact); --uix-control-h: var(--uix-space-7); }
23
+ :root[data-density="comfortable"] { --uix-row-h: var(--uix-row-h-comfortable); --uix-control-h: var(--uix-space-8); }
24
+ }
25
+
26
+ @layer uix.components {
27
+ /* a switch of theme, contrast, size or density changes tokens under every element: no transition
28
+ runs for that frame, so nothing fades between themes (the hook sets this for two frames) */
29
+ :root[data-uix-switching] *, :root[data-uix-switching] *::before, :root[data-uix-switching] *::after { transition: none !important; }
30
+
31
+ .uix-display-prefs { display: grid; gap: var(--uix-space-4); min-width: 0; }
32
+ .uix-display-prefs__row { display: grid; gap: var(--uix-space-2); min-width: 0; }
33
+ .uix-display-prefs__label { font-size: var(--uix-text-meta); font-weight: 600; color: var(--uix-text-hushed); }
34
+ /* the groups keep their own width in a stretching grid or flex column; they wrap at 320 px */
35
+ :is(.uix-display-prefs .uix-segmented, .uix-theme-switcher) { justify-self: start; align-self: flex-start; max-width: 100%; }
36
+ :is(.uix-theme-switcher, .uix-display-prefs) .uix-segmented__option { display: inline-flex; align-items: center; gap: var(--uix-space-1); }
37
+ :is(.uix-theme-switcher, .uix-display-prefs) .uix-segmented__option > .uix-icon { flex: none; }
38
+ }
@@ -0,0 +1,63 @@
1
+ /* uix drag — the shared affordances of anything a person drags (HAR-2590, motion standard §2.8):
2
+ a list row, a kanban card, a dashboard widget. Attributes, so any element can carry them:
3
+ [data-uix-drag="lifted"] picked up by the pointer: raised, a little larger, a deeper shadow
4
+ [data-uix-drag="grabbed"] picked up from the keyboard (Space): a dashed ring, no motion
5
+ [data-uix-drop-target] the zone the item would land in: tinted and outlined
6
+ .uix-drop-line where it would land between two items: an accent line with ends
7
+ [data-uix-settle] set for a moment on drop: the item lands (settleDrop)
8
+ Lift and settle are transform only; the shadow steps. Reduced motion: no scale, no settle.
9
+ Depends only on --uix-* tokens. */
10
+ @layer uix.components {
11
+ [data-uix-drag="lifted"] {
12
+ position: relative; z-index: var(--uix-z-raised); cursor: grabbing;
13
+ box-shadow: var(--uix-shadow-popover); transform: scale(1.02);
14
+ transition: transform var(--uix-dur-fast) var(--uix-ease-out);
15
+ }
16
+ [data-uix-drag="grabbed"] {
17
+ position: relative; z-index: var(--uix-z-raised);
18
+ box-shadow: var(--uix-shadow-popover); outline: 2px dashed var(--uix-ring); outline-offset: 2px;
19
+ }
20
+
21
+ [data-uix-drop-target] {
22
+ background-color: color-mix(in srgb, var(--uix-accent) 8%, transparent);
23
+ outline: 2px dashed var(--uix-ring); outline-offset: -2px;
24
+ transition: background-color var(--uix-dur-fast) var(--uix-ease-out);
25
+ }
26
+
27
+ /* zero tall where it stands (a flex gap around it opens the room); the line is drawn across it */
28
+ .uix-drop-line { position: relative; display: block; block-size: 0; pointer-events: none; }
29
+ .uix-drop-line::before {
30
+ content: ""; position: absolute; inset-inline: var(--uix-space-1); inset-block-start: calc(var(--uix-space-1) * -.25);
31
+ block-size: calc(var(--uix-space-1) * .5); border-radius: var(--uix-radius-pill); background: var(--uix-accent);
32
+ }
33
+ .uix-drop-line::after {
34
+ content: ""; position: absolute; inset-inline-start: 0; inset-block-start: calc(var(--uix-space-2) * -.5);
35
+ inline-size: var(--uix-space-2); block-size: var(--uix-space-2); border-radius: var(--uix-radius-pill);
36
+ border: 2px solid var(--uix-accent); background: var(--uix-surface);
37
+ }
38
+
39
+ @keyframes uix-drag-settle { from { transform: scale(1.02); } }
40
+ [data-uix-settle] { animation: uix-drag-settle var(--uix-dur) var(--uix-ease-out); }
41
+
42
+ @media (prefers-reduced-motion: reduce) {
43
+ [data-uix-drag="lifted"] { transform: none; }
44
+ [data-uix-settle] { animation: none; }
45
+ }
46
+ @media (forced-colors: active) {
47
+ /* the dashes are what tells a picked-up item from a focused one: keep them */
48
+ [data-uix-drag="lifted"] { outline: 2px solid Highlight; }
49
+ [data-uix-drag="grabbed"] { outline: 2px dashed Highlight; }
50
+ [data-uix-drop-target] { outline-color: Highlight; }
51
+ .uix-drop-line::before, .uix-drop-line::after { forced-color-adjust: none; }
52
+ .uix-drop-line::before { background: Highlight; }
53
+ .uix-drop-line::after { border-color: Highlight; background: Canvas; }
54
+ }
55
+ }
56
+
57
+ /* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same rules */
58
+ @layer uix.components {
59
+ @media (prefers-reduced-motion: no-preference) {
60
+ :root[data-motion="reduce"] [data-uix-drag="lifted"] { transform: none; }
61
+ :root[data-motion="reduce"] [data-uix-settle] { animation: none; }
62
+ }
63
+ }
@@ -5,18 +5,19 @@
5
5
  border: 0; border-left: 1px solid var(--uix-border); border-radius: 0;
6
6
  background: var(--uix-surface); box-shadow: var(--uix-shadow-overlay);
7
7
  transform: translateX(100%);
8
- transition: transform var(--uix-dur-slow) var(--uix-ease-out),
9
- overlay var(--uix-dur-slow) allow-discrete, display var(--uix-dur-slow) allow-discrete;
8
+ /* the closed state's durations are the exit's (--uix-dur-exit-slow); [open] carries the enter */
9
+ transition: transform var(--uix-dur-exit-slow) var(--uix-ease-out),
10
+ overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
10
11
  /* Author display values for both states required for the slide-in (see peek.css). Closed = none. */
11
12
  display: none;
12
13
  }
13
- .uix-drawer[open] { transform: none; display: flex; flex-direction: column; }
14
+ .uix-drawer[open] { transform: none; display: flex; flex-direction: column; transition-duration: var(--uix-dur-slow); }
14
15
  @starting-style { .uix-drawer[open] { transform: translateX(100%); } }
15
16
  .uix-drawer::backdrop {
16
17
  background: var(--uix-scrim); opacity: 0;
17
- transition: opacity var(--uix-dur-slow) var(--uix-ease-out), overlay var(--uix-dur-slow) allow-discrete, display var(--uix-dur-slow) allow-discrete;
18
+ transition: opacity var(--uix-dur-exit-slow) var(--uix-ease-out), overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
18
19
  }
19
- .uix-drawer[open]::backdrop { opacity: 1; }
20
+ .uix-drawer[open]::backdrop { opacity: 1; transition-duration: var(--uix-dur-slow); }
20
21
  @starting-style { .uix-drawer[open]::backdrop { opacity: 0; } }
21
22
  /* side="start" — the phone navigation drawer, from the left edge (HAR-1351) */
22
23
  .uix-drawer--start { margin: 0 auto 0 0; border-left: 0; border-right: 1px solid var(--uix-border); transform: translateX(-100%); }
@@ -166,8 +166,17 @@
166
166
  .uix-event-date { text-align: center; }
167
167
  .uix-event-date__month { display: block; color: var(--uix-text-hushed); font-size: var(--uix-text-eyebrow); text-transform: uppercase; }
168
168
  .uix-event-date__day { display: block; font-size: 18px; font-variant-numeric: tabular-nums; }
169
- .uix-event-row__title { margin: 0; font-weight: 600; }
170
- .uix-event-row__meta { margin: var(--uix-space-1) 0 0; color: var(--uix-text-hushed); font-size: var(--uix-text-meta); }
169
+ .uix-event-row__title { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
170
+ .uix-event-row__meta { margin: var(--uix-space-1) 0 0; color: var(--uix-text-hushed); font-size: var(--uix-text-meta); overflow-wrap: anywhere; }
171
+ /* HAR-1370: weekday, year and time in the tile (it widens to its longest line, never under
172
+ 42 px), and a 4:3 thumbnail at the row's end */
173
+ .uix-event-row:has(.uix-event-date__weekday, .uix-event-date__year, .uix-event-date__time) { grid-template-columns: minmax(42px, max-content) minmax(0, 1fr); }
174
+ .uix-event-row--media, .uix-event-row--media:has(.uix-event-date__weekday, .uix-event-date__year, .uix-event-date__time) { grid-template-columns: minmax(42px, max-content) minmax(0, 1fr) auto; }
175
+ .uix-event-date__weekday, .uix-event-date__year, .uix-event-date__time { display: block; color: var(--uix-text-hushed); font-size: var(--uix-text-eyebrow); font-variant-numeric: tabular-nums; white-space: nowrap; }
176
+ .uix-event-date__weekday { text-transform: uppercase; }
177
+ .uix-event-date__time { margin-top: var(--uix-space-1); color: var(--uix-text); font-weight: 600; }
178
+ .uix-event-row__media { width: 96px; aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--uix-radius-md); background: var(--uix-bg-subtle); }
179
+ .uix-event-row__media > :is(img, picture, video, svg) { display: block; width: 100%; height: 100%; object-fit: cover; }
171
180
 
172
181
  .uix-status-row { min-height: 48px; display: flex; align-items: center; gap: var(--uix-space-3); border-bottom: 1px solid var(--uix-border); }
173
182
  .uix-status-row:last-child { border-bottom: 0; }
@@ -218,5 +227,18 @@
218
227
 
219
228
  .uix-stat-line { grid-template-columns: 1fr; }
220
229
  .uix-stat-line__item { display: flex; align-items: baseline; justify-content: space-between; border-bottom: 1px solid var(--uix-border); }
230
+
231
+ /* on a phone the thumbnail goes under the text, so the title keeps the row's width */
232
+ .uix-event-row--media, .uix-event-row--media:has(.uix-event-date__weekday, .uix-event-date__year, .uix-event-date__time) { grid-template-columns: minmax(42px, max-content) minmax(0, 1fr); }
233
+ .uix-event-row--media > .uix-event-date { grid-row: span 2; align-self: start; }
234
+ .uix-event-row__media { grid-column: 2; width: min(8rem, 100%); }
221
235
  }
222
236
  }
237
+
238
+ /* Compact section title (HAR-1815): the in-card section preset, 15 px / 1.3 / 600, for a section
239
+ head inside a card or a side panel. .uix-section-title--compact for a title on its own. */
240
+ @layer uix.components {
241
+ .uix-section-head--compact { min-height: 0; margin-bottom: var(--uix-space-2); }
242
+ :is(.uix-section-head--compact .uix-section-title, .uix-section-title--compact),
243
+ .uix-section-head--compact .uix-section-head__title-row { font-family: var(--uix-font-sans); font-size: var(--uix-text-h3); line-height: var(--uix-leading-h3); font-weight: 600; letter-spacing: normal; }
244
+ }
@@ -225,3 +225,8 @@
225
225
  .uix-flow--linear .uix-flow__connector { flex-basis: 38px; }
226
226
  }
227
227
  }
228
+
229
+ /* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same pulse */
230
+ @layer uix.components {
231
+ @media (prefers-reduced-motion: no-preference) { :root[data-motion="reduce"] .uix-node--running .uix-node__icon svg { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
232
+ }
@@ -15,3 +15,11 @@
15
15
  }
16
16
  .uix-live { display: inline-flex; align-items: center; gap: var(--uix-space-2); font-size: var(--uix-text-meta); font-weight: 500; color: var(--uix-text-hushed); }
17
17
  }
18
+
19
+ /* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same rule */
20
+ @layer uix.components {
21
+ @media (prefers-reduced-motion: no-preference) {
22
+ :root[data-motion="reduce"] .uix-heartbeat__ping { display: none; }
23
+ :root[data-motion="reduce"] .uix-heartbeat:not(.uix-heartbeat--idle) .uix-heartbeat__dot { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; }
24
+ }
25
+ }
@@ -20,3 +20,17 @@
20
20
  .uix-icon-grid__item { display: grid; justify-items: center; gap: var(--uix-space-2); padding: var(--uix-space-3) var(--uix-space-2); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-sm); background: var(--uix-surface); color: var(--uix-text); }
21
21
  .uix-icon-grid__item code { font-size: var(--uix-text-meta); color: var(--uix-text-muted); overflow-wrap: break-word; text-align: center; }
22
22
  }
23
+
24
+ /* Glyphs for plain CSS (HAR-1576): an icon where markup cannot hold an <svg> (generated content, a
25
+ CMS field), drawn from the icon set through a mask in currentColor, so it takes the text colour and
26
+ the theme. <span class="uix-glyph uix-glyph--x" aria-hidden="true"></span>. Never a text character:
27
+ a typed × or › has the font's shape and baseline, not the icon set's.
28
+ scripts/glyph-mask.mjs prints each url; src/glyph-masks.test.mjs checks them against the glyphs. */
29
+ @layer uix.components {
30
+ .uix-glyph { display: inline-block; flex: none; width: var(--uix-icon-sm); height: var(--uix-icon-sm); vertical-align: middle; background: currentColor; -webkit-mask: var(--glyph) center / contain no-repeat; mask: var(--glyph) center / contain no-repeat; }
31
+ .uix-glyph--chevron-down { --glyph: 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"); }
32
+ .uix-glyph--arrow-up { --glyph: 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='m5 12 7-7 7 7'/%3E%3Cpath d='M12 19V5'/%3E%3C/svg%3E"); }
33
+ .uix-glyph--arrow-down { --glyph: 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='M12 5v14'/%3E%3Cpath d='m19 12-7 7-7-7'/%3E%3C/svg%3E"); }
34
+ .uix-glyph--arrow-up-down { --glyph: 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='m21 16-4 4-4-4'/%3E%3Cpath d='M17 20V4'/%3E%3Cpath d='m3 8 4-4 4 4'/%3E%3Cpath d='M7 4v16'/%3E%3C/svg%3E"); }
35
+ .uix-glyph--x { --glyph: 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='M18 6 6 18'/%3E%3Cpath d='m6 6 12 12'/%3E%3C/svg%3E"); }
36
+ }
@@ -6,14 +6,17 @@
6
6
  /* A value that cannot be edited is plain text on the same line box as its editable neighbours. */
7
7
  .uix-inline-edit--readonly { align-items: center; min-height: 28px; }
8
8
  /* The value IS the button: it looks like text until it is hovered or focused. The negative
9
- inline margin keeps the text on the column it had as plain text. */
9
+ inline margin keeps the text on the column it had as plain text. It sits on the wrapper, not
10
+ the button: an inline-flex box sizes itself to its item's width MINUS the item's negative
11
+ margins, so on the button it squeezed every value by 16 px and wrapped "Amra / Kovač" (HAR-1833). */
12
+ .uix-inline-edit:has(> .uix-inline-edit__view) { margin-inline: calc(-1 * var(--uix-space-2)); max-width: calc(100% + 2 * var(--uix-space-2)); }
10
13
  .uix-inline-edit__view {
11
14
  display: inline-flex; align-items: center; gap: var(--uix-space-2); max-width: 100%; min-width: 0; min-height: 28px;
12
- margin: 0 calc(-1 * var(--uix-space-2)); padding: var(--uix-space-1) var(--uix-space-2);
15
+ margin: 0; padding: var(--uix-space-1) var(--uix-space-2);
13
16
  border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: inherit; font: inherit; text-align: start; cursor: pointer;
14
17
  transition: background-color var(--uix-dur-fast) var(--uix-ease-out);
15
18
  }
16
- .uix-inline-edit__view:hover { background: var(--uix-bg-hover); }
19
+ @media (hover: hover) and (pointer: fine) { .uix-inline-edit__view:hover { background: var(--uix-bg-hover); } }
17
20
  .uix-inline-edit__view:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
18
21
  .uix-inline-edit__value { min-width: 0; overflow-wrap: anywhere; }
19
22
  .uix-inline-edit__placeholder { color: var(--uix-text-muted); }
@@ -26,4 +29,17 @@
26
29
  .uix-inline-edit__control > * { width: 100%; }
27
30
  .uix-inline-edit__actions { display: inline-flex; flex: none; gap: var(--uix-space-2); }
28
31
  .uix-inline-edit__error { flex-basis: 100%; }
32
+ /* trigger="icon" (HAR-1833): the value stays text; a pencil button after it is named only
33
+ "Edit {label}". 28 px, the same row height as the value button. */
34
+ .uix-inline-edit--icon-trigger { align-items: center; gap: var(--uix-space-1); min-height: 28px; }
35
+ .uix-inline-edit__trigger {
36
+ display: inline-grid; place-items: center; flex: none; width: 28px; height: 28px; padding: 0;
37
+ border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text-muted); cursor: pointer;
38
+ transition: background-color var(--uix-dur-fast) var(--uix-ease-out), color var(--uix-dur-fast) var(--uix-ease-out);
39
+ }
40
+ @media (hover: hover) and (pointer: fine) { .uix-inline-edit__trigger:hover { background: var(--uix-bg-hover); color: var(--uix-text); } }
41
+ .uix-inline-edit__trigger:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; color: var(--uix-text); }
42
+ /* readOnlyReason (HAR-1833): why it cannot be edited, quieter, under the value */
43
+ .uix-inline-edit--reason { flex-direction: column; align-items: flex-start; gap: var(--uix-space-1); }
44
+ .uix-inline-edit__reason { font-size: var(--uix-text-meta); color: var(--uix-text-muted); overflow-wrap: anywhere; }
29
45
  }
@@ -30,6 +30,8 @@
30
30
  .uix-input[aria-invalid="true"] { border-color: var(--uix-danger); }
31
31
  .uix-input[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px var(--uix-danger-bg); }
32
32
  .uix-input--sm { height: 28px; font-size: var(--uix-text-meta); }
33
+ /* lg: 44 px, the touch-target floor of a public page, as tall as .uix-btn--lg (HAR-1812) */
34
+ .uix-input--lg { height: 44px; padding: 0 var(--uix-space-4); font-size: var(--uix-text-h3); }
33
35
 
34
36
  /* leading/trailing adornments */
35
37
  .uix-input-group { position: relative; display: flex; align-items: center; }
@@ -45,4 +47,64 @@
45
47
  .uix-input[data-warning]:not([aria-invalid="true"]):focus { box-shadow: 0 0 0 3px var(--uix-warning-bg); }
46
48
  .uix-input-group--has-trailing .uix-input { padding-right: 38px; }
47
49
  .uix-input-group__icon--trailing { left: auto; right: 10px; }
50
+ /* lg: the icons move in with the field's wider padding, and the text clears them */
51
+ .uix-input-group:has(> .uix-input--lg) .uix-input-group__icon { left: 14px; }
52
+ .uix-input-group:has(> .uix-input--lg) .uix-input-group__icon--trailing { left: auto; right: 14px; }
53
+ .uix-input-group--has-leading > .uix-input--lg { padding-left: 44px; }
54
+ .uix-input-group--has-trailing > .uix-input--lg { padding-right: 44px; }
55
+
56
+ /* trailing action (HAR-1376): a real button inside the field's end (a reveal toggle, a clear),
57
+ square, as tall as the field less a 4 px inset: 28 px at md, 36 px at lg, 24 px at sm */
58
+ .uix-input-group__action {
59
+ position: absolute; top: 4px; bottom: 4px; right: 4px; aspect-ratio: 1; display: inline-grid; place-items: center;
60
+ padding: 0; border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text-muted);
61
+ font: inherit; line-height: 0; cursor: pointer;
62
+ transition: background-color var(--uix-dur-fast) var(--uix-ease-out), color var(--uix-dur-fast) var(--uix-ease-out);
63
+ }
64
+ .uix-input-group__action svg { width: var(--uix-icon-sm); height: var(--uix-icon-sm); pointer-events: none; }
65
+ @media (hover: hover) { .uix-input-group__action:hover:not(:disabled) { background: var(--uix-bg-hover); color: var(--uix-text); } }
66
+ .uix-input-group__action:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 0; }
67
+ .uix-input-group__action[aria-pressed="true"] { color: var(--uix-text); }
68
+ .uix-input-group__action:disabled { cursor: not-allowed; opacity: .55; }
69
+ .uix-input-group:has(> .uix-input--sm) > .uix-input-group__action { top: 2px; bottom: 2px; right: 2px; }
70
+ .uix-input-group--has-action > .uix-input { padding-right: 40px; }
71
+ .uix-input-group--has-action > .uix-input--sm { padding-right: 32px; }
72
+ .uix-input-group--has-action > .uix-input--lg { padding-right: 48px; }
73
+ /* at lg (a public page) the 36 px button gets a 44 px target without a bigger look */
74
+ .uix-input-group:has(> .uix-input--lg) > .uix-input-group__action::before { content: ""; position: absolute; inset: -4px; }
75
+
76
+ /* OtpInput (HAR-1376): ONE native input (screen readers, autofill and paste see one field)
77
+ laid transparently over cells that draw its characters; the cell at the caret shows the ring */
78
+ /* As wide as its cells at 44 px (React sets --uix-otp-cells and --uix-otp-seps), never wider than
79
+ its container: the cells give way first, down to 24 px each. */
80
+ .uix-otp {
81
+ --uix-otp-cells: 6; --uix-otp-seps: 0;
82
+ position: relative; display: flex; gap: var(--uix-space-2); width: 100%;
83
+ direction: ltr; /* a code reads left to right, also in an RTL page */
84
+ max-width: calc(var(--uix-otp-cells) * 44px + (var(--uix-otp-cells) + var(--uix-otp-seps) - 1) * var(--uix-space-2) + var(--uix-otp-seps) * 8px);
85
+ }
86
+ .uix-otp__input {
87
+ position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; outline: none;
88
+ background: transparent; color: transparent; caret-color: transparent; font: inherit; letter-spacing: 0;
89
+ opacity: 0; cursor: text;
90
+ }
91
+ .uix-otp__input:disabled { cursor: not-allowed; }
92
+ .uix-otp__cell {
93
+ display: grid; place-items: center; flex: 1 1 0; min-width: 20px; height: 44px;
94
+ border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface);
95
+ color: var(--uix-text); font-size: var(--uix-text-h3); font-weight: 600; font-variant-numeric: tabular-nums;
96
+ /* no transition: the active cell moves on every keystroke, and typing gets no animation */
97
+ }
98
+ .uix-otp__cell[data-active] { border-color: var(--uix-ring); box-shadow: 0 0 0 3px var(--uix-brand-muted); }
99
+ /* the caret of the cell being typed into: a static bar (typing is frequent, so it does not blink) */
100
+ .uix-otp__cell[data-active]:empty::after { content: ""; width: 1px; height: 1.2em; background: var(--uix-text); }
101
+ @media (hover: hover) { .uix-otp:hover:not(:has(> .uix-otp__input:disabled)) > .uix-otp__cell:not([data-active]) { border-color: var(--uix-border-strong); } }
102
+ .uix-otp:has(> .uix-otp__input[aria-invalid="true"]) > .uix-otp__cell { border-color: var(--uix-danger); }
103
+ .uix-otp:has(> .uix-otp__input[aria-invalid="true"]) > .uix-otp__cell[data-active] { box-shadow: 0 0 0 3px var(--uix-danger-bg); }
104
+ .uix-otp:has(> .uix-otp__input:disabled) > .uix-otp__cell { opacity: .55; background: var(--uix-bg-subtle); }
105
+ .uix-otp__sep { align-self: center; flex: none; width: 8px; height: 2px; border-radius: var(--uix-radius-pill); background: var(--uix-border-strong); }
106
+ @media (forced-colors: active) {
107
+ .uix-otp__cell[data-active] { outline: 2px solid Highlight; outline-offset: 1px; }
108
+ .uix-otp__cell[data-active]:empty::after { background: CanvasText; }
109
+ }
48
110
  }
@@ -4,10 +4,19 @@
4
4
  .uix-kanban__col { flex: 0 0 260px; background: var(--uix-bg-subtle); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-lg); display: flex; flex-direction: column; max-height: 460px; }
5
5
  .uix-kanban__head { display: flex; align-items: center; gap: var(--uix-space-2); padding: var(--uix-space-3) var(--uix-space-4); font-weight: 600; font-size: var(--uix-text-meta); }
6
6
  .uix-kanban__count { margin-left: auto; color: var(--uix-text-muted); font-weight: 500; }
7
- .uix-kanban__body { padding: 0 var(--uix-space-3) var(--uix-space-3); display: flex; flex-direction: column; gap: var(--uix-space-2); overflow-y: auto; }
7
+ /* a little room on top: the scrolling body would clip a lifted or picked-up card's edge */
8
+ .uix-kanban__body { padding: var(--uix-space-1) var(--uix-space-3) var(--uix-space-3); display: flex; flex-direction: column; gap: var(--uix-space-2); overflow-y: auto; }
8
9
  .uix-kanban__card { background: var(--uix-surface); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); padding: var(--uix-space-3); box-shadow: var(--uix-shadow-sm); cursor: grab; transition: transform var(--uix-dur-fast) var(--uix-ease-out); }
9
10
  @media (hover: hover) and (pointer: fine) { .uix-kanban__card:hover { box-shadow: var(--uix-shadow-popover); transform: translateY(calc(-1 * var(--uix-lift))); } }
10
11
  .uix-kanban__card:active { cursor: grabbing; }
12
+ /* dragged (drag.css, HAR-2590): the lift wins over the hover rise while the pointer is on it */
13
+ .uix-kanban__card[data-uix-drag="lifted"] { transform: scale(1.02); box-shadow: var(--uix-shadow-popover); }
14
+ @media (prefers-reduced-motion: reduce) { .uix-kanban__card[data-uix-drag="lifted"] { transform: none; } }
11
15
  .uix-kanban__card-title { font-weight: 500; font-size: var(--uix-text-body); margin-bottom: var(--uix-space-2); }
12
16
  .uix-kanban__card-meta { display: flex; align-items: center; gap: var(--uix-space-2); }
13
17
  }
18
+
19
+ /* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same rule */
20
+ @layer uix.components {
21
+ @media (prefers-reduced-motion: no-preference) { :root[data-motion="reduce"] .uix-kanban__card[data-uix-drag="lifted"] { transform: none; } }
22
+ }
@@ -12,3 +12,28 @@
12
12
  .uix-kbd-combo { display: inline-flex; align-items: center; }
13
13
  .uix-kbd-combo__keys { display: inline-flex; align-items: center; gap: var(--uix-space-1); }
14
14
  }
15
+
16
+ /* Shortcut hints are for a keyboard. On a device with no hover and a coarse pointer (a touch
17
+ phone or tablet) the kit's decorative key caps go: the Sidebar search row's keys, the keys of a
18
+ Menu, CommandPalette or Tooltip row, SearchSuggest's trailing key, and a Button's hint bubble
19
+ (a tap fires mouseenter, which opened it). A keyboard-and-mouse window of any width keeps them.
20
+ Nothing a screen reader hears changes: what goes is already aria-hidden (each KbdCombo says its
21
+ keys in a visually-hidden span, which stays), a key a product put in a Menu or CommandItem
22
+ shortcut slot is only clipped from view, and `aria-keyshortcuts` and descriptions stay. Each
23
+ selector out-specifies the rule that lays the part out, so the order the sheets load in does
24
+ not matter. */
25
+ @layer uix.components {
26
+ @media (hover: none) and (pointer: coarse) {
27
+ .uix-sidebar__search .uix-sidebar__search-keys,
28
+ :is(.uix-menu__shortcut, .uix-cmdk__shortcut, .uix-tooltip__shortcut) .uix-kbd-combo__keys,
29
+ .uix-search-suggest__trail > .uix-kbd,
30
+ .uix-tooltip__bubble.uix-tooltip__bubble--hint,
31
+ .uix-tooltip__bubble:has(> .uix-tooltip__shortcut:only-child) { display: none; }
32
+ :is(.uix-menu__shortcut, .uix-cmdk__item) > kbd.uix-kbd {
33
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
34
+ }
35
+ /* no gap left where the keys were */
36
+ .uix-menu__item .uix-menu__shortcut { padding-left: 0; }
37
+ .uix-tooltip__bubble .uix-tooltip__shortcut { margin-inline-start: 0; }
38
+ }
39
+ }
@@ -40,12 +40,25 @@
40
40
  .uix-lightbox__nav--prev { left: var(--uix-space-3); }
41
41
  .uix-lightbox__nav--next { right: var(--uix-space-3); }
42
42
  .uix-lightbox__close { top: var(--uix-space-3); right: var(--uix-space-3); }
43
+ /* HAR-1826: the consumer's actions, then Close, top right; it wraps under itself on a phone. */
44
+ .uix-lightbox__toolbar { position: absolute; top: var(--uix-space-3); right: var(--uix-space-3); left: var(--uix-space-3); display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--uix-space-2); pointer-events: none; }
45
+ .uix-lightbox__toolbar > * { pointer-events: auto; }
46
+ .uix-lightbox__toolbar .uix-lightbox__close { position: static; flex: none; }
47
+ /* With actions the toolbar is wider than Close alone: the picture starts below it. */
48
+ .uix-lightbox--gallery[data-actions] { padding-top: calc(44px + 2 * var(--uix-space-3)); }
49
+ .uix-lightbox--gallery[data-actions] :is(.uix-lightbox__stage, .uix-lightbox__media) { max-height: calc(100dvh - 8rem - 44px - var(--uix-space-3)); }
50
+ /* Laid over the picture's own box (measured by the React adapter); it draws, so no swipe. */
51
+ .uix-lightbox__stage:has(.uix-lightbox__overlay) { position: relative; }
52
+ .uix-lightbox__overlay { position: absolute; touch-action: none; }
43
53
  .uix-lightbox__nav:disabled { opacity: .4; cursor: default; }
44
54
  .uix-lightbox__nav:not(:disabled):hover, .uix-lightbox__close:hover { background: var(--uix-bg-hover); }
45
55
  .uix-lightbox__nav:focus-visible, .uix-lightbox__close:focus-visible, .uix-lightbox__tool:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
46
56
  /* phones: the image takes the width; previous/next sit at the bottom corners, clear of it */
47
57
  @media (max-width: 40rem) {
48
58
  .uix-lightbox--gallery .uix-lightbox__media { width: calc(100vw - 2 * var(--uix-space-4)); max-height: calc(100dvh - 10rem); }
59
+ /* the actions may wrap to a second row here */
60
+ .uix-lightbox--gallery[data-actions] { padding-top: calc(2 * 44px + 3 * var(--uix-space-3)); }
61
+ .uix-lightbox--gallery[data-actions] :is(.uix-lightbox__stage, .uix-lightbox__media) { max-height: calc(100dvh - 10rem - 2 * 44px - 2 * var(--uix-space-3)); }
49
62
  .uix-lightbox__nav { top: auto; bottom: var(--uix-space-3); transform: none; }
50
63
  .uix-lightbox__figure { margin-bottom: 3.5rem; }
51
64
  }
@@ -0,0 +1,27 @@
1
+ /* uix list motion — items arrive, leave and move visibly (HAR-2586, motion standard §2.5).
2
+ React's MotionList marks its children: [data-uix-entering] rises 8 px and fades in (never on
3
+ the first render); [data-uix-exiting] fades out in place, and only then is it removed, so the
4
+ items after it glide up into the gap ([data-uix-moving]: a FLIP transform, never a height).
5
+ Reduced motion keeps the fades and drops the rise and the glide. Depends only on --uix-*
6
+ tokens. */
7
+ @layer uix.components {
8
+ @keyframes uix-list-enter { from { opacity: 0; transform: translateY(var(--uix-space-2)); } }
9
+ @keyframes uix-list-exit { to { opacity: 0; transform: scale(.98); } }
10
+ @keyframes uix-list-fade-out { to { opacity: 0; } }
11
+ .uix-motion-list > [data-uix-entering] { animation: uix-list-enter var(--uix-dur) var(--uix-ease-out); }
12
+ .uix-motion-list > [data-uix-exiting] { animation: uix-list-exit var(--uix-dur-exit) var(--uix-ease-out) forwards; pointer-events: none; }
13
+ .uix-motion-list > [data-uix-moving] { transition: transform var(--uix-dur-slow) var(--uix-ease-in-out); }
14
+ @media (prefers-reduced-motion: reduce) {
15
+ /* !important: the kit's reduced-motion policy ends keyframes at once; the fades are the signal */
16
+ .uix-motion-list > [data-uix-entering] { animation: uix-fade-in var(--uix-dur) var(--uix-ease-out) !important; }
17
+ .uix-motion-list > [data-uix-exiting] { animation: uix-list-fade-out var(--uix-dur-exit) var(--uix-ease-out) forwards !important; }
18
+ }
19
+ }
20
+
21
+ /* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same fades */
22
+ @layer uix.components {
23
+ @media (prefers-reduced-motion: no-preference) {
24
+ :root[data-motion="reduce"] .uix-motion-list > [data-uix-entering] { animation: uix-fade-in var(--uix-dur) var(--uix-ease-out) !important; }
25
+ :root[data-motion="reduce"] .uix-motion-list > [data-uix-exiting] { animation: uix-list-fade-out var(--uix-dur-exit) var(--uix-ease-out) forwards !important; }
26
+ }
27
+ }
@@ -9,5 +9,7 @@
9
9
  .uix-list--roving > .uix-list__item[tabindex] { cursor: default; }
10
10
  .uix-list__title { display: block; font-weight: 500; }
11
11
  .uix-list__meta { display: block; font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
12
+ /* the kit's one title -> secondary rhythm: --uix-space-1 under the title (HAR-1577) */
13
+ .uix-list__title + .uix-list__meta { margin-top: var(--uix-space-1); }
12
14
  .uix-list__trail { margin-left: auto; display: flex; align-items: center; gap: var(--uix-space-2); }
13
15
  }
@@ -7,5 +7,45 @@
7
7
  .uix-match-review__table { min-width:54rem; } .uix-match-review__table tr[data-status="resolved"] { background:var(--uix-success-bg); }
8
8
  .uix-match-review__confidence { font-variant-numeric:tabular-nums; font-weight:600; }
9
9
  .uix-match-review__actions { display:flex; flex-wrap:wrap; gap:var(--uix-space-1); }
10
+ /* fieldMode="adopt" (HAR-1369): per candidate, the record now against the candidate, field by field */
11
+ .uix-match-review__candidate { display:grid; gap:var(--uix-space-3); padding:var(--uix-space-4); border:1px solid var(--uix-border); border-radius:var(--uix-radius-lg); background:var(--uix-surface); }
12
+ .uix-match-review__candidate-head { display:flex; flex-wrap:wrap; align-items:center; gap:var(--uix-space-2) var(--uix-space-3); }
13
+ .uix-match-review__candidate-title { margin:0; min-width:0; font-size:var(--uix-text-h3); overflow-wrap:anywhere; }
14
+ .uix-match-review__candidate .uix-match-review__table-wrap { border-radius:var(--uix-radius-md); }
15
+ .uix-match-review__compare :is(tbody th, td) { padding-block:var(--uix-space-2); overflow-wrap:anywhere; }
16
+ .uix-match-review__compare thead th:last-child, .uix-match-review__compare td:last-child { width:1%; white-space:nowrap; text-align:center; }
17
+ /* row headers sit in the rows like cells: same padding, rule and height, the row's own background */
18
+ .uix-match-review__compare tbody th { padding-inline:var(--uix-space-4); height:var(--uix-row-h); border-bottom:1px solid var(--uix-border); background:transparent; color:var(--uix-text-muted); font-weight:500; text-align:start; vertical-align:middle; }
19
+ .uix-match-review__compare tr[data-differs] td:nth-child(3) { font-weight:600; }
20
+ .uix-match-review__compare tr[data-chosen] { background:var(--uix-row-selected-bg); }
21
+ .uix-match-review__adopt { width:18px; height:18px; margin:0; accent-color:var(--uix-accent); }
22
+ .uix-match-review__same { color:var(--uix-text-muted); font-size:var(--uix-text-meta); }
23
+ /* The disclosure is a quiet row, not a link: the kit chevron (as CollapsibleSection), muted text. */
24
+ .uix-match-review__provenance-summary { display:inline-flex; align-items:flex-start; gap:var(--uix-space-1); list-style:none; cursor:pointer; color:var(--uix-text-muted); font-size:var(--uix-text-meta); font-weight:500; border-radius:var(--uix-radius-xs); }
25
+ .uix-match-review__provenance-summary::-webkit-details-marker { display:none; }
26
+ .uix-match-review__provenance-summary:focus-visible { outline:2px solid var(--uix-ring); outline-offset:2px; }
27
+ .uix-match-review__provenance[open] .uix-collapsible__chevron { transform:rotate(180deg); }
28
+ @media (hover: hover) and (pointer: fine) {
29
+ .uix-match-review__provenance-summary:hover, .uix-match-review__provenance-summary:hover .uix-collapsible__chevron { color:var(--uix-text); }
30
+ }
31
+ .uix-match-review__provenance-body { margin-top:var(--uix-space-2); color:var(--uix-text-muted); font-size:var(--uix-text-meta); }
32
+ .uix-match-review__candidate-actions { display:flex; flex-wrap:wrap; align-items:center; gap:var(--uix-space-2); }
33
+ /* A resolved candidate keeps only the (empty) live region here: it takes no row gap until it speaks. */
34
+ .uix-match-review__candidate-actions:not(:has(.uix-btn)):has(.uix-match-review__result:empty) { margin-block-start:calc(-1 * var(--uix-space-3)); }
35
+ .uix-match-review__result { color:var(--uix-text-muted); font-size:var(--uix-text-meta); }
36
+ .uix-match-review__result--failed { color:var(--uix-danger-text); }
37
+ /* A phone: each field is a block, its name and the checkbox on top, the two values under it,
38
+ each named by its column (data-label), so no word breaks inside itself (HAR-1369 screenshots). */
39
+ @media (max-width:30rem) {
40
+ .uix-match-review__candidate { padding:var(--uix-space-3); }
41
+ .uix-match-review__compare thead { position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); }
42
+ .uix-match-review__compare, .uix-match-review__compare tbody { display:block; }
43
+ .uix-match-review__compare tbody tr { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:var(--uix-space-1) var(--uix-space-3); padding:var(--uix-space-2) var(--uix-space-3); border-bottom:1px solid var(--uix-border); }
44
+ .uix-match-review__compare tbody tr:last-child { border-bottom:0; }
45
+ .uix-match-review__compare tbody :is(th, td) { display:block; height:auto; padding:0; border:0; }
46
+ .uix-match-review__compare tbody td:nth-child(2), .uix-match-review__compare tbody td:nth-child(3) { grid-column:1 / -1; }
47
+ .uix-match-review__compare tbody td:last-child { grid-row:1; grid-column:2; width:auto; }
48
+ .uix-match-review__compare tbody td[data-label]::before { content:attr(data-label) ": "; color:var(--uix-text-muted); font-weight:400; }
49
+ }
10
50
  .uix-match-review--state { justify-items:start; padding:var(--uix-space-6); border:1px dashed var(--uix-border-strong); border-radius:var(--uix-radius-lg); }
11
51
  }
@@ -4,6 +4,8 @@
4
4
  .uix-media__thumb { width: 64px; height: 64px; flex: none; border-radius: var(--uix-radius-md); overflow: hidden; background: var(--uix-bg-subtle); border: 1px solid var(--uix-border); display: grid; place-items: center; }
5
5
  .uix-media__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
6
6
  .uix-media__thumb svg { width: 28px; height: 28px; color: var(--uix-text-muted); }
7
- .uix-media__name { font-weight: 600; }
8
- .uix-media__meta { font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
7
+ .uix-media__name { display: block; font-weight: 600; }
8
+ .uix-media__meta { display: block; font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
9
+ /* the kit's one title -> secondary rhythm: --uix-space-1 under the name (HAR-1577) */
10
+ .uix-media__name + .uix-media__meta { margin-top: var(--uix-space-1); }
9
11
  }
@@ -6,5 +6,7 @@
6
6
  .uix-metric-input__unit { display:flex; align-items:center; padding-inline:var(--uix-space-3); border-block:1px solid var(--uix-border-strong); background:var(--uix-bg-subtle); color:var(--uix-text-muted); }
7
7
  .uix-metric-input__steps { display:grid; grid-template-rows:1fr 1fr; width:2.5rem; min-height:0; }
8
8
  .uix-metric-input__step { min-height:0; padding:0; border:1px solid var(--uix-border-strong); background:var(--uix-surface); color:var(--uix-text); line-height:1; } .uix-metric-input__step:first-child { border-top-right-radius:var(--uix-radius-md); } .uix-metric-input__step:last-child { border-top:0; border-bottom-right-radius:var(--uix-radius-md); }
9
- .uix-metric-input__step:hover { background:var(--uix-bg-hover); } .uix-metric-input--invalid .uix-metric-input__control { color:var(--uix-danger-text); }
9
+ .uix-metric-input__step:hover { background:var(--uix-bg-hover); }
10
+ /* + and − are the icon set's Plus and Minus (HAR-1576), centred in each half */
11
+ .uix-metric-input__step { display:grid; place-items:center; } .uix-metric-input__step > .uix-icon { width:var(--uix-icon-sm); height:var(--uix-icon-sm); } .uix-metric-input--invalid .uix-metric-input__control { color:var(--uix-danger-text); }
10
12
  }
@@ -5,20 +5,21 @@
5
5
  border: 1px solid var(--uix-border); border-radius: var(--uix-radius-lg);
6
6
  background: var(--uix-surface-2); box-shadow: var(--uix-shadow-overlay);
7
7
  opacity: 0; transform: translateY(8px) scale(.98);
8
- transition: opacity var(--uix-dur-slow) var(--uix-ease-out), transform var(--uix-dur-slow) var(--uix-ease-out),
9
- overlay var(--uix-dur-slow) allow-discrete, display var(--uix-dur-slow) allow-discrete;
8
+ /* the closed state's durations are the exit's: --uix-dur-exit-slow, faster than the enter, ease-out */
9
+ transition: opacity var(--uix-dur-exit-slow) var(--uix-ease-out), transform var(--uix-dur-exit-slow) var(--uix-ease-out),
10
+ overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
10
11
  /* Author display values for both states required for the fade-in to animate: the allow-discrete
11
12
  `display` transition only engages with author values, which also lets @starting-style fire.
12
13
  With only the UA dialog display, the modal stays at opacity:0. Closed = none; open = block. */
13
14
  display: none;
14
15
  }
15
- .uix-dialog[open] { opacity: 1; transform: none; display: block; }
16
+ .uix-dialog[open] { opacity: 1; transform: none; display: block; transition-duration: var(--uix-dur-slow); }
16
17
  @starting-style { .uix-dialog[open] { opacity: 0; transform: translateY(8px) scale(.98); } }
17
18
  .uix-dialog::backdrop {
18
19
  background: var(--uix-scrim); opacity: 0;
19
- transition: opacity var(--uix-dur-slow) var(--uix-ease-out), overlay var(--uix-dur-slow) allow-discrete, display var(--uix-dur-slow) allow-discrete;
20
+ transition: opacity var(--uix-dur-exit-slow) var(--uix-ease-out), overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
20
21
  }
21
- .uix-dialog[open]::backdrop { opacity: 1; }
22
+ .uix-dialog[open]::backdrop { opacity: 1; transition-duration: var(--uix-dur-slow); }
22
23
  @starting-style { .uix-dialog[open]::backdrop { opacity: 0; } }
23
24
 
24
25
  .uix-dialog__header { display: flex; align-items: center; gap: var(--uix-space-3); padding: var(--uix-space-4) var(--uix-space-5); border-bottom: 1px solid var(--uix-border); }
@@ -47,6 +48,10 @@
47
48
  .uix-confirm__body { display: grid; gap: var(--uix-space-3); }
48
49
  .uix-confirm__compensation { display: grid; gap: var(--uix-space-1); padding: var(--uix-space-3); border-radius: var(--uix-radius-sm); background: var(--uix-bg-subtle); color: var(--uix-text); font-size: var(--uix-text-meta); }
49
50
  .uix-confirm__compensation-title { font-weight: 600; }
51
+ /* the mismatch message (HAR-1831): a hidden copy holds its wrapped height, so it never moves the footer */
52
+ .uix-confirm__typed-msg { display: grid; }
53
+ .uix-confirm__typed-msg > * { grid-area: 1 / 1; }
54
+ .uix-confirm__typed-msg > [aria-hidden="true"] { visibility: hidden; }
50
55
  .uix-confirm__error { padding: var(--uix-space-2) var(--uix-space-3); border-radius: var(--uix-radius-sm); background: var(--uix-danger-bg); color: var(--uix-danger-text); font-size: var(--uix-text-meta); }
51
56
  .uix-dialog__footer .uix-confirm__tertiary { margin-inline-end: auto; }
52
57
  }
@@ -10,3 +10,15 @@
10
10
  .uix-page-header__subtitle { color: var(--uix-text-muted); font-size: var(--uix-text-meta); margin-top: var(--uix-space-2); max-width: 70ch; }
11
11
  .uix-page-header__actions { display: flex; gap: var(--uix-space-2); align-items: center; flex-wrap: wrap; }
12
12
  }
13
+
14
+ /* Display preset (HAR-1815): the staff page header TENSOR and MOTUS share. A 40 px heading-font
15
+ title (--uix-text-display, leading 1.05, weight 600, tight tracking), a bold hushed eyebrow, a
16
+ body-size hushed subtitle up to 42rem, and the actions on the titles' bottom line. */
17
+ @layer uix.components {
18
+ .uix-page-header--display { align-items: flex-end; }
19
+ .uix-page-header--display .uix-page-header__eyebrow { color: var(--uix-text-hushed); font-weight: 700; }
20
+ .uix-page-header--display :is(.uix-page-header__title, .uix-page-header__title-row) { font-family: var(--uix-font-heading); font-size: var(--uix-text-display); line-height: var(--uix-leading-display); font-weight: 600; letter-spacing: var(--uix-tracking-tight); }
21
+ .uix-page-header--display .uix-page-header__subtitle { font-size: var(--uix-text-body); color: var(--uix-text-hushed); max-width: 42rem; }
22
+ /* a 40 px title is too wide for a phone: it steps down to the h2 size there */
23
+ @media (max-width: 40rem) { .uix-page-header--display :is(.uix-page-header__title, .uix-page-header__title-row) { font-size: var(--uix-text-h2); line-height: var(--uix-leading-h2); } }
24
+ }
@@ -8,6 +8,8 @@
8
8
  transition: background-color var(--uix-dur-fast) var(--uix-ease-out), border-color var(--uix-dur-fast) var(--uix-ease-out);
9
9
  }
10
10
  .uix-pagination__btn:hover { background: var(--uix-bg-hover); }
11
+ /* first / previous / next are the icon set's chevrons (HAR-1576) */
12
+ .uix-pagination__btn > .uix-icon { width: var(--uix-icon-sm); height: var(--uix-icon-sm); }
11
13
  .uix-pagination__btn[aria-current="page"] { background: var(--uix-accent); border-color: var(--uix-accent); color: var(--uix-accent-fg); }
12
14
  .uix-pagination__btn:disabled, .uix-pagination__btn[aria-disabled="true"] { opacity: .45; pointer-events: none; }
13
15
  /* link pager (HAR-1357): anchors look like the buttons */