@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
@@ -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
+ }
@@ -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
  }
@@ -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
  }
@@ -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
  }
@@ -47,6 +47,10 @@
47
47
  .uix-confirm__body { display: grid; gap: var(--uix-space-3); }
48
48
  .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
49
  .uix-confirm__compensation-title { font-weight: 600; }
50
+ /* the mismatch message (HAR-1831): a hidden copy holds its wrapped height, so it never moves the footer */
51
+ .uix-confirm__typed-msg { display: grid; }
52
+ .uix-confirm__typed-msg > * { grid-area: 1 / 1; }
53
+ .uix-confirm__typed-msg > [aria-hidden="true"] { visibility: hidden; }
50
54
  .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
55
  .uix-dialog__footer .uix-confirm__tertiary { margin-inline-end: auto; }
52
56
  }
@@ -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 */
@@ -27,5 +27,9 @@
27
27
  .uix-popconfirm { display: grid; gap: var(--uix-space-2); width: max-content; max-width: min(20rem, calc(100vw - 2 * var(--uix-space-4))); padding: var(--uix-space-3); }
28
28
  .uix-popconfirm__title { font-weight: 600; color: var(--uix-text); }
29
29
  .uix-popconfirm__body { display: grid; gap: var(--uix-space-2); color: var(--uix-text-hushed); font-size: var(--uix-text-meta); }
30
+ /* content of its own, e.g. a required reason field (HAR-1831): the step takes its full width
31
+ so the field is not squeezed to the text's max-content */
32
+ .uix-popconfirm--content { width: min(20rem, calc(100vw - 2 * var(--uix-space-4))); }
33
+ .uix-popconfirm__content { display: grid; gap: var(--uix-space-2); min-width: 0; }
30
34
  .uix-popconfirm__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--uix-space-2); margin-top: var(--uix-space-1); }
31
35
  }
@@ -0,0 +1,177 @@
1
+ /* uix print layout (HAR-986, HAR-1252) — React PrintLayout, PrintSignature, PrintPageBreak.
2
+ A document frame for the browser's own print: A4 or A5 named pages with the paper's margins,
3
+ a header and a footer that repeat on every page (a CSS table's header and footer groups, which
4
+ the browser repeats per page and which take their own room, so they never sit on the content),
5
+ "page / pages" in the bottom-right page margin, and black-on-white tokens inside the frame.
6
+
7
+ On screen the frame is a paper preview: the page at its true width and its print type size,
8
+ scaled as a whole (zoom, measured by PrintLayout) to the room it has, ink on paper in both
9
+ themes — what you see, and what fits on one page, is what prints.
10
+ Units: mm and pt are the paper's own units; they are not --uix-* scale values. */
11
+ @layer uix.components {
12
+ /* ── the paper ─────────────────────────────────────────────────────────────── */
13
+ @page uix-print-a4 { size: A4 portrait; margin: 16mm 16mm 18mm; @bottom-right { content: counter(page) " / " counter(pages); font-size: 8pt; color: var(--uix-print-ink-quiet); } }
14
+ @page uix-print-a4-landscape { size: A4 landscape; margin: 14mm 16mm 16mm; @bottom-right { content: counter(page) " / " counter(pages); font-size: 8pt; color: var(--uix-print-ink-quiet); } }
15
+ @page uix-print-a5 { size: A5 portrait; margin: 10mm 10mm 12mm; @bottom-right { content: counter(page) " / " counter(pages); font-size: 7pt; color: var(--uix-print-ink-quiet); } }
16
+ @page uix-print-a5-landscape { size: A5 landscape; margin: 10mm 12mm 12mm; @bottom-right { content: counter(page) " / " counter(pages); font-size: 7pt; color: var(--uix-print-ink-quiet); } }
17
+ @page uix-print-a4-plain { size: A4 portrait; margin: 16mm 16mm 18mm; }
18
+ @page uix-print-a4-landscape-plain { size: A4 landscape; margin: 14mm 16mm 16mm; }
19
+ @page uix-print-a5-plain { size: A5 portrait; margin: 10mm 10mm 12mm; }
20
+ @page uix-print-a5-landscape-plain { size: A5 landscape; margin: 10mm 12mm 12mm; }
21
+
22
+ .uix-print-layout {
23
+ /* the page box and its margins, for the screen preview (the same as the @page rules) */
24
+ --print-width: 210mm; --print-height: 297mm; --print-margin: 16mm 16mm 18mm;
25
+ /* everything inside reads ink on paper, in both themes, on screen and on paper */
26
+ --uix-text: var(--uix-print-ink);
27
+ --uix-text-muted: var(--uix-print-ink-quiet);
28
+ --uix-text-hushed: var(--uix-print-ink-quiet);
29
+ --uix-surface: var(--uix-print-paper);
30
+ --uix-surface-2: var(--uix-print-paper);
31
+ --uix-surface-3: var(--uix-print-paper-quiet);
32
+ --uix-bg-app: var(--uix-print-paper);
33
+ --uix-bg-subtle: var(--uix-print-paper-quiet);
34
+ --uix-bg-hover: var(--uix-print-paper-quiet);
35
+ --uix-border: var(--uix-print-line);
36
+ --uix-border-strong: var(--uix-print-ink-quiet);
37
+ --uix-link: var(--uix-print-ink);
38
+ --uix-accent-text: var(--uix-print-ink);
39
+ --uix-row: var(--uix-print-paper);
40
+ --uix-row-alt: var(--uix-print-paper-quiet);
41
+ --uix-row-selected-bg: var(--uix-print-paper-quiet);
42
+ --uix-row-pinned-bg: var(--uix-print-paper-quiet);
43
+ color-scheme: light;
44
+ box-sizing: border-box;
45
+ width: var(--print-width);
46
+ margin-inline: auto;
47
+ /* the type and the rows of the paper, on screen too */
48
+ font-size: 10pt;
49
+ line-height: 1.45;
50
+ --uix-row-h: var(--uix-row-h-compact);
51
+ background: var(--uix-print-paper);
52
+ color: var(--uix-print-ink);
53
+ box-shadow: var(--uix-shadow-md);
54
+ border: 1px solid var(--uix-print-line);
55
+ }
56
+ /* The dark theme's tints are dark: on the paper they read as quiet paper with ink text, so a
57
+ pill or a callout inside the document prints in black and white instead of a dark slab. */
58
+ :root:where(.dark,[data-theme="dark"]) .uix-print-layout {
59
+ --uix-brand-muted: var(--uix-print-paper-quiet);
60
+ --uix-neutral-bg: var(--uix-print-paper-quiet); --uix-neutral-text: var(--uix-print-ink); --uix-neutral-border: var(--uix-print-line);
61
+ --uix-info-bg: var(--uix-print-paper-quiet); --uix-info-text: var(--uix-print-ink);
62
+ --uix-success-bg: var(--uix-print-paper-quiet); --uix-success-text: var(--uix-print-ink);
63
+ --uix-warning-bg: var(--uix-print-paper-quiet); --uix-warning-text: var(--uix-print-ink); --uix-warning-border: var(--uix-print-line);
64
+ --uix-danger-bg: var(--uix-print-paper-quiet); --uix-danger-text: var(--uix-print-danger); --uix-danger-border: var(--uix-print-danger);
65
+ --uix-attention-bg: var(--uix-print-paper-quiet); --uix-attention-text: var(--uix-print-ink);
66
+ --uix-amber-bg: var(--uix-print-paper-quiet); --uix-amber-text: var(--uix-print-ink);
67
+ --uix-overdue-bg: var(--uix-print-paper-quiet); --uix-overdue-text: var(--uix-print-ink);
68
+ --uix-sev-4-bg: var(--uix-print-paper-quiet); --uix-sev-4-text: var(--uix-print-ink);
69
+ --uix-sev-5-bg: var(--uix-print-paper-quiet); --uix-sev-5-text: var(--uix-print-ink);
70
+ }
71
+ .uix-print-layout[data-size="A4"][data-orientation="landscape"] { --print-width: 297mm; --print-height: 210mm; --print-margin: 14mm 16mm 16mm; }
72
+ .uix-print-layout[data-size="A5"] { --print-width: 148mm; --print-height: 210mm; --print-margin: 10mm 10mm 12mm; font-size: 9pt; }
73
+ @media screen { .uix-print-layout { zoom: var(--uix-print-preview-scale, 1); } }
74
+ .uix-print-layout[data-size="A5"][data-orientation="landscape"] { --print-width: 210mm; --print-height: 148mm; --print-margin: 10mm 12mm 12mm; }
75
+ /* The preview box: the paper's width (set inline by PrintLayout), or its container's when that
76
+ is narrower; the page inside is scaled to it. */
77
+ .uix-print-preview { box-sizing: border-box; max-inline-size: 100%; margin-inline: auto; }
78
+ .uix-print-preview + .uix-print-preview { margin-top: var(--uix-space-6); }
79
+
80
+ /* The page is a <table> for print's sake (a repeated thead and tfoot); on screen it is a column. */
81
+ .uix-print-layout__page {
82
+ box-sizing: border-box;
83
+ display: flex;
84
+ flex-direction: column;
85
+ gap: var(--uix-space-4);
86
+ width: 100%;
87
+ min-height: var(--print-height);
88
+ padding: var(--print-margin);
89
+ border-spacing: 0;
90
+ font-size: inherit;
91
+ text-align: start;
92
+ overflow-wrap: anywhere;
93
+ }
94
+ /* zero specificity, so the part classes below (and the print rules) win over this reset */
95
+ :where(.uix-print-layout__page > thead, .uix-print-layout__page > tbody, .uix-print-layout__page > tfoot, .uix-print-layout__page > * > tr, .uix-print-layout__page > * > tr > td) { display: block; padding: 0; }
96
+ .uix-print-layout__main { flex: 1 0 auto; }
97
+ .uix-print-layout__bar { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--uix-space-2) var(--uix-space-4); }
98
+ .uix-print-layout__header .uix-print-layout__bar { padding-bottom: var(--uix-space-3); border-bottom: 1px solid var(--uix-print-ink); }
99
+ .uix-print-layout__body { display: flex; flex-direction: column; gap: var(--uix-space-4); min-width: 0; }
100
+ .uix-print-layout__footer { color: var(--uix-print-ink-quiet); font-size: var(--uix-text-meta); }
101
+ .uix-print-layout__footer .uix-print-layout__bar { padding-top: var(--uix-space-2); border-top: 1px solid var(--uix-print-line); }
102
+
103
+ /* one page and no more: the preview is one page tall; content that does not fit is outlined */
104
+ .uix-print-layout[data-single-page] .uix-print-layout__page { height: var(--print-height); overflow: hidden; }
105
+ .uix-print-layout[data-overflowing] { outline: 2px dashed var(--uix-danger); outline-offset: 2px; }
106
+
107
+ /* ── signature ─────────────────────────────────────────────────────────────── */
108
+ .uix-print-signature { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: flex-end; gap: var(--uix-space-4) var(--uix-space-6); margin-top: var(--uix-space-6); break-inside: avoid; }
109
+ .uix-print-signature__place { margin-right: auto; }
110
+ .uix-print-signature__field { display: grid; gap: var(--uix-space-1); width: min(70mm, 100%); text-align: center; }
111
+ .uix-print-signature__line { height: 14mm; border-bottom: 1px solid var(--uix-print-ink); }
112
+ .uix-print-signature__caption { color: var(--uix-print-ink-quiet); font-size: var(--uix-text-meta); }
113
+ .uix-print-signature__name { font-weight: 600; }
114
+ .uix-print-signature__stamp { display: grid; place-items: center; width: 34mm; height: 34mm; border: 1px dashed var(--uix-print-line); border-radius: 50%; color: var(--uix-print-ink-quiet); font-size: var(--uix-text-meta); }
115
+
116
+ /* ── page breaks and helpers ───────────────────────────────────────────────── */
117
+ .uix-print-break { height: 0; margin-block: var(--uix-space-3); border-top: 1px dashed var(--uix-print-line); }
118
+ .uix-print-keep { break-inside: avoid; }
119
+ .uix-print-only { display: none; }
120
+
121
+ @media print {
122
+ .uix-print-hide { display: none !important; }
123
+ .uix-print-only { display: revert; }
124
+
125
+ /* Only the documents print: every element that neither is nor holds a print layout goes,
126
+ and the ones that hold it (the app shell, its scrolling main) become plain flow, so a
127
+ scroll box cannot cut the document to one page. */
128
+ :root:has(.uix-print-layout[data-isolate]) body :not(:has(.uix-print-layout[data-isolate]), .uix-print-layout[data-isolate], .uix-print-layout[data-isolate] *) { display: none !important; }
129
+ :root:has(.uix-print-layout[data-isolate]) body :has(.uix-print-layout[data-isolate]) {
130
+ display: block !important; position: static !important; overflow: visible !important;
131
+ height: auto !important; min-height: 0 !important; max-height: none !important;
132
+ margin: 0 !important; padding: 0 !important; border: 0 !important; background: none !important;
133
+ box-shadow: none !important; transform: none !important;
134
+ }
135
+ /* the sheet is paper whatever the screen theme was, also with background graphics on */
136
+ :root:has(.uix-print-layout[data-isolate]) { color-scheme: light; background: var(--uix-print-paper) !important; }
137
+ :root:has(.uix-print-layout[data-isolate]) body { margin: 0; padding: 0; background: none !important; }
138
+
139
+ .uix-print-layout {
140
+ display: block; width: 100%; max-width: none; margin: 0;
141
+ border: 0; box-shadow: none; outline: none;
142
+ -webkit-print-color-adjust: exact; print-color-adjust: exact;
143
+ }
144
+ .uix-print-preview { inline-size: auto !important; max-inline-size: none; }
145
+ .uix-print-preview + .uix-print-preview { margin: 0; break-before: page; }
146
+ .uix-print-layout[data-page="uix-print-a4"] { page: uix-print-a4; }
147
+ .uix-print-layout[data-page="uix-print-a4-landscape"] { page: uix-print-a4-landscape; }
148
+ .uix-print-layout[data-page="uix-print-a5"] { page: uix-print-a5; }
149
+ .uix-print-layout[data-page="uix-print-a5-landscape"] { page: uix-print-a5-landscape; }
150
+ .uix-print-layout[data-page="uix-print-a4-plain"] { page: uix-print-a4-plain; }
151
+ .uix-print-layout[data-page="uix-print-a4-landscape-plain"] { page: uix-print-a4-landscape-plain; }
152
+ .uix-print-layout[data-page="uix-print-a5-plain"] { page: uix-print-a5-plain; }
153
+ .uix-print-layout[data-page="uix-print-a5-landscape-plain"] { page: uix-print-a5-landscape-plain; }
154
+
155
+ /* the page is a table again, and the browser repeats its thead and tfoot on every page */
156
+ .uix-print-layout__page { display: table; table-layout: fixed; min-height: 0; padding: 0; }
157
+ :where(.uix-print-layout__page > thead, .uix-print-layout__page > tbody, .uix-print-layout__page > tfoot, .uix-print-layout__page > * > tr, .uix-print-layout__page > * > tr > td) { display: revert; }
158
+ .uix-print-layout__body { display: table-cell; }
159
+ .uix-print-layout__body > * + * { margin-top: var(--uix-space-4); }
160
+ /* space between the bars and the body, on every page */
161
+ .uix-print-layout__header .uix-print-layout__bar { margin-bottom: 5mm; }
162
+ .uix-print-layout__footer .uix-print-layout__bar { margin-top: 5mm; }
163
+
164
+ /* one page: nothing repeats, nothing is cut, the frame stays in one piece */
165
+ .uix-print-layout[data-single-page] { display: block; break-inside: avoid; }
166
+ .uix-print-layout[data-single-page] .uix-print-layout__page { display: flex; height: auto; overflow: visible; }
167
+ .uix-print-layout[data-single-page] :where(.uix-print-layout__page > thead, .uix-print-layout__page > tbody, .uix-print-layout__page > tfoot, .uix-print-layout__page > * > tr, .uix-print-layout__page > * > tr > td) { display: block; }
168
+ .uix-print-layout[data-single-page] .uix-print-layout__body { display: flex; }
169
+ .uix-print-layout[data-single-page] .uix-print-layout__body > * + * { margin-top: 0; }
170
+
171
+ /* tables, lists and figures inside keep their rows whole; focus rings belong to the screen */
172
+ .uix-print-layout__body :is(tr, li, figure, .uix-card) { break-inside: avoid; }
173
+ .uix-print-layout__body thead { display: table-header-group; }
174
+ .uix-print-layout :focus, .uix-print-layout :focus-visible { outline: none; }
175
+ .uix-print-break { height: 0; margin: 0; border: 0; break-after: page; }
176
+ }
177
+ }
@@ -16,3 +16,10 @@
16
16
  background: conic-gradient(var(--uix-accent) calc(var(--val, .66) * 360deg), var(--uix-bg-active) 0); }
17
17
  .uix-ring__inner { width: 32px; height: 32px; border-radius: 50%; background: var(--uix-surface); display: grid; place-items: center; font-size: var(--uix-text-meta); font-weight: 600; }
18
18
  }
19
+
20
+ /* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same rule */
21
+ @layer uix.components {
22
+ @media (prefers-reduced-motion: no-preference) {
23
+ :root[data-motion="reduce"] .uix-progress--indeterminate .uix-progress__bar { width: 100%; animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; }
24
+ }
25
+ }