@tensor_1/tokens 2.47.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.
- package/build/css/components.css +1 -1
- package/build/css/styles.css +1 -1
- package/package.json +1 -1
- package/styles/components/alert.css +6 -4
- package/styles/components/app-shell.css +40 -0
- package/styles/components/arrival.css +7 -1
- package/styles/components/attachment.css +4 -1
- package/styles/components/audit-log.css +29 -0
- package/styles/components/breadcrumbs.css +2 -0
- package/styles/components/busy-region.css +10 -2
- package/styles/components/button.css +7 -0
- package/styles/components/card.css +10 -0
- package/styles/components/change-highlight.css +58 -0
- package/styles/components/checkbox.css +18 -0
- package/styles/components/code-display.css +43 -0
- package/styles/components/combobox.css +4 -1
- package/styles/components/command-palette.css +16 -2
- package/styles/components/contact-card.css +17 -6
- package/styles/components/count.css +23 -0
- package/styles/components/data-workspace.css +42 -0
- package/styles/components/date-picker.css +14 -1
- package/styles/components/detail-layout.css +9 -0
- package/styles/components/diff-viewer.css +5 -0
- package/styles/components/display-preferences.css +38 -0
- package/styles/components/editorial-home.css +24 -2
- package/styles/components/flow.css +5 -0
- package/styles/components/heartbeat.css +8 -0
- package/styles/components/icon.css +14 -0
- package/styles/components/inline-edit.css +19 -3
- package/styles/components/input.css +62 -0
- package/styles/components/lightbox.css +13 -0
- package/styles/components/list.css +2 -0
- package/styles/components/match-review.css +40 -0
- package/styles/components/media.css +4 -2
- package/styles/components/metric-input.css +3 -1
- package/styles/components/modal.css +4 -0
- package/styles/components/page-header.css +12 -0
- package/styles/components/pagination.css +2 -0
- package/styles/components/popover.css +4 -0
- package/styles/components/print-layout.css +177 -0
- package/styles/components/progress.css +7 -0
- package/styles/components/qr-code.css +30 -0
- package/styles/components/qr-scanner.css +61 -0
- package/styles/components/relationship-graph.css +43 -8
- package/styles/components/rule-builder.css +2 -1
- package/styles/components/save-status.css +5 -0
- package/styles/components/scheduling-calendar.css +82 -1
- package/styles/components/search-suggest.css +2 -0
- package/styles/components/segmented.css +10 -1
- package/styles/components/select.css +11 -0
- package/styles/components/sidebar.css +51 -1
- package/styles/components/sortable-list.css +42 -0
- package/styles/components/spinner.css +5 -0
- package/styles/components/stat-tile.css +36 -12
- package/styles/components/states.css +5 -0
- package/styles/components/status-pill.css +28 -6
- package/styles/components/steps.css +2 -0
- package/styles/components/table-toolbar.css +1 -1
- package/styles/components/table.css +18 -5
- package/styles/components/tabs.css +8 -3
- package/styles/components/tag-input.css +21 -0
- package/styles/components/textarea.css +3 -0
- package/styles/components/toast.css +12 -0
- package/styles/components/tree.css +3 -1
- package/styles/components/view-menu.css +3 -0
- package/styles/motion.css +18 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tensor_1/tokens",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.48.0",
|
|
4
4
|
"description": "UIx v2 design tokens — the --uix-* contract for house products (Tensor, POSx, SHOPx). One DTCG source -> CSS variables, Tailwind theme, and typed TS constants.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
.uix-alert__icon svg { width: var(--uix-icon-md); height: var(--uix-icon-md); }
|
|
10
10
|
.uix-alert__title { font-weight: 600; margin-bottom: var(--uix-space-1); }
|
|
11
11
|
.uix-alert__body { font-size: var(--uix-text-meta); color: var(--uix-text-hushed); }
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.uix-alert--
|
|
15
|
-
.uix-alert--
|
|
12
|
+
/* the border is a 24 % hairline of the tone (HAR-1563): the tint already says the tone, and a
|
|
13
|
+
stronger edge made a page of alerts loud. The icon takes the text role, which clears 3:1. */
|
|
14
|
+
.uix-alert--info { --tone-bg: var(--uix-info-bg); --tone-border: color-mix(in srgb, var(--uix-info) 24%, transparent); --tone-fg: var(--uix-info-text); }
|
|
15
|
+
.uix-alert--success { --tone-bg: var(--uix-success-bg); --tone-border: color-mix(in srgb, var(--uix-success) 24%, transparent); --tone-fg: var(--uix-success-text); }
|
|
16
|
+
.uix-alert--warning { --tone-bg: var(--uix-warning-bg); --tone-border: color-mix(in srgb, var(--uix-warning) 24%, transparent); --tone-fg: var(--uix-warning-text); }
|
|
17
|
+
.uix-alert--danger { --tone-bg: var(--uix-danger-bg); --tone-border: color-mix(in srgb, var(--uix-danger) 24%, transparent); --tone-fg: var(--uix-danger-text); }
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
/* HAR-1614: the text column takes the free width and may shrink below its longest word, so a
|
|
@@ -57,3 +57,43 @@
|
|
|
57
57
|
the gutter. Pair with a frozen identifier column for horizontal scroll. */
|
|
58
58
|
.uix-shell__main--bleed { padding: 0; }
|
|
59
59
|
}
|
|
60
|
+
|
|
61
|
+
/* Full-viewport mode (HAR-1815): an application's shell, not a framed demo. No border or radius,
|
|
62
|
+
the dynamic viewport height, and no fixed top-bar row: the sidebar runs the full height, and a
|
|
63
|
+
topbar, if there is one, takes the height it needs at the top of the main column. Main gets the
|
|
64
|
+
staff padding (space-7 / space-6 / space-6) and space-8 at the sides on a wide screen. Below
|
|
65
|
+
40rem the shell is one column and the sidebar is hidden: the phone nav tier takes over. */
|
|
66
|
+
@layer uix.components {
|
|
67
|
+
.uix-shell--viewport { height: 100dvh; min-height: 0; border: 0; border-radius: 0; grid-template-rows: auto minmax(0, 1fr); }
|
|
68
|
+
.uix-shell--viewport > .uix-shell__topbar { grid-row: 1; padding-block: var(--uix-space-3); }
|
|
69
|
+
.uix-shell--viewport > .uix-shell__main { grid-row: 2; padding: var(--uix-space-7) var(--uix-space-6) var(--uix-space-6); }
|
|
70
|
+
.uix-shell--viewport:not(:has(> .uix-shell__topbar)) > .uix-shell__main { grid-row: 1 / 3; }
|
|
71
|
+
.uix-shell--viewport > .uix-shell__main.uix-shell__main--bleed { padding: 0; }
|
|
72
|
+
@media (min-width: 80rem) { .uix-shell--viewport > .uix-shell__main:not(.uix-shell__main--bleed) { padding-inline: var(--uix-space-8); } }
|
|
73
|
+
/* one column and no sidebar at phone width come from the phone tier below ([data-phone-nav],
|
|
74
|
+
the viewport default); with phoneNav="none" the sidebar column stays, as in the frame */
|
|
75
|
+
@media (max-width: 40rem) {
|
|
76
|
+
.uix-shell--viewport > .uix-shell__main { padding: var(--uix-space-5) var(--uix-space-4) var(--uix-space-6); }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* The phone nav tier (HAR-1371, AppShell phoneNav="drawer"): at 40rem and under, the sidebar
|
|
81
|
+
column goes and a menu button opens the sidebar in a start-side drawer. The switch is a
|
|
82
|
+
breakpoint, not an animation; the drawer brings the kit's own enter and exit. */
|
|
83
|
+
@layer uix.components {
|
|
84
|
+
.uix-shell__nav-button { display: none; }
|
|
85
|
+
.uix-shell__phone-bar { display: none; }
|
|
86
|
+
/* the sidebar fills its column, so a Sidebar footer (the AccountMenu) sits at the bottom */
|
|
87
|
+
.uix-shell__sidebar > .uix-sidebar { min-height: 100%; }
|
|
88
|
+
/* in the drawer the sidebar grows to the drawer body (a flex column whose height is a min-height,
|
|
89
|
+
so min-height: 100% would not resolve) and the AccountMenu sits at the bottom, as in the column */
|
|
90
|
+
.uix-shell__nav-drawer .uix-sidebar { flex: 1 0 auto; width: auto; padding: 0; background: transparent; }
|
|
91
|
+
.uix-shell__nav-drawer-body { display: flex; flex-direction: column; min-height: 100%; }
|
|
92
|
+
@media (max-width: 40rem) {
|
|
93
|
+
.uix-shell[data-phone-nav], .uix-shell[data-phone-nav][data-nav] { grid-template-columns: minmax(0, 1fr); }
|
|
94
|
+
.uix-shell[data-phone-nav] > .uix-shell__sidebar { display: none; }
|
|
95
|
+
.uix-shell[data-phone-nav] > :is(.uix-shell__topbar, .uix-shell__main) { grid-column: 1; }
|
|
96
|
+
.uix-shell[data-phone-nav] .uix-shell__nav-button { display: inline-flex; flex: none; }
|
|
97
|
+
.uix-shell[data-phone-nav] .uix-shell__phone-bar { display: flex; margin-block-end: var(--uix-space-3); }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -19,8 +19,14 @@
|
|
|
19
19
|
0% { outline: 2px solid var(--uix-ring); outline-offset: var(--uix-space-1); }
|
|
20
20
|
100% { outline: 2px solid transparent; outline-offset: var(--uix-space-1); }
|
|
21
21
|
}
|
|
22
|
+
/* !important: the reduced-motion policy (motion.css) ends every keyframe animation at once, which
|
|
23
|
+
would end this steady outline before anyone saw it; a layered !important wins over it. The
|
|
24
|
+
in-app preference (data-motion="reduce", HAR-1372) gets the same outline. */
|
|
22
25
|
@media (prefers-reduced-motion: reduce) {
|
|
23
|
-
.uix-arrival { animation: uix-arrival-static var(--uix-dur-highlight) step-end both; }
|
|
26
|
+
.uix-arrival { animation: uix-arrival-static var(--uix-dur-highlight) step-end both !important; }
|
|
27
|
+
}
|
|
28
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
29
|
+
:root[data-motion="reduce"] .uix-arrival { animation: uix-arrival-static var(--uix-dur-highlight) step-end both !important; }
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
@media (forced-colors: active) {
|
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
.uix-attachment { display: flex; align-items: center; gap: var(--uix-space-3); padding: var(--uix-space-2) var(--uix-space-3); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface); }
|
|
5
5
|
.uix-attachment__icon { width: 32px; height: 32px; flex: none; border-radius: var(--uix-radius-sm); background: var(--uix-bg-subtle); display: grid; place-items: center; color: var(--uix-text-muted); }
|
|
6
6
|
.uix-attachment__icon svg { width: var(--uix-icon-md); height: var(--uix-icon-md); }
|
|
7
|
-
.uix-attachment__name { font-size: var(--uix-text-body); }
|
|
7
|
+
.uix-attachment__name { display: block; font-size: var(--uix-text-body); }
|
|
8
8
|
.uix-attachment__size { font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
|
|
9
|
+
/* plain markup: the size under the name, --uix-space-1 apart (HAR-1577). The React row stacks
|
|
10
|
+
its line and meta in .uix-attachment__body, whose gap is the same --uix-space-1. */
|
|
11
|
+
.uix-attachment__name + .uix-attachment__size { display: block; margin-top: var(--uix-space-1); }
|
|
9
12
|
.uix-attachment__actions { margin-left: auto; }
|
|
10
13
|
}
|
|
11
14
|
|
|
@@ -8,4 +8,33 @@
|
|
|
8
8
|
.uix-audit__detail ins { background: var(--uix-success-bg); color: var(--uix-success); text-decoration: none; padding: 0 var(--uix-space-1); border-radius: var(--uix-radius-xs); }
|
|
9
9
|
.uix-audit__detail del { background: var(--uix-danger-bg); color: var(--uix-danger-text); text-decoration: line-through; padding: 0 var(--uix-space-1); border-radius: var(--uix-radius-xs); }
|
|
10
10
|
.uix-audit__time { color: var(--uix-text-muted); white-space: nowrap; }
|
|
11
|
+
|
|
12
|
+
/* React `AuditLog` (HAR-987): filters over a list of entries; a row is who · what (a labelled
|
|
13
|
+
action and its target) · when (relative, the exact time under it), with Details below. */
|
|
14
|
+
.uix-audit-log { display: grid; gap: var(--uix-space-3); min-width: 0; }
|
|
15
|
+
.uix-audit-log__filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--uix-space-2); }
|
|
16
|
+
/* a reload keeps the rows on show: BusyRegion dims them under its bar (HAR-2582) */
|
|
17
|
+
ol.uix-audit-log__list { margin: 0; padding: 0; list-style: none; }
|
|
18
|
+
.uix-audit-log__row { align-items: start; }
|
|
19
|
+
.uix-audit-log__what { display: flex; flex-wrap: wrap; align-items: center; gap: var(--uix-space-1) var(--uix-space-2); min-width: 0; color: var(--uix-text); }
|
|
20
|
+
.uix-audit-log__target { min-width: 0; overflow-wrap: anywhere; }
|
|
21
|
+
.uix-audit-log__when { display: grid; justify-items: end; gap: calc(var(--uix-space-1) / 2); text-align: end; }
|
|
22
|
+
.uix-audit-log__exact { font-size: var(--uix-text-eyebrow); color: var(--uix-text-muted); font-variant-numeric: tabular-nums; }
|
|
23
|
+
.uix-audit-log__more { grid-column: 1 / -1; }
|
|
24
|
+
/* Details: a quiet disclosure, not a link — the kit chevron (as CollapsibleSection), muted text. */
|
|
25
|
+
.uix-audit-log__more-summary { display: inline-flex; align-items: flex-start; gap: var(--uix-space-1); width: max-content; list-style: none; cursor: pointer; color: var(--uix-text-muted); font-weight: 500; }
|
|
26
|
+
.uix-audit-log__more-summary::-webkit-details-marker { display: none; }
|
|
27
|
+
.uix-audit-log__more-summary:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 2px; border-radius: var(--uix-radius-xs); }
|
|
28
|
+
.uix-audit-log__more[open] .uix-collapsible__chevron { transform: rotate(180deg); }
|
|
29
|
+
@media (hover: hover) and (pointer: fine) {
|
|
30
|
+
.uix-audit-log__more-summary:hover, .uix-audit-log__more-summary:hover .uix-collapsible__chevron { color: var(--uix-text); }
|
|
31
|
+
}
|
|
32
|
+
.uix-audit-log__detail { margin-top: var(--uix-space-2); padding: var(--uix-space-2) var(--uix-space-3); border-radius: var(--uix-radius-sm); background: var(--uix-bg-subtle); color: var(--uix-text-hushed); overflow-wrap: anywhere; }
|
|
33
|
+
.uix-audit-log__more-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--uix-space-2) var(--uix-space-3); }
|
|
34
|
+
.uix-audit-log__more-failed { color: var(--uix-danger-text); font-size: var(--uix-text-meta); }
|
|
35
|
+
/* a phone: who, then what, then when, one under the other */
|
|
36
|
+
@media (max-width: 30rem) {
|
|
37
|
+
.uix-audit-log__row { grid-template-columns: minmax(0, 1fr); gap: var(--uix-space-1); }
|
|
38
|
+
.uix-audit-log__when { justify-items: start; text-align: start; grid-auto-flow: column; justify-content: start; gap: var(--uix-space-2); }
|
|
39
|
+
}
|
|
11
40
|
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
/* already a quiet link by its own design — hover/focus parity with the quiet-link
|
|
6
6
|
contract in link.css, so the keyboard state matches the pointer state. */
|
|
7
7
|
.uix-breadcrumbs a:is(:hover, :focus-visible) { color: var(--uix-text); text-decoration: underline; }
|
|
8
|
+
/* a long unbroken title (a German compound, a file name) wraps instead of widening a phone page */
|
|
9
|
+
.uix-breadcrumbs > li { min-inline-size: 0; overflow-wrap: anywhere; }
|
|
8
10
|
.uix-breadcrumbs__sep { opacity: .55; }
|
|
9
11
|
.uix-breadcrumbs [aria-current="page"] { color: var(--uix-text); font-weight: 500; }
|
|
10
12
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
@layer uix.components {
|
|
8
8
|
.uix-busy-region { position: relative; }
|
|
9
9
|
.uix-busy-region__content { transition: opacity var(--uix-dur-exit) var(--uix-ease-out); }
|
|
10
|
-
.uix-busy-region:has(> .uix-busy-bar[data-shown]) > .uix-busy-region__content { opacity: .
|
|
10
|
+
.uix-busy-region:has(> .uix-busy-bar[data-shown]) > .uix-busy-region__content { opacity: .8; transition-duration: var(--uix-dur); }
|
|
11
11
|
|
|
12
12
|
.uix-busy-bar {
|
|
13
13
|
position: absolute; inset-block-start: 0; inset-inline: 0; z-index: var(--uix-z-sticky);
|
|
@@ -43,5 +43,13 @@
|
|
|
43
43
|
.uix-table-wrap > .uix-busy-bar { position: sticky; inset-inline-start: 0; height: 0; overflow-x: clip; overflow-y: visible; z-index: calc(var(--uix-z-sticky) + 1); }
|
|
44
44
|
.uix-table-wrap > .uix-busy-bar::before { inset-block: 0 auto; height: 2px; }
|
|
45
45
|
.uix-table-wrap > .uix-table > tbody { transition: opacity var(--uix-dur-exit) var(--uix-ease-out); }
|
|
46
|
-
.uix-table-wrap:has(> .uix-busy-bar[data-shown]) > .uix-table > tbody { opacity: .
|
|
46
|
+
.uix-table-wrap:has(> .uix-busy-bar[data-shown]) > .uix-table > tbody { opacity: .8; transition-duration: var(--uix-dur); }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same rule */
|
|
50
|
+
@layer uix.components {
|
|
51
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
52
|
+
:root[data-motion="reduce"] .uix-busy-bar::before { width: 100%; transform: none; }
|
|
53
|
+
:root[data-motion="reduce"] .uix-busy-bar[data-shown]::before { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; }
|
|
54
|
+
}
|
|
47
55
|
}
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
.uix-btn--icon { width: var(--uix-control-h); padding: 0; }
|
|
37
37
|
.uix-btn--icon.uix-btn--xs { width: 24px; }
|
|
38
38
|
.uix-btn--icon.uix-btn--sm { width: 28px; }
|
|
39
|
+
/* A small link button is still a link: no side padding, so it lines up with the text around it. */
|
|
40
|
+
.uix-btn--link.uix-btn--sm { padding-inline: 0; }
|
|
39
41
|
.uix-btn--icon.uix-btn--lg { width: 44px; }
|
|
40
42
|
|
|
41
43
|
/* A label that may wrap (HAR-1813, WCAG 1.4.10). The size sets a floor instead of a fixed
|
|
@@ -102,3 +104,8 @@
|
|
|
102
104
|
.uix-btn-group .uix-btn:first-child { border-top-left-radius: var(--uix-radius-md); border-bottom-left-radius: var(--uix-radius-md); }
|
|
103
105
|
.uix-btn-group .uix-btn:last-child { border-radius: 0 var(--uix-radius-md) var(--uix-radius-md) 0; border-right-width: 1px; }
|
|
104
106
|
}
|
|
107
|
+
|
|
108
|
+
/* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same pulse */
|
|
109
|
+
@layer uix.components {
|
|
110
|
+
@media (prefers-reduced-motion: no-preference) { :root[data-motion="reduce"] .uix-btn:has(> [data-uix-btn-busy])::after, :root[data-motion="reduce"] .uix-btn[data-loading]::after { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
|
|
111
|
+
}
|
|
@@ -36,3 +36,13 @@
|
|
|
36
36
|
.uix-collapsible--compact > .uix-collapsible__summary { padding: var(--uix-space-2) 0; border-radius: var(--uix-radius-sm); }
|
|
37
37
|
.uix-collapsible--compact > .uix-collapsible__body { padding: 0 0 var(--uix-space-3); }
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/* Inset (HAR-1818): a bordered, sunken well inside a card body for a preview or a specimen (a
|
|
41
|
+
rendered header, an image crop, a code sample). Not a nested card: no shadow, no card padding,
|
|
42
|
+
no card semantics. The consumer lays out what is inside. */
|
|
43
|
+
@layer uix.components {
|
|
44
|
+
.uix-inset { min-width: 0; padding: var(--uix-space-4); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-bg-subtle); overflow-wrap: anywhere; }
|
|
45
|
+
.uix-inset--sm { padding: var(--uix-space-3); }
|
|
46
|
+
/* the well on a sunken page (a bg-subtle panel) takes the surface instead, so it still reads as a well */
|
|
47
|
+
.uix-inset--surface { background: var(--uix-surface); }
|
|
48
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* uix change highlight — "this changed": the sibling of .uix-arrival for a value that changed in
|
|
2
|
+
place (a live update, a transition's result, a rolled-back optimistic change; HAR-2585, motion
|
|
3
|
+
standard §2.5). The element is tinted at once, holds a moment, and the tint fades back to the
|
|
4
|
+
element's own background. React's ChangeHighlight / useChangeHighlight set [data-uix-changed]
|
|
5
|
+
(never on the first render); plain markup adds .uix-changed (remove it, force a reflow and add
|
|
6
|
+
it again to replay). Depends only on --uix-* tokens. */
|
|
7
|
+
@layer uix.components {
|
|
8
|
+
.uix-change-highlight, .uix-changed {
|
|
9
|
+
/* opaque, so a pinned table cell never turns see-through while it is tinted */
|
|
10
|
+
--uix-change-tint: color-mix(in srgb, var(--uix-accent) 14%, var(--uix-surface));
|
|
11
|
+
--uix-change-ring: var(--uix-ring);
|
|
12
|
+
/* about 1.3 s: long enough to find, short enough to be gone before the next glance */
|
|
13
|
+
--uix-change-duration: calc(var(--uix-dur-slower) * 4);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* No fill-mode: the last frame is the element's own background, so a pill or a zebra row keeps
|
|
17
|
+
its colour once the tint is gone. */
|
|
18
|
+
@keyframes uix-changed { 0%, 40% { background-color: var(--uix-change-tint); } }
|
|
19
|
+
@keyframes uix-changed-ring { 0%, 40% { outline: 2px solid var(--uix-change-ring); outline-offset: 2px; } 100% { outline: 2px solid transparent; outline-offset: 2px; } }
|
|
20
|
+
/* reduced motion and forced colours: a steady outline for the same time, then nothing — just
|
|
21
|
+
inside the edge of a row or field, just outside a ringed pill (inside it would cover the label) */
|
|
22
|
+
@keyframes uix-changed-static-ring { 0% { outline: 2px solid var(--uix-change-ring); outline-offset: 2px; } 100% { outline: 2px solid transparent; outline-offset: 2px; } }
|
|
23
|
+
@keyframes uix-changed-static { 0% { outline: 2px solid var(--uix-change-ring); outline-offset: calc(-1 * var(--uix-space-1)); } 100% { outline: 2px solid transparent; outline-offset: calc(-1 * var(--uix-space-1)); } }
|
|
24
|
+
|
|
25
|
+
/* held, then an even fade: an ease-out would drain most of the tint before anyone looks */
|
|
26
|
+
.uix-changed, .uix-change-highlight[data-uix-changed] { animation: uix-changed var(--uix-change-duration) var(--uix-ease-in-out); }
|
|
27
|
+
/* the ring hugs what it wraps (a pill, a star): inline-flex, pill-round */
|
|
28
|
+
.uix-change-highlight--ring { display: inline-flex; vertical-align: middle; border-radius: var(--uix-radius-pill); }
|
|
29
|
+
.uix-change-highlight--ring[data-uix-changed], .uix-changed.uix-change-highlight--ring { animation-name: uix-changed-ring; }
|
|
30
|
+
/* a row tints its cells: cells paint over the row's own background */
|
|
31
|
+
tr.uix-changed, tr.uix-change-highlight[data-uix-changed] { animation: none; }
|
|
32
|
+
tr.uix-changed > :is(td, th), tr.uix-change-highlight[data-uix-changed] > :is(td, th) { animation: uix-changed var(--uix-change-duration) var(--uix-ease-in-out); }
|
|
33
|
+
|
|
34
|
+
@media (prefers-reduced-motion: reduce) {
|
|
35
|
+
/* !important: the kit's reduced-motion policy ends keyframes at once; this one is the signal.
|
|
36
|
+
A row is outlined as one box, not cell by cell. */
|
|
37
|
+
.uix-changed, .uix-change-highlight[data-uix-changed],
|
|
38
|
+
tr.uix-changed, tr.uix-change-highlight[data-uix-changed] { animation: uix-changed-static var(--uix-change-duration) step-end !important; }
|
|
39
|
+
.uix-change-highlight--ring[data-uix-changed] { animation: uix-changed-static-ring var(--uix-change-duration) step-end !important; }
|
|
40
|
+
tr.uix-changed > :is(td, th), tr.uix-change-highlight[data-uix-changed] > :is(td, th) { animation: none !important; }
|
|
41
|
+
}
|
|
42
|
+
@media (forced-colors: active) {
|
|
43
|
+
.uix-change-highlight, .uix-changed { --uix-change-ring: Highlight; }
|
|
44
|
+
.uix-changed, .uix-change-highlight[data-uix-changed],
|
|
45
|
+
tr.uix-changed, tr.uix-change-highlight[data-uix-changed] { animation: uix-changed-static var(--uix-change-duration) step-end; }
|
|
46
|
+
.uix-change-highlight--ring[data-uix-changed] { animation: uix-changed-static-ring var(--uix-change-duration) step-end; }
|
|
47
|
+
tr.uix-changed > :is(td, th), tr.uix-change-highlight[data-uix-changed] > :is(td, th) { animation: none; }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same outline */
|
|
52
|
+
@layer uix.components {
|
|
53
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
54
|
+
:root[data-motion="reduce"] :is(.uix-changed, .uix-change-highlight[data-uix-changed]) { animation: uix-changed-static var(--uix-change-duration) step-end !important; }
|
|
55
|
+
:root[data-motion="reduce"] .uix-change-highlight--ring[data-uix-changed] { animation: uix-changed-static-ring var(--uix-change-duration) step-end !important; }
|
|
56
|
+
:root[data-motion="reduce"] :is(tr.uix-changed, tr.uix-change-highlight[data-uix-changed]) > :is(td, th) { animation: none !important; }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -19,3 +19,21 @@
|
|
|
19
19
|
.uix-checkbox > input:focus-visible + .uix-checkbox__box { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
|
|
20
20
|
.uix-checkbox > input:disabled ~ * { opacity: .5; }
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
/* CheckboxGroup (HAR-1799): a fieldset whose legend names the boxes, with the field's message
|
|
24
|
+
slot. The UA fieldset chrome is stripped so it reads like a Field. `--uix-checkbox-group-columns`
|
|
25
|
+
(React `columns`) is a ceiling: a column never gets narrower than 10rem, so a phone gets one. */
|
|
26
|
+
@layer uix.components {
|
|
27
|
+
.uix-checkbox-group { display: flex; flex-direction: column; gap: var(--uix-space-2); min-width: 0; min-inline-size: auto; margin: 0; padding: 0; border: 0; }
|
|
28
|
+
.uix-checkbox-group__legend { padding: 0; margin: 0 0 var(--uix-space-2); font-size: var(--uix-text-meta); font-weight: 600; color: var(--uix-text); overflow-wrap: anywhere; }
|
|
29
|
+
.uix-checkbox-group__legend[data-required]::after { content: " *"; color: var(--uix-danger-text); }
|
|
30
|
+
.uix-checkbox-group__options {
|
|
31
|
+
--uix-checkbox-group-columns: 1;
|
|
32
|
+
display: grid; gap: var(--uix-space-2) var(--uix-space-5); align-items: start;
|
|
33
|
+
grid-template-columns: repeat(auto-fill, minmax(min(100%, max(10rem, calc((100% - (var(--uix-checkbox-group-columns) - 1) * var(--uix-space-5)) / var(--uix-checkbox-group-columns)))), 1fr));
|
|
34
|
+
}
|
|
35
|
+
.uix-checkbox-group__options > .uix-checkbox { align-items: flex-start; min-width: 0; overflow-wrap: anywhere; }
|
|
36
|
+
/* the box sits on the first line of a label that wraps */
|
|
37
|
+
.uix-checkbox-group__options > .uix-checkbox > .uix-checkbox__box { translate: 0 calc((1lh - 18px) / 2); }
|
|
38
|
+
.uix-checkbox-group[data-invalid] .uix-checkbox__box { border-color: var(--uix-danger); }
|
|
39
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* uix code display (HAR-1253) — a one-time code shown large: React CodeDisplay.
|
|
2
|
+
Groups wrap as whole groups on a narrow screen; a single group too long for the line breaks
|
|
3
|
+
between characters rather than leaving the page. The expired state is said in words (the meta
|
|
4
|
+
line) and the code is struck through, so it never rests on colour alone. */
|
|
5
|
+
@layer uix.components {
|
|
6
|
+
.uix-code-display { display: inline-grid; justify-items: start; gap: var(--uix-space-2); max-width: 100%; min-width: 0; }
|
|
7
|
+
.uix-code-display__label { color: var(--uix-text-muted); font-size: var(--uix-text-meta); font-weight: 500; }
|
|
8
|
+
.uix-code-display__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--uix-space-2) var(--uix-space-3); max-width: 100%; min-width: 0; }
|
|
9
|
+
.uix-code-display__code {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-wrap: wrap;
|
|
12
|
+
align-items: baseline;
|
|
13
|
+
min-width: 0;
|
|
14
|
+
max-width: 100%;
|
|
15
|
+
color: var(--uix-text);
|
|
16
|
+
font-family: var(--uix-font-mono);
|
|
17
|
+
font-size: var(--uix-text-data-hero);
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
line-height: var(--uix-leading-data-hero);
|
|
20
|
+
font-variant-numeric: tabular-nums slashed-zero;
|
|
21
|
+
letter-spacing: .06em;
|
|
22
|
+
}
|
|
23
|
+
/* lg is the hero of a card or a till screen: it takes the width it is given and fits a
|
|
24
|
+
three-by-three code (11 characters with the separators, ~8.4em of mono at this tracking) on
|
|
25
|
+
one line, down to the h2 size; only below that do the groups wrap (review of PR #166: in a
|
|
26
|
+
card at 320–360 px the last group fell to its own line). */
|
|
27
|
+
.uix-code-display--lg { display: grid; inline-size: 100%; container-type: inline-size; }
|
|
28
|
+
.uix-code-display--lg .uix-code-display__code { font-size: clamp(var(--uix-text-h2), calc(100cqi / 8.4), var(--uix-text-display)); line-height: var(--uix-leading-display); }
|
|
29
|
+
.uix-code-display__group { min-width: 0; overflow-wrap: anywhere; }
|
|
30
|
+
.uix-code-display__sep { margin-inline: .2em; color: var(--uix-text-hushed); font-weight: 400; }
|
|
31
|
+
/* the spoken copy is for screen readers only; a pointer selection of the code never picks it up */
|
|
32
|
+
.uix-code-display__spoken { user-select: none; }
|
|
33
|
+
.uix-code-display__meta { color: var(--uix-text-muted); font-size: var(--uix-text-meta); font-variant-numeric: tabular-nums; }
|
|
34
|
+
|
|
35
|
+
/* the last minute (warnBefore): the countdown turns to a warning; nothing moves per tick */
|
|
36
|
+
.uix-code-display[data-state="soon"] .uix-code-display__meta { color: var(--uix-warning-text); font-weight: 600; }
|
|
37
|
+
.uix-code-display[data-state="expired"] .uix-code-display__code { color: var(--uix-text-hushed); text-decoration: line-through; text-decoration-thickness: .06em; }
|
|
38
|
+
.uix-code-display[data-state="expired"] .uix-code-display__meta { color: var(--uix-danger-text); font-weight: 600; }
|
|
39
|
+
|
|
40
|
+
@media print {
|
|
41
|
+
.uix-code-display__code { color: var(--uix-print-ink); }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
@layer uix.components {
|
|
3
3
|
.uix-combobox { position: relative; }
|
|
4
4
|
.uix-combobox > .uix-input { width: 100%; }
|
|
5
|
-
|
|
5
|
+
/* a static list right under the field (plain HTML); the React Combobox draws its list in a
|
|
6
|
+
top-layer popup instead (HAR-1588), styled as Select's */
|
|
7
|
+
.uix-combobox > .uix-combobox__listbox { position: absolute; z-index: var(--uix-z-dropdown); inset-inline: 0; top: calc(100% + var(--uix-space-1)); max-height: 240px; overflow-y: auto; }
|
|
8
|
+
.uix-combobox__popup { max-width: min(32rem, calc(100vw - 2 * var(--uix-space-4))); }
|
|
6
9
|
.uix-listbox {
|
|
7
10
|
list-style: none; margin: 0; padding: var(--uix-space-1); min-width: 200px;
|
|
8
11
|
background: var(--uix-surface-2); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* uix command palette (⌘K) — inner surface; mounts in a dialog/popover (overlay mechanics in motion + overlays). */
|
|
2
2
|
@layer uix.components {
|
|
3
|
-
.uix-cmdk { width: min(560px, 92vw); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-lg); background: var(--uix-surface-2); box-shadow: var(--uix-shadow-overlay); overflow: hidden; }
|
|
3
|
+
.uix-cmdk { width: min(560px, 92vw); max-width: 100%; border: 1px solid var(--uix-border); border-radius: var(--uix-radius-lg); background: var(--uix-surface-2); box-shadow: var(--uix-shadow-overlay); overflow: hidden; }
|
|
4
4
|
.uix-cmdk__input { width: 100%; height: 48px; border: 0; border-bottom: 1px solid var(--uix-border); background: transparent; color: var(--uix-text); font: inherit; font-size: var(--uix-text-h3); padding: 0 var(--uix-space-4); }
|
|
5
5
|
.uix-cmdk__input:focus { outline: none; }
|
|
6
6
|
/* visible focus replacement for the suppressed outline (UIX-A11Y-1): the divider under the
|
|
@@ -10,8 +10,22 @@
|
|
|
10
10
|
.uix-cmdk__group { font-size: var(--uix-text-eyebrow); letter-spacing: var(--uix-tracking-eyebrow); text-transform: uppercase; color: var(--uix-text-muted); padding: var(--uix-space-2) var(--uix-space-3) var(--uix-space-1); }
|
|
11
11
|
.uix-cmdk__item { display: flex; align-items: center; gap: var(--uix-space-3); padding: var(--uix-space-3) var(--uix-space-3); border-radius: var(--uix-radius-sm); color: var(--uix-text); cursor: pointer; font-size: var(--uix-text-body); }
|
|
12
12
|
.uix-cmdk__item svg { width: var(--uix-icon-md); height: var(--uix-icon-md); color: var(--uix-text-muted); }
|
|
13
|
-
.uix-cmdk__item
|
|
13
|
+
.uix-cmdk__item[data-active] { background: var(--uix-bg-hover); }
|
|
14
|
+
/* items mode moves the active row with the pointer, so a hover tint there would mark a second row */
|
|
15
|
+
@media (hover: hover) and (pointer: fine) { .uix-cmdk:not([data-items]) .uix-cmdk__item:hover { background: var(--uix-bg-hover); } }
|
|
14
16
|
.uix-cmdk__item .uix-kbd { margin-left: auto; }
|
|
17
|
+
/* items mode (HAR-1378): label + description, matched text, the shortcut at the end, empty state */
|
|
18
|
+
.uix-cmdk__icon { display: inline-flex; flex: none; }
|
|
19
|
+
.uix-cmdk__text { display: grid; flex: 1; min-width: 0; gap: calc(var(--uix-space-1) / 2); }
|
|
20
|
+
.uix-cmdk__label { overflow-wrap: anywhere; }
|
|
21
|
+
.uix-cmdk__desc { font-size: var(--uix-text-meta); color: var(--uix-text-muted); overflow-wrap: anywhere; }
|
|
22
|
+
.uix-cmdk__mark { background: transparent; color: inherit; font-weight: 600; }
|
|
23
|
+
.uix-cmdk__shortcut { flex: none; margin-left: auto; }
|
|
24
|
+
.uix-cmdk__item[aria-disabled="true"] { color: var(--uix-text-muted); cursor: default; }
|
|
25
|
+
.uix-cmdk__empty { margin: 0; padding: var(--uix-space-6) var(--uix-space-4); text-align: center; color: var(--uix-text-muted); font-size: var(--uix-text-body); }
|
|
26
|
+
/* the palette's own dialog: the palette is the visible box, the dialog only hosts it */
|
|
27
|
+
.uix-dialog.uix-cmdk-dialog { width: auto; max-width: min(560px, 92vw); margin: 12vh auto auto; padding: 0; border: 0; background: transparent; box-shadow: none; overflow: visible; }
|
|
28
|
+
.uix-cmdk-dialog .uix-cmdk { width: min(560px, 92vw); }
|
|
15
29
|
/* ⌘K is keyboard-triggered and high-frequency → open instantly, no animation (Emil's rule) */
|
|
16
30
|
dialog[data-uix-cmdk-dialog], dialog[data-uix-cmdk-dialog]::backdrop { transition: none; }
|
|
17
31
|
}
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
/* uix contact / customer card (CRM) — depends only on --uix-* tokens. */
|
|
1
|
+
/* uix contact / customer card (CRM) — depends only on --uix-* tokens. React: ContactCard (HAR-1379). */
|
|
2
2
|
@layer uix.components {
|
|
3
|
-
.uix-contact { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--uix-space-1); padding: var(--uix-space-5); }
|
|
4
|
-
|
|
5
|
-
.uix-
|
|
6
|
-
.uix-
|
|
7
|
-
.uix-
|
|
3
|
+
.uix-contact { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--uix-space-1); padding: var(--uix-space-5); min-width: 0; }
|
|
4
|
+
/* bare: inside a surface that already has its padding (a card body, a peek, a popover) */
|
|
5
|
+
.uix-contact--bare { padding: 0; }
|
|
6
|
+
.uix-contact__name { font-size: var(--uix-text-h3); font-weight: 600; margin: var(--uix-space-2) 0 0; max-width: 100%; overflow-wrap: anywhere; }
|
|
7
|
+
.uix-contact__role { font-size: var(--uix-text-meta); color: var(--uix-text-muted); max-width: 100%; overflow-wrap: anywhere; }
|
|
8
|
+
.uix-contact__actions { display: flex; flex-wrap: wrap; gap: var(--uix-space-2); margin-top: var(--uix-space-3); align-self: stretch; }
|
|
9
|
+
.uix-contact__actions > .uix-btn { flex: 1 1 7rem; }
|
|
8
10
|
.uix-contact__stats { display: flex; gap: var(--uix-space-5); margin-top: var(--uix-space-4); }
|
|
9
11
|
.uix-contact__stat { text-align: center; }
|
|
10
12
|
.uix-contact__stat b { display: block; font-size: var(--uix-text-h3); }
|
|
11
13
|
.uix-contact__stat span { font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
|
|
14
|
+
|
|
15
|
+
/* the address and number as text, selectable; a long address breaks rather than widening the card */
|
|
16
|
+
.uix-contact__details { display: flex; flex-direction: column; gap: var(--uix-space-1); margin-top: var(--uix-space-2); max-width: 100%; font-size: var(--uix-text-meta); }
|
|
17
|
+
.uix-contact__detail { overflow-wrap: anywhere; }
|
|
18
|
+
/* an erased or unavailable contact: a neutral placeholder and a line that says why */
|
|
19
|
+
.uix-contact__placeholder { background: var(--uix-bg-subtle); color: var(--uix-text-muted); border: 1px solid var(--uix-border); }
|
|
20
|
+
.uix-contact__placeholder svg { width: 55%; height: 55%; }
|
|
21
|
+
.uix-contact__note { color: var(--uix-text-muted); font-size: var(--uix-text-meta); font-style: italic; }
|
|
22
|
+
.uix-contact[data-state="purged"] .uix-contact__name { color: var(--uix-text-muted); font-weight: 500; }
|
|
12
23
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* uix count — a number that ticks once when it changes (HAR-2587, motion standard §2.8): a badge,
|
|
2
|
+
a stat value, an unread total. React's Count sets [data-uix-tick="up" | "down"] after a change,
|
|
3
|
+
never on the first render: the new figure comes in 4 px from below (up) or above (down) with a
|
|
4
|
+
fade. One tick, never a loop, never a count-up. Depends only on --uix-* tokens. */
|
|
5
|
+
@layer uix.components {
|
|
6
|
+
.uix-count { display: inline-block; font-variant-numeric: tabular-nums; }
|
|
7
|
+
@keyframes uix-count-up { from { opacity: .3; transform: translateY(var(--uix-lift)); } }
|
|
8
|
+
@keyframes uix-count-down { from { opacity: .3; transform: translateY(calc(-1 * var(--uix-lift))); } }
|
|
9
|
+
@keyframes uix-count-fade { from { opacity: .3; } }
|
|
10
|
+
.uix-count[data-uix-tick="up"] { animation: uix-count-up var(--uix-dur) var(--uix-ease-out); }
|
|
11
|
+
.uix-count[data-uix-tick="down"] { animation: uix-count-down var(--uix-dur) var(--uix-ease-out); }
|
|
12
|
+
@media (prefers-reduced-motion: reduce) {
|
|
13
|
+
/* no travel; the fade stays (!important: the reduced-motion policy ends keyframes at once) */
|
|
14
|
+
.uix-count[data-uix-tick="up"], .uix-count[data-uix-tick="down"] { animation: uix-count-fade var(--uix-dur) var(--uix-ease-out) !important; }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same fade */
|
|
19
|
+
@layer uix.components {
|
|
20
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
21
|
+
:root[data-motion="reduce"] .uix-count:is([data-uix-tick="up"], [data-uix-tick="down"]) { animation: uix-count-fade var(--uix-dur) var(--uix-ease-out) !important; }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* uix data workspace — the generic presentation half of a product's data table (React
|
|
2
|
+
`DataWorkspace`, HAR-1388, ADR-0005): toolbar · bulk bar · table · footer, composed from
|
|
3
|
+
.uix-toolbar, .uix-chip, .uix-searchbar, .uix-bulkbar, .uix-table and .uix-pagination.
|
|
4
|
+
Depends only on --uix-* tokens. */
|
|
5
|
+
@layer uix.components {
|
|
6
|
+
.uix-data-workspace { display: grid; gap: var(--uix-space-3); min-width: 0; }
|
|
7
|
+
.uix-data-workspace > .uix-toolbar { margin-bottom: 0; }
|
|
8
|
+
.uix-data-workspace__search { flex: 0 1 18rem; min-width: min(12rem, 100%); }
|
|
9
|
+
.uix-data-workspace__chip, .uix-data-workspace__menu { display: inline-flex; }
|
|
10
|
+
/* the row-actions column takes the width of its buttons, not the table's 84px */
|
|
11
|
+
.uix-data-workspace .uix-table__actions { width: 1%; }
|
|
12
|
+
/* with selection, the id column is frozen too, right after the checkbox column */
|
|
13
|
+
.uix-data-workspace__grid--pin-id :is(th, td):nth-child(2) { position: sticky; left: var(--uix-selcol-w); z-index: var(--uix-z-raised); box-shadow: 1px 0 0 var(--uix-border); }
|
|
14
|
+
.uix-data-workspace__grid--pin-id :is(th, td):first-child { box-shadow: none; }
|
|
15
|
+
/* A squeezed table shrinks the checkbox column to its content (30px), which would open a gap
|
|
16
|
+
before the id frozen at --uix-selcol-w: an empty, 0-high block holds the column at that width
|
|
17
|
+
(the checkbox itself stays as it is, centred, its input over its box). */
|
|
18
|
+
.uix-data-workspace__grid--pin-id :is(th, td):first-child::after { content: ""; display: block; inline-size: calc(var(--uix-selcol-w) - var(--uix-space-3)); }
|
|
19
|
+
.uix-data-workspace__grid--pin-id thead th:nth-child(2) { z-index: var(--uix-z-sticky); background: var(--uix-surface); }
|
|
20
|
+
.uix-data-workspace__grid--pin-id tbody tr td:nth-child(2) { background: var(--uix-row); transition: background-color var(--uix-dur-fast) var(--uix-ease-out); }
|
|
21
|
+
.uix-data-workspace__grid--pin-id:not(.uix-table--no-zebra) tbody tr:nth-child(even) td:nth-child(2) { background: var(--uix-row-alt); }
|
|
22
|
+
.uix-data-workspace__grid--pin-id tbody tr[data-pinned] td:nth-child(2) { background: linear-gradient(var(--uix-row-pinned-bg), var(--uix-row-pinned-bg)) var(--uix-surface); }
|
|
23
|
+
.uix-data-workspace__grid--pin-id tbody tr[data-selected] td:nth-child(2) { background: linear-gradient(var(--uix-row-selected-bg), var(--uix-row-selected-bg)) var(--uix-surface); }
|
|
24
|
+
.uix-data-workspace__grid--pin-id tbody tr:hover td:nth-child(2) { background: var(--uix-bg-hover); }
|
|
25
|
+
.uix-table-wrap[data-overflow="false"] .uix-data-workspace__grid--pin-id :is(th, td):nth-child(2) { box-shadow: none; }
|
|
26
|
+
@media (prefers-reduced-motion: reduce) { .uix-data-workspace__grid--pin-id tbody tr td:nth-child(2) { transition: none; } }
|
|
27
|
+
/* the record-id cell: the id opens a peek, ↗ opens the full record */
|
|
28
|
+
.uix-data-workspace__id { display: inline-flex; align-items: center; gap: var(--uix-space-1); white-space: nowrap; }
|
|
29
|
+
.uix-data-workspace__open { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: var(--uix-radius-xs); color: var(--uix-text-muted); }
|
|
30
|
+
.uix-data-workspace__open:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
|
|
31
|
+
@media (hover: hover) and (pointer: fine) { .uix-data-workspace__open:hover { background: var(--uix-bg-hover); color: var(--uix-text); } }
|
|
32
|
+
.uix-data-workspace__footer { display: flex; flex-wrap: wrap; align-items: center; gap: var(--uix-space-2) var(--uix-space-4); color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
|
|
33
|
+
.uix-data-workspace__range { font-variant-numeric: tabular-nums; }
|
|
34
|
+
.uix-data-workspace__size { display: inline-flex; align-items: center; gap: var(--uix-space-2); white-space: nowrap; }
|
|
35
|
+
/* maximized: the workspace takes the window; Escape or the toggle restores it */
|
|
36
|
+
.uix-data-workspace--maximized {
|
|
37
|
+
position: fixed; inset: 0; z-index: var(--uix-z-overlay); align-content: start; overflow: auto;
|
|
38
|
+
padding: var(--uix-space-4); background: var(--uix-bg-app);
|
|
39
|
+
}
|
|
40
|
+
/* a phone: the search takes its own row */
|
|
41
|
+
@media (max-width: 40rem) { .uix-data-workspace__search { flex: 1 1 100%; } }
|
|
42
|
+
}
|
|
@@ -47,10 +47,23 @@
|
|
|
47
47
|
/* --- DateTimePicker: the date field, a time input and the zone they are in ---------------- */
|
|
48
48
|
.uix-date-time-picker { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--uix-space-2); min-width: 0; }
|
|
49
49
|
.uix-date-time-picker > .uix-date-picker { flex: 1 1 8.5rem; }
|
|
50
|
-
|
|
50
|
+
/* the time part is a TimePicker (HAR-1835): narrower than the date, never under a "12:30 PM" */
|
|
51
|
+
.uix-date-time-picker > .uix-date-time-picker__time { flex: 0 1 8.5rem; min-width: 7rem; }
|
|
51
52
|
.uix-date-time-picker__zone { display: inline-flex; align-items: center; min-height: var(--uix-control-h); color: var(--uix-text-muted); font-size: var(--uix-text-meta); white-space: nowrap; }
|
|
52
53
|
.uix-date-time-picker--sm .uix-date-time-picker__zone { min-height: 28px; }
|
|
53
54
|
|
|
55
|
+
/* --- TimePicker (HAR-1835): the date field's shape, a clock button and a list of times ------- */
|
|
56
|
+
.uix-time-picker { max-width: 9rem; }
|
|
57
|
+
.uix-time-picker__input { text-overflow: ellipsis; }
|
|
58
|
+
.uix-time-picker__popover { padding: var(--uix-space-1); }
|
|
59
|
+
.uix-time-picker__list {
|
|
60
|
+
min-width: 8rem; max-height: min(16rem, 50vh); overflow-y: auto; overscroll-behavior: contain;
|
|
61
|
+
padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none; font-variant-numeric: tabular-nums;
|
|
62
|
+
}
|
|
63
|
+
.uix-time-picker__list:focus { outline: none; }
|
|
64
|
+
/* the list has focus, its active time is the ring (aria-activedescendant) */
|
|
65
|
+
.uix-time-picker__list:focus-visible .uix-listbox__option[data-active] { outline: 2px solid var(--uix-ring); outline-offset: -2px; }
|
|
66
|
+
|
|
54
67
|
/* --- DateRangePicker mode="field": a trigger that shows the range ------------------------- */
|
|
55
68
|
.uix-date-range-field { display: grid; width: 100%; min-width: 0; max-width: 18rem; }
|
|
56
69
|
.uix-date-range-field__trigger { display: flex; align-items: center; gap: var(--uix-space-2); min-width: 0; text-align: start; cursor: pointer; }
|
|
@@ -13,3 +13,12 @@
|
|
|
13
13
|
.uix-detail-page__metrics dd { margin: var(--uix-space-1) 0 0; color: var(--uix-text); font-size: var(--uix-text-h3); font-weight: 600; }
|
|
14
14
|
@media (max-width: 820px) { .uix-detail { grid-template-columns: 1fr; } }
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
/* Staff preset (HAR-1815): the record page TENSOR and MOTUS share. */
|
|
18
|
+
@layer uix.components {
|
|
19
|
+
.uix-detail-page--staff .uix-page-header__title { font-size: var(--uix-text-h2); line-height: var(--uix-leading-h2); }
|
|
20
|
+
.uix-detail-page--staff .uix-detail-page__back { display: inline-flex; align-items: center; gap: var(--uix-space-1); color: var(--uix-text-hushed); text-decoration: none; }
|
|
21
|
+
@media (hover: hover) { .uix-detail-page--staff .uix-detail-page__back:hover { color: var(--uix-text); } }
|
|
22
|
+
.uix-detail-page--staff .uix-detail-page__metrics dt { font-size: var(--uix-text-eyebrow); line-height: var(--uix-leading-eyebrow); letter-spacing: var(--uix-tracking-eyebrow); text-transform: uppercase; font-weight: 600; color: var(--uix-text-muted); }
|
|
23
|
+
.uix-detail-page--staff .uix-detail-page__metrics dd { font-size: var(--uix-text-h3); line-height: var(--uix-leading-h3); font-weight: 400; font-variant-numeric: tabular-nums; }
|
|
24
|
+
}
|
|
@@ -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
|
+
}
|