@tensor_1/tokens 2.48.0 → 2.50.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/build/css/tokens.css +4 -0
- package/build/ts/tokens.d.ts +1 -1
- package/build/ts/tokens.js +8 -0
- package/package.json +1 -1
- package/styles/components/card.css +12 -0
- package/styles/components/data-workspace.css +106 -6
- package/styles/components/drag.css +63 -0
- package/styles/components/drawer.css +6 -5
- package/styles/components/kanban.css +10 -1
- package/styles/components/kbd.css +25 -0
- package/styles/components/list-motion.css +27 -0
- package/styles/components/modal.css +6 -5
- package/styles/components/peek.css +6 -5
- package/styles/components/popover.css +10 -10
- package/styles/components/relationship-graph.css +32 -11
- package/styles/components/search-suggest.css +6 -6
- package/styles/components/segmented.css +21 -0
- package/styles/components/select.css +9 -2
- package/styles/components/states.css +45 -0
- package/styles/components/table.css +35 -24
- package/styles/components/tabs.css +24 -0
- package/styles/components/toast.css +18 -15
- package/styles/components/tooltip.css +7 -3
- package/styles/motion.css +5 -4
- package/tokens/base/effect.json +6 -0
package/build/css/tokens.css
CHANGED
|
@@ -120,6 +120,10 @@
|
|
|
120
120
|
--uix-dur-exit: 110ms;
|
|
121
121
|
--uix-dur-exit-slow: 160ms;
|
|
122
122
|
--uix-dur-highlight: 2400ms;
|
|
123
|
+
--uix-dur-change: 1200ms;
|
|
124
|
+
--uix-delay-pending: 300ms;
|
|
125
|
+
--uix-hold-pending: 500ms;
|
|
126
|
+
--uix-hold-result: 1500ms;
|
|
123
127
|
--uix-delay-tooltip: 400ms;
|
|
124
128
|
--uix-delay-tooltip-skip: 300ms;
|
|
125
129
|
--uix-loop-spin: 600ms;
|
package/build/ts/tokens.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* GENERATED — do not edit. */
|
|
2
|
-
export type UixTokenName = "bg-app" | "bg-subtle" | "bg-hover" | "bg-active" | "surface" | "surface-2" | "surface-3" | "border" | "border-strong" | "text" | "text-hushed" | "text-muted" | "text-reverse" | "text-hushed-reverse" | "accent" | "accent-dark" | "accent-light" | "accent-hover" | "accent-fg" | "link" | "accent-text" | "ring" | "brand-muted" | "success" | "success-fg" | "success-bg" | "success-text" | "warning" | "warning-fg" | "warning-bg" | "warning-text" | "warning-border" | "info" | "info-fg" | "info-bg" | "info-text" | "danger" | "danger-fg" | "danger-bg" | "danger-border" | "danger-text" | "amber" | "amber-text" | "amber-bg" | "attention" | "attention-bg" | "attention-text" | "attention-solid" | "overdue" | "overdue-bg" | "overdue-text" | "overdue-solid" | "sev-4" | "sev-4-bg" | "sev-4-text" | "sev-5" | "sev-5-bg" | "sev-5-text" | "success-solid" | "warning-solid" | "info-solid" | "danger-solid" | "neutral-solid" | "neutral-text" | "neutral-bg" | "neutral-border" | "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "chart-6" | "chart-7" | "chart-8" | "chart-neutral" | "chart-grid" | "chart-axis" | "chart-reference" | "chart-zone-warning" | "chart-zone-danger" | "chart-forecast-band" | "chart-event" | "chart-partial" | "chart-comparison" | "row" | "row-alt" | "row-pinned-bg" | "row-selected-bg" | "scrollbar-thumb" | "scrollbar-thumb-hover" | "scrollbar-track" | "scrim" | "scrim-strong" | "print-ink" | "print-ink-quiet" | "print-paper" | "print-paper-quiet" | "print-line" | "print-danger" | "shadow-sm" | "shadow-md" | "shadow-popover" | "shadow-overlay" | "highlight-top" | "ease-out" | "ease-out-strong" | "ease-in" | "ease-in-out" | "ease-spring" | "dur-fast" | "dur" | "dur-slow" | "dur-slower" | "dur-exit-fast" | "dur-exit" | "dur-exit-slow" | "dur-highlight" | "delay-tooltip" | "delay-tooltip-skip" | "loop-spin" | "loop-progress" | "loop-shimmer" | "loop-ping" | "loop-blink" | "loop-pulse" | "lift" | "z-raised" | "z-sticky" | "z-dropdown" | "z-overlay" | "z-tooltip" | "z-toast" | "radius-xs" | "radius-sm" | "radius-md" | "radius-lg" | "radius-xl" | "radius-pill" | "row-h" | "row-h-compact" | "row-h-comfortable" | "control-h" | "sidebar-w" | "sidebar-w-rail" | "topbar-h" | "scrollbar-size" | "icon-sm" | "icon-md" | "icon-lg" | "peek-w" | "col-w-xs" | "col-w-sm" | "col-w-md" | "col-w-lg" | "col-w-xl" | "col-title-min" | "space-0" | "space-1" | "space-2" | "space-3" | "space-4" | "space-5" | "space-6" | "space-7" | "space-8" | "space-9" | "space-10" | "space-11" | "space-12" | "font-sans" | "font-heading" | "font-mono" | "text-display" | "leading-display" | "text-data-hero" | "leading-data-hero" | "text-h2" | "leading-h2" | "text-h3" | "leading-h3" | "text-body" | "leading-body" | "text-meta" | "leading-meta" | "text-eyebrow" | "leading-eyebrow" | "tracking-tight" | "tracking-eyebrow";
|
|
2
|
+
export type UixTokenName = "bg-app" | "bg-subtle" | "bg-hover" | "bg-active" | "surface" | "surface-2" | "surface-3" | "border" | "border-strong" | "text" | "text-hushed" | "text-muted" | "text-reverse" | "text-hushed-reverse" | "accent" | "accent-dark" | "accent-light" | "accent-hover" | "accent-fg" | "link" | "accent-text" | "ring" | "brand-muted" | "success" | "success-fg" | "success-bg" | "success-text" | "warning" | "warning-fg" | "warning-bg" | "warning-text" | "warning-border" | "info" | "info-fg" | "info-bg" | "info-text" | "danger" | "danger-fg" | "danger-bg" | "danger-border" | "danger-text" | "amber" | "amber-text" | "amber-bg" | "attention" | "attention-bg" | "attention-text" | "attention-solid" | "overdue" | "overdue-bg" | "overdue-text" | "overdue-solid" | "sev-4" | "sev-4-bg" | "sev-4-text" | "sev-5" | "sev-5-bg" | "sev-5-text" | "success-solid" | "warning-solid" | "info-solid" | "danger-solid" | "neutral-solid" | "neutral-text" | "neutral-bg" | "neutral-border" | "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "chart-6" | "chart-7" | "chart-8" | "chart-neutral" | "chart-grid" | "chart-axis" | "chart-reference" | "chart-zone-warning" | "chart-zone-danger" | "chart-forecast-band" | "chart-event" | "chart-partial" | "chart-comparison" | "row" | "row-alt" | "row-pinned-bg" | "row-selected-bg" | "scrollbar-thumb" | "scrollbar-thumb-hover" | "scrollbar-track" | "scrim" | "scrim-strong" | "print-ink" | "print-ink-quiet" | "print-paper" | "print-paper-quiet" | "print-line" | "print-danger" | "shadow-sm" | "shadow-md" | "shadow-popover" | "shadow-overlay" | "highlight-top" | "ease-out" | "ease-out-strong" | "ease-in" | "ease-in-out" | "ease-spring" | "dur-fast" | "dur" | "dur-slow" | "dur-slower" | "dur-exit-fast" | "dur-exit" | "dur-exit-slow" | "dur-highlight" | "dur-change" | "delay-pending" | "hold-pending" | "hold-result" | "delay-tooltip" | "delay-tooltip-skip" | "loop-spin" | "loop-progress" | "loop-shimmer" | "loop-ping" | "loop-blink" | "loop-pulse" | "lift" | "z-raised" | "z-sticky" | "z-dropdown" | "z-overlay" | "z-tooltip" | "z-toast" | "radius-xs" | "radius-sm" | "radius-md" | "radius-lg" | "radius-xl" | "radius-pill" | "row-h" | "row-h-compact" | "row-h-comfortable" | "control-h" | "sidebar-w" | "sidebar-w-rail" | "topbar-h" | "scrollbar-size" | "icon-sm" | "icon-md" | "icon-lg" | "peek-w" | "col-w-xs" | "col-w-sm" | "col-w-md" | "col-w-lg" | "col-w-xl" | "col-title-min" | "space-0" | "space-1" | "space-2" | "space-3" | "space-4" | "space-5" | "space-6" | "space-7" | "space-8" | "space-9" | "space-10" | "space-11" | "space-12" | "font-sans" | "font-heading" | "font-mono" | "text-display" | "leading-display" | "text-data-hero" | "leading-data-hero" | "text-h2" | "leading-h2" | "text-h3" | "leading-h3" | "text-body" | "leading-body" | "text-meta" | "leading-meta" | "text-eyebrow" | "leading-eyebrow" | "tracking-tight" | "tracking-eyebrow";
|
|
3
3
|
export declare const cssVar: Record<UixTokenName, string>;
|
|
4
4
|
export declare const light: Record<UixTokenName, string>;
|
|
5
5
|
export declare const dark: Partial<Record<UixTokenName, string>>;
|
package/build/ts/tokens.js
CHANGED
|
@@ -121,6 +121,10 @@ export const cssVar = {
|
|
|
121
121
|
"dur-exit": "var(--uix-dur-exit)",
|
|
122
122
|
"dur-exit-slow": "var(--uix-dur-exit-slow)",
|
|
123
123
|
"dur-highlight": "var(--uix-dur-highlight)",
|
|
124
|
+
"dur-change": "var(--uix-dur-change)",
|
|
125
|
+
"delay-pending": "var(--uix-delay-pending)",
|
|
126
|
+
"hold-pending": "var(--uix-hold-pending)",
|
|
127
|
+
"hold-result": "var(--uix-hold-result)",
|
|
124
128
|
"delay-tooltip": "var(--uix-delay-tooltip)",
|
|
125
129
|
"delay-tooltip-skip": "var(--uix-delay-tooltip-skip)",
|
|
126
130
|
"loop-spin": "var(--uix-loop-spin)",
|
|
@@ -312,6 +316,10 @@ export const light = {
|
|
|
312
316
|
"dur-exit": "110ms",
|
|
313
317
|
"dur-exit-slow": "160ms",
|
|
314
318
|
"dur-highlight": "2400ms",
|
|
319
|
+
"dur-change": "1200ms",
|
|
320
|
+
"delay-pending": "300ms",
|
|
321
|
+
"hold-pending": "500ms",
|
|
322
|
+
"hold-result": "1500ms",
|
|
315
323
|
"delay-tooltip": "400ms",
|
|
316
324
|
"delay-tooltip-skip": "300ms",
|
|
317
325
|
"loop-spin": "600ms",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tensor_1/tokens",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.50.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": [
|
|
@@ -24,6 +24,18 @@
|
|
|
24
24
|
.uix-collapsible__summary:hover .uix-collapsible__chevron { color: var(--uix-text); }
|
|
25
25
|
.uix-collapsible[open] .uix-collapsible__chevron { transform: rotate(180deg); }
|
|
26
26
|
.uix-collapsible__body { padding: 0 var(--uix-space-5) var(--uix-space-5); }
|
|
27
|
+
/* Opening (HAR-2589): the body fades in and settles 4 px down into place. A transition on
|
|
28
|
+
::details-content, not @starting-style, so a section that is already open when the page
|
|
29
|
+
loads does not animate; opening from the keyboard (the summary is :focus-visible) is
|
|
30
|
+
instant too, and so is closing. Height never animates (the motion contract forbids layout
|
|
31
|
+
animation): what follows moves at once and the eye follows the fade. Browsers without
|
|
32
|
+
::details-content simply open. Reduced motion drops the 4 px and keeps the fade. */
|
|
33
|
+
.uix-collapsible::details-content { opacity: 0; translate: 0 calc(-1 * var(--uix-space-1)); }
|
|
34
|
+
.uix-collapsible[open]::details-content {
|
|
35
|
+
opacity: 1; translate: none;
|
|
36
|
+
transition: opacity var(--uix-dur) var(--uix-ease-out), translate var(--uix-dur) var(--uix-ease-out);
|
|
37
|
+
}
|
|
38
|
+
.uix-collapsible[open]:has(> summary:focus-visible)::details-content { transition: none; }
|
|
27
39
|
}
|
|
28
40
|
|
|
29
41
|
/* CollapsibleSection `headingLevel` and `compact` (HAR-1628). The heading only adds the outline
|
|
@@ -17,13 +17,11 @@
|
|
|
17
17
|
(the checkbox itself stays as it is, centred, its input over its box). */
|
|
18
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
19
|
.uix-data-workspace__grid--pin-id thead th:nth-child(2) { z-index: var(--uix-z-sticky); background: var(--uix-surface); }
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
.uix-data-workspace__grid--pin-id tbody tr
|
|
23
|
-
.uix-data-workspace__grid--pin-id tbody tr
|
|
24
|
-
.uix-data-workspace__grid--pin-id tbody tr:hover td:nth-child(2) { background: var(--uix-bg-hover); }
|
|
20
|
+
/* the row's state (--uix-row-bg, table.css) over the surface: opaque, and the same as the row
|
|
21
|
+
(a zebra rule here outranked selected on every even row: operator review 2026-10-23) */
|
|
22
|
+
.uix-data-workspace__grid--pin-id tbody tr td:nth-child(2) { background: linear-gradient(var(--uix-row-bg), var(--uix-row-bg)), var(--uix-surface); }
|
|
23
|
+
@media (forced-colors: active) { .uix-data-workspace__grid--pin-id tbody tr td:nth-child(2) { background: Canvas; } }
|
|
25
24
|
.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
25
|
/* the record-id cell: the id opens a peek, ↗ opens the full record */
|
|
28
26
|
.uix-data-workspace__id { display: inline-flex; align-items: center; gap: var(--uix-space-1); white-space: nowrap; }
|
|
29
27
|
.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); }
|
|
@@ -39,4 +37,106 @@
|
|
|
39
37
|
}
|
|
40
38
|
/* a phone: the search takes its own row */
|
|
41
39
|
@media (max-width: 40rem) { .uix-data-workspace__search { flex: 1 1 100%; } }
|
|
40
|
+
|
|
41
|
+
/* ── 2.50: adoption gaps (HAR-2622..2626, ADR-0005 addendum) ─────────────────────────── */
|
|
42
|
+
/* the product's notices between the toolbar (and bulk bar) and the rows */
|
|
43
|
+
.uix-data-workspace__before { display: grid; gap: var(--uix-space-2); min-width: 0; }
|
|
44
|
+
/* a note above the saved-view list ("Saved views could not be loaded") */
|
|
45
|
+
.uix-data-workspace__views-note {
|
|
46
|
+
max-inline-size: 20rem; padding: var(--uix-space-2) var(--uix-space-3); border-bottom: 1px solid var(--uix-border);
|
|
47
|
+
color: var(--uix-text-muted); font-size: var(--uix-text-meta);
|
|
48
|
+
}
|
|
49
|
+
/* "Nothing matches" with the product's action beside Clear filters */
|
|
50
|
+
.uix-data-workspace__state-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--uix-space-2); }
|
|
51
|
+
|
|
52
|
+
/* the rich record-id cell: a strong line over a muted one, inside the same button or link */
|
|
53
|
+
.uix-data-workspace__id { max-inline-size: 100%; }
|
|
54
|
+
.uix-data-workspace__id-text { display: inline-flex; flex-direction: column; align-items: flex-start; min-inline-size: 0; text-align: start; }
|
|
55
|
+
.uix-data-workspace__id-text > * { max-inline-size: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
56
|
+
.uix-data-workspace__id-link { color: var(--uix-text); text-decoration: none; }
|
|
57
|
+
|
|
58
|
+
/* row groups: each group is a tbody under its header row. The zebra restarts under every header
|
|
59
|
+
with a plain row: the group swaps the two row colours its rows read (no selector here outranks
|
|
60
|
+
a row state; table.css keeps their order). */
|
|
61
|
+
.uix-data-workspace__grid--grouped { --uix-dw-row: var(--uix-row); --uix-dw-row-alt: var(--uix-row-alt); }
|
|
62
|
+
.uix-data-workspace__grid--grouped .uix-data-workspace__group { --uix-row: var(--uix-dw-row-alt); --uix-row-alt: var(--uix-dw-row); }
|
|
63
|
+
.uix-table tbody tr:where(.uix-data-workspace__group-head) { --uix-row-bg: var(--uix-bg-subtle); }
|
|
64
|
+
.uix-data-workspace__group-head > :is(th, td) {
|
|
65
|
+
height: auto; padding-block: var(--uix-space-2); border-bottom: 1px solid var(--uix-border);
|
|
66
|
+
background: linear-gradient(var(--uix-row-bg), var(--uix-row-bg)), var(--uix-surface);
|
|
67
|
+
}
|
|
68
|
+
.uix-data-workspace__group-label { font-weight: 400; text-align: start; }
|
|
69
|
+
/* the label spans several columns: its words stay in view while the table scrolls sideways,
|
|
70
|
+
the cell itself does not stick (it would slide over the aggregates) */
|
|
71
|
+
.uix-data-workspace .uix-data-workspace__group-head > th { position: static; box-shadow: none; }
|
|
72
|
+
.uix-data-workspace__group-title {
|
|
73
|
+
position: sticky; inset-inline-start: var(--uix-space-4); display: inline-flex; flex-wrap: wrap; align-items: center;
|
|
74
|
+
gap: var(--uix-space-1) var(--uix-space-2); max-inline-size: 100%;
|
|
75
|
+
}
|
|
76
|
+
.uix-data-workspace__grid--pin-id .uix-data-workspace__group-title { inset-inline-start: calc(var(--uix-selcol-w) + var(--uix-space-4)); }
|
|
77
|
+
.uix-data-workspace__group-toggle {
|
|
78
|
+
display: inline-grid; place-items: center; width: 24px; height: 24px; flex: none; padding: 0; border: 0;
|
|
79
|
+
border-radius: var(--uix-radius-xs); background: transparent; color: var(--uix-text-muted); cursor: pointer;
|
|
80
|
+
}
|
|
81
|
+
.uix-data-workspace__group-toggle svg { width: 16px; height: 16px; transition: transform var(--uix-dur-fast) var(--uix-ease-out); }
|
|
82
|
+
.uix-data-workspace__group-toggle[aria-expanded="true"] svg { transform: rotate(90deg); }
|
|
83
|
+
.uix-data-workspace__group-toggle:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
|
|
84
|
+
@media (hover: hover) and (pointer: fine) { .uix-data-workspace__group-toggle:hover { background: var(--uix-bg-active); color: var(--uix-text); } }
|
|
85
|
+
.uix-data-workspace__group-name { font-weight: 600; color: var(--uix-text); }
|
|
86
|
+
.uix-data-workspace__group-count, .uix-data-workspace__group-agg { color: var(--uix-text-muted); font-size: var(--uix-text-meta); font-variant-numeric: tabular-nums; }
|
|
87
|
+
@media (prefers-reduced-motion: reduce) { .uix-data-workspace__group-toggle svg { transition: none; } }
|
|
88
|
+
|
|
89
|
+
/* the keyboard row model: one row in the tab order; its ring sits inside the row, so moving
|
|
90
|
+
focus never shifts the layout and the wrap's edge never clips it */
|
|
91
|
+
/* a row a click activates (onRowActivate) */
|
|
92
|
+
.uix-data-workspace__grid--click tbody tr[data-uix-row-id] { cursor: pointer; }
|
|
93
|
+
/* The frozen cells paint over a row's outline, so every cell draws its edge of the ring too
|
|
94
|
+
(forced colours drop box-shadow and keep the outline). A card has no frozen cells: its outline is enough. */
|
|
95
|
+
.uix-data-workspace__grid--rows tbody tr:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: -2px; }
|
|
96
|
+
.uix-data-workspace__table .uix-data-workspace__grid--rows:not(.uix-table--cards) tbody tr[data-uix-row-id]:focus-visible > td { box-shadow: inset 0 2px 0 var(--uix-ring), inset 0 -1px 0 var(--uix-ring); border-bottom-color: var(--uix-ring); }
|
|
97
|
+
.uix-data-workspace__table .uix-data-workspace__grid--rows:not(.uix-table--cards) tbody tr[data-uix-row-id]:focus-visible > td:first-child { box-shadow: inset 2px 0 0 var(--uix-ring), inset 0 2px 0 var(--uix-ring), inset 0 -1px 0 var(--uix-ring); border-bottom-color: var(--uix-ring); }
|
|
98
|
+
.uix-data-workspace__table .uix-data-workspace__grid--rows:not(.uix-table--cards) tbody tr[data-uix-row-id]:focus-visible > td:last-child { box-shadow: inset -2px 0 0 var(--uix-ring), inset 0 2px 0 var(--uix-ring), inset 0 -1px 0 var(--uix-ring); border-bottom-color: var(--uix-ring); }
|
|
99
|
+
/* windowing: the rows outside the window are two plain spacers */
|
|
100
|
+
.uix-table tbody tr:where(.uix-data-workspace__spacer) { --uix-row-bg: transparent; }
|
|
101
|
+
.uix-data-workspace__spacer > td { padding: 0; border: 0; }
|
|
102
|
+
|
|
103
|
+
/* cards (mobile="cards", under 768 px of workspace): the wrap drops its frame and each row is a
|
|
104
|
+
card, a grid in flow. Its first line is the checkbox and the id; every value spans the card
|
|
105
|
+
below, labelled; the row's actions close the card, always shown (a card has no hover to
|
|
106
|
+
reveal them), so word actions ("Renew · Return") never run over a long title. */
|
|
107
|
+
.uix-data-workspace__table--cards { border: 0; border-radius: 0; background: none; }
|
|
108
|
+
.uix-data-workspace .uix-table--cards tbody tr:not(.uix-data-workspace__group-head) {
|
|
109
|
+
display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: stretch;
|
|
110
|
+
}
|
|
111
|
+
.uix-data-workspace .uix-table--cards tbody tr:not(.uix-data-workspace__group-head) > td { grid-column: 1 / -1; }
|
|
112
|
+
.uix-data-workspace .uix-table--cards tbody tr:not(.uix-data-workspace__group-head) > td:is([data-col="select"], [data-col="id"]) {
|
|
113
|
+
grid-row: 1; padding: var(--uix-space-2) 0 var(--uix-space-3); border-bottom: 1px solid var(--uix-border);
|
|
114
|
+
}
|
|
115
|
+
/* placed with the same selector as the span-all rule above, so they outrank it */
|
|
116
|
+
.uix-data-workspace .uix-table--cards tbody tr:not(.uix-data-workspace__group-head) > td[data-col="select"] { display: block; grid-column: 1; width: auto; }
|
|
117
|
+
/* no column gap: the line under the first line runs unbroken under the checkbox and the id */
|
|
118
|
+
.uix-data-workspace .uix-table--cards tbody tr:not(.uix-data-workspace__group-head) > td[data-col="id"] { grid-column: 2 / -1; padding-inline-start: var(--uix-space-3); }
|
|
119
|
+
.uix-data-workspace .uix-table--cards tbody tr:not(.uix-data-workspace__group-head):not(:has(> td[data-col="select"])) > td[data-col="id"] { grid-column: 1 / -1; padding-inline-start: 0; }
|
|
120
|
+
.uix-data-workspace .uix-table--cards tbody tr:not(.uix-data-workspace__group-head) > td[data-col="actions"] {
|
|
121
|
+
display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--uix-space-1) var(--uix-space-2);
|
|
122
|
+
grid-column: 1 / -1; width: auto; min-inline-size: 0; padding: var(--uix-space-2) 0;
|
|
123
|
+
}
|
|
124
|
+
.uix-data-workspace .uix-table--cards td[data-col="select"]::before, .uix-data-workspace .uix-table--cards td[data-col="actions"]::before { content: none; }
|
|
125
|
+
.uix-data-workspace .uix-table--cards td[data-col="actions"] .uix-rowact { opacity: 1; }
|
|
126
|
+
/* the id's words wrap in a card rather than run under the actions */
|
|
127
|
+
.uix-data-workspace .uix-table--cards .uix-data-workspace__id, .uix-data-workspace .uix-table--cards .uix-id-cell__btn { white-space: normal; text-align: start; }
|
|
128
|
+
.uix-data-workspace .uix-table--cards .uix-data-workspace__id-text > * { white-space: normal; overflow-wrap: anywhere; }
|
|
129
|
+
/* a group's header in cards: one line above its cards (checkbox, toggle, name, count, then the aggregates) */
|
|
130
|
+
.uix-data-workspace .uix-table--cards tbody tr.uix-data-workspace__group-head {
|
|
131
|
+
display: flex; flex-wrap: wrap; align-items: center; gap: var(--uix-space-1) var(--uix-space-3);
|
|
132
|
+
margin: var(--uix-space-5) 0 var(--uix-space-2); padding: 0; border: 0; box-shadow: none; background: none;
|
|
133
|
+
}
|
|
134
|
+
.uix-data-workspace .uix-table--cards tbody:first-of-type tr.uix-data-workspace__group-head { margin-block-start: 0; }
|
|
135
|
+
.uix-data-workspace .uix-table--cards .uix-data-workspace__group-head > :is(th, td) {
|
|
136
|
+
display: flex; align-items: center; gap: var(--uix-space-2); position: static; width: auto; height: auto;
|
|
137
|
+
padding: 0; border: 0; background: none; text-align: start;
|
|
138
|
+
}
|
|
139
|
+
.uix-data-workspace .uix-table--cards .uix-data-workspace__group-label { flex: 1 1 auto; }
|
|
140
|
+
.uix-data-workspace .uix-table--cards .uix-data-workspace__group-title { position: static; }
|
|
141
|
+
.uix-data-workspace .uix-table--cards .uix-data-workspace__group-agg:empty { display: none; }
|
|
42
142
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* uix drag — the shared affordances of anything a person drags (HAR-2590, motion standard §2.8):
|
|
2
|
+
a list row, a kanban card, a dashboard widget. Attributes, so any element can carry them:
|
|
3
|
+
[data-uix-drag="lifted"] picked up by the pointer: raised, a little larger, a deeper shadow
|
|
4
|
+
[data-uix-drag="grabbed"] picked up from the keyboard (Space): a dashed ring, no motion
|
|
5
|
+
[data-uix-drop-target] the zone the item would land in: tinted and outlined
|
|
6
|
+
.uix-drop-line where it would land between two items: an accent line with ends
|
|
7
|
+
[data-uix-settle] set for a moment on drop: the item lands (settleDrop)
|
|
8
|
+
Lift and settle are transform only; the shadow steps. Reduced motion: no scale, no settle.
|
|
9
|
+
Depends only on --uix-* tokens. */
|
|
10
|
+
@layer uix.components {
|
|
11
|
+
[data-uix-drag="lifted"] {
|
|
12
|
+
position: relative; z-index: var(--uix-z-raised); cursor: grabbing;
|
|
13
|
+
box-shadow: var(--uix-shadow-popover); transform: scale(1.02);
|
|
14
|
+
transition: transform var(--uix-dur-fast) var(--uix-ease-out);
|
|
15
|
+
}
|
|
16
|
+
[data-uix-drag="grabbed"] {
|
|
17
|
+
position: relative; z-index: var(--uix-z-raised);
|
|
18
|
+
box-shadow: var(--uix-shadow-popover); outline: 2px dashed var(--uix-ring); outline-offset: 2px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
[data-uix-drop-target] {
|
|
22
|
+
background-color: color-mix(in srgb, var(--uix-accent) 8%, transparent);
|
|
23
|
+
outline: 2px dashed var(--uix-ring); outline-offset: -2px;
|
|
24
|
+
transition: background-color var(--uix-dur-fast) var(--uix-ease-out);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* zero tall where it stands (a flex gap around it opens the room); the line is drawn across it */
|
|
28
|
+
.uix-drop-line { position: relative; display: block; block-size: 0; pointer-events: none; }
|
|
29
|
+
.uix-drop-line::before {
|
|
30
|
+
content: ""; position: absolute; inset-inline: var(--uix-space-1); inset-block-start: calc(var(--uix-space-1) * -.25);
|
|
31
|
+
block-size: calc(var(--uix-space-1) * .5); border-radius: var(--uix-radius-pill); background: var(--uix-accent);
|
|
32
|
+
}
|
|
33
|
+
.uix-drop-line::after {
|
|
34
|
+
content: ""; position: absolute; inset-inline-start: 0; inset-block-start: calc(var(--uix-space-2) * -.5);
|
|
35
|
+
inline-size: var(--uix-space-2); block-size: var(--uix-space-2); border-radius: var(--uix-radius-pill);
|
|
36
|
+
border: 2px solid var(--uix-accent); background: var(--uix-surface);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@keyframes uix-drag-settle { from { transform: scale(1.02); } }
|
|
40
|
+
[data-uix-settle] { animation: uix-drag-settle var(--uix-dur) var(--uix-ease-out); }
|
|
41
|
+
|
|
42
|
+
@media (prefers-reduced-motion: reduce) {
|
|
43
|
+
[data-uix-drag="lifted"] { transform: none; }
|
|
44
|
+
[data-uix-settle] { animation: none; }
|
|
45
|
+
}
|
|
46
|
+
@media (forced-colors: active) {
|
|
47
|
+
/* the dashes are what tells a picked-up item from a focused one: keep them */
|
|
48
|
+
[data-uix-drag="lifted"] { outline: 2px solid Highlight; }
|
|
49
|
+
[data-uix-drag="grabbed"] { outline: 2px dashed Highlight; }
|
|
50
|
+
[data-uix-drop-target] { outline-color: Highlight; }
|
|
51
|
+
.uix-drop-line::before, .uix-drop-line::after { forced-color-adjust: none; }
|
|
52
|
+
.uix-drop-line::before { background: Highlight; }
|
|
53
|
+
.uix-drop-line::after { border-color: Highlight; background: Canvas; }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same rules */
|
|
58
|
+
@layer uix.components {
|
|
59
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
60
|
+
:root[data-motion="reduce"] [data-uix-drag="lifted"] { transform: none; }
|
|
61
|
+
:root[data-motion="reduce"] [data-uix-settle] { animation: none; }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
border: 0; border-left: 1px solid var(--uix-border); border-radius: 0;
|
|
6
6
|
background: var(--uix-surface); box-shadow: var(--uix-shadow-overlay);
|
|
7
7
|
transform: translateX(100%);
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/* the closed state's durations are the exit's (--uix-dur-exit-slow); [open] carries the enter */
|
|
9
|
+
transition: transform var(--uix-dur-exit-slow) var(--uix-ease-out),
|
|
10
|
+
overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
|
|
10
11
|
/* Author display values for both states required for the slide-in (see peek.css). Closed = none. */
|
|
11
12
|
display: none;
|
|
12
13
|
}
|
|
13
|
-
.uix-drawer[open] { transform: none; display: flex; flex-direction: column; }
|
|
14
|
+
.uix-drawer[open] { transform: none; display: flex; flex-direction: column; transition-duration: var(--uix-dur-slow); }
|
|
14
15
|
@starting-style { .uix-drawer[open] { transform: translateX(100%); } }
|
|
15
16
|
.uix-drawer::backdrop {
|
|
16
17
|
background: var(--uix-scrim); opacity: 0;
|
|
17
|
-
transition: opacity var(--uix-dur-slow) var(--uix-ease-out), overlay var(--uix-dur-slow) allow-discrete, display var(--uix-dur-slow) allow-discrete;
|
|
18
|
+
transition: opacity var(--uix-dur-exit-slow) var(--uix-ease-out), overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
|
|
18
19
|
}
|
|
19
|
-
.uix-drawer[open]::backdrop { opacity: 1; }
|
|
20
|
+
.uix-drawer[open]::backdrop { opacity: 1; transition-duration: var(--uix-dur-slow); }
|
|
20
21
|
@starting-style { .uix-drawer[open]::backdrop { opacity: 0; } }
|
|
21
22
|
/* side="start" — the phone navigation drawer, from the left edge (HAR-1351) */
|
|
22
23
|
.uix-drawer--start { margin: 0 auto 0 0; border-left: 0; border-right: 1px solid var(--uix-border); transform: translateX(-100%); }
|
|
@@ -4,10 +4,19 @@
|
|
|
4
4
|
.uix-kanban__col { flex: 0 0 260px; background: var(--uix-bg-subtle); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-lg); display: flex; flex-direction: column; max-height: 460px; }
|
|
5
5
|
.uix-kanban__head { display: flex; align-items: center; gap: var(--uix-space-2); padding: var(--uix-space-3) var(--uix-space-4); font-weight: 600; font-size: var(--uix-text-meta); }
|
|
6
6
|
.uix-kanban__count { margin-left: auto; color: var(--uix-text-muted); font-weight: 500; }
|
|
7
|
-
|
|
7
|
+
/* a little room on top: the scrolling body would clip a lifted or picked-up card's edge */
|
|
8
|
+
.uix-kanban__body { padding: var(--uix-space-1) var(--uix-space-3) var(--uix-space-3); display: flex; flex-direction: column; gap: var(--uix-space-2); overflow-y: auto; }
|
|
8
9
|
.uix-kanban__card { background: var(--uix-surface); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); padding: var(--uix-space-3); box-shadow: var(--uix-shadow-sm); cursor: grab; transition: transform var(--uix-dur-fast) var(--uix-ease-out); }
|
|
9
10
|
@media (hover: hover) and (pointer: fine) { .uix-kanban__card:hover { box-shadow: var(--uix-shadow-popover); transform: translateY(calc(-1 * var(--uix-lift))); } }
|
|
10
11
|
.uix-kanban__card:active { cursor: grabbing; }
|
|
12
|
+
/* dragged (drag.css, HAR-2590): the lift wins over the hover rise while the pointer is on it */
|
|
13
|
+
.uix-kanban__card[data-uix-drag="lifted"] { transform: scale(1.02); box-shadow: var(--uix-shadow-popover); }
|
|
14
|
+
@media (prefers-reduced-motion: reduce) { .uix-kanban__card[data-uix-drag="lifted"] { transform: none; } }
|
|
11
15
|
.uix-kanban__card-title { font-weight: 500; font-size: var(--uix-text-body); margin-bottom: var(--uix-space-2); }
|
|
12
16
|
.uix-kanban__card-meta { display: flex; align-items: center; gap: var(--uix-space-2); }
|
|
13
17
|
}
|
|
18
|
+
|
|
19
|
+
/* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same rule */
|
|
20
|
+
@layer uix.components {
|
|
21
|
+
@media (prefers-reduced-motion: no-preference) { :root[data-motion="reduce"] .uix-kanban__card[data-uix-drag="lifted"] { transform: none; } }
|
|
22
|
+
}
|
|
@@ -12,3 +12,28 @@
|
|
|
12
12
|
.uix-kbd-combo { display: inline-flex; align-items: center; }
|
|
13
13
|
.uix-kbd-combo__keys { display: inline-flex; align-items: center; gap: var(--uix-space-1); }
|
|
14
14
|
}
|
|
15
|
+
|
|
16
|
+
/* Shortcut hints are for a keyboard. On a device with no hover and a coarse pointer (a touch
|
|
17
|
+
phone or tablet) the kit's decorative key caps go: the Sidebar search row's keys, the keys of a
|
|
18
|
+
Menu, CommandPalette or Tooltip row, SearchSuggest's trailing key, and a Button's hint bubble
|
|
19
|
+
(a tap fires mouseenter, which opened it). A keyboard-and-mouse window of any width keeps them.
|
|
20
|
+
Nothing a screen reader hears changes: what goes is already aria-hidden (each KbdCombo says its
|
|
21
|
+
keys in a visually-hidden span, which stays), a key a product put in a Menu or CommandItem
|
|
22
|
+
shortcut slot is only clipped from view, and `aria-keyshortcuts` and descriptions stay. Each
|
|
23
|
+
selector out-specifies the rule that lays the part out, so the order the sheets load in does
|
|
24
|
+
not matter. */
|
|
25
|
+
@layer uix.components {
|
|
26
|
+
@media (hover: none) and (pointer: coarse) {
|
|
27
|
+
.uix-sidebar__search .uix-sidebar__search-keys,
|
|
28
|
+
:is(.uix-menu__shortcut, .uix-cmdk__shortcut, .uix-tooltip__shortcut) .uix-kbd-combo__keys,
|
|
29
|
+
.uix-search-suggest__trail > .uix-kbd,
|
|
30
|
+
.uix-tooltip__bubble.uix-tooltip__bubble--hint,
|
|
31
|
+
.uix-tooltip__bubble:has(> .uix-tooltip__shortcut:only-child) { display: none; }
|
|
32
|
+
:is(.uix-menu__shortcut, .uix-cmdk__item) > kbd.uix-kbd {
|
|
33
|
+
position: absolute; width: 1px; height: 1px; padding: 0; margin: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
|
|
34
|
+
}
|
|
35
|
+
/* no gap left where the keys were */
|
|
36
|
+
.uix-menu__item .uix-menu__shortcut { padding-left: 0; }
|
|
37
|
+
.uix-tooltip__bubble .uix-tooltip__shortcut { margin-inline-start: 0; }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* uix list motion — items arrive, leave and move visibly (HAR-2586, motion standard §2.5).
|
|
2
|
+
React's MotionList marks its children: [data-uix-entering] rises 8 px and fades in (never on
|
|
3
|
+
the first render); [data-uix-exiting] fades out in place, and only then is it removed, so the
|
|
4
|
+
items after it glide up into the gap ([data-uix-moving]: a FLIP transform, never a height).
|
|
5
|
+
Reduced motion keeps the fades and drops the rise and the glide. Depends only on --uix-*
|
|
6
|
+
tokens. */
|
|
7
|
+
@layer uix.components {
|
|
8
|
+
@keyframes uix-list-enter { from { opacity: 0; transform: translateY(var(--uix-space-2)); } }
|
|
9
|
+
@keyframes uix-list-exit { to { opacity: 0; transform: scale(.98); } }
|
|
10
|
+
@keyframes uix-list-fade-out { to { opacity: 0; } }
|
|
11
|
+
.uix-motion-list > [data-uix-entering] { animation: uix-list-enter var(--uix-dur) var(--uix-ease-out); }
|
|
12
|
+
.uix-motion-list > [data-uix-exiting] { animation: uix-list-exit var(--uix-dur-exit) var(--uix-ease-out) forwards; pointer-events: none; }
|
|
13
|
+
.uix-motion-list > [data-uix-moving] { transition: transform var(--uix-dur-slow) var(--uix-ease-in-out); }
|
|
14
|
+
@media (prefers-reduced-motion: reduce) {
|
|
15
|
+
/* !important: the kit's reduced-motion policy ends keyframes at once; the fades are the signal */
|
|
16
|
+
.uix-motion-list > [data-uix-entering] { animation: uix-fade-in var(--uix-dur) var(--uix-ease-out) !important; }
|
|
17
|
+
.uix-motion-list > [data-uix-exiting] { animation: uix-list-fade-out var(--uix-dur-exit) var(--uix-ease-out) forwards !important; }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the same fades */
|
|
22
|
+
@layer uix.components {
|
|
23
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
24
|
+
:root[data-motion="reduce"] .uix-motion-list > [data-uix-entering] { animation: uix-fade-in var(--uix-dur) var(--uix-ease-out) !important; }
|
|
25
|
+
:root[data-motion="reduce"] .uix-motion-list > [data-uix-exiting] { animation: uix-list-fade-out var(--uix-dur-exit) var(--uix-ease-out) forwards !important; }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -5,20 +5,21 @@
|
|
|
5
5
|
border: 1px solid var(--uix-border); border-radius: var(--uix-radius-lg);
|
|
6
6
|
background: var(--uix-surface-2); box-shadow: var(--uix-shadow-overlay);
|
|
7
7
|
opacity: 0; transform: translateY(8px) scale(.98);
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/* the closed state's durations are the exit's: --uix-dur-exit-slow, faster than the enter, ease-out */
|
|
9
|
+
transition: opacity var(--uix-dur-exit-slow) var(--uix-ease-out), transform var(--uix-dur-exit-slow) var(--uix-ease-out),
|
|
10
|
+
overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
|
|
10
11
|
/* Author display values for both states required for the fade-in to animate: the allow-discrete
|
|
11
12
|
`display` transition only engages with author values, which also lets @starting-style fire.
|
|
12
13
|
With only the UA dialog display, the modal stays at opacity:0. Closed = none; open = block. */
|
|
13
14
|
display: none;
|
|
14
15
|
}
|
|
15
|
-
.uix-dialog[open] { opacity: 1; transform: none; display: block; }
|
|
16
|
+
.uix-dialog[open] { opacity: 1; transform: none; display: block; transition-duration: var(--uix-dur-slow); }
|
|
16
17
|
@starting-style { .uix-dialog[open] { opacity: 0; transform: translateY(8px) scale(.98); } }
|
|
17
18
|
.uix-dialog::backdrop {
|
|
18
19
|
background: var(--uix-scrim); opacity: 0;
|
|
19
|
-
transition: opacity var(--uix-dur-slow) var(--uix-ease-out), overlay var(--uix-dur-slow) allow-discrete, display var(--uix-dur-slow) allow-discrete;
|
|
20
|
+
transition: opacity var(--uix-dur-exit-slow) var(--uix-ease-out), overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
|
|
20
21
|
}
|
|
21
|
-
.uix-dialog[open]::backdrop { opacity: 1; }
|
|
22
|
+
.uix-dialog[open]::backdrop { opacity: 1; transition-duration: var(--uix-dur-slow); }
|
|
22
23
|
@starting-style { .uix-dialog[open]::backdrop { opacity: 0; } }
|
|
23
24
|
|
|
24
25
|
.uix-dialog__header { display: flex; align-items: center; gap: var(--uix-space-3); padding: var(--uix-space-4) var(--uix-space-5); border-bottom: 1px solid var(--uix-border); }
|
|
@@ -6,21 +6,22 @@
|
|
|
6
6
|
border: 0; border-left: 1px solid var(--uix-border); border-radius: 0;
|
|
7
7
|
background: var(--uix-surface); box-shadow: var(--uix-shadow-overlay);
|
|
8
8
|
transform: translateX(100%);
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/* the closed state's durations are the exit's (--uix-dur-exit-slow); [open] carries the enter */
|
|
10
|
+
transition: transform var(--uix-dur-exit-slow) var(--uix-ease-out),
|
|
11
|
+
overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
|
|
11
12
|
/* Author display values for BOTH states are required for the slide-in to animate: the
|
|
12
13
|
allow-discrete `display` transition only engages with author values, and that gating also
|
|
13
14
|
lets the @starting-style entrance fire. With only the UA dialog display, the panel stays
|
|
14
15
|
parked at translateX(100%). Closed = none; open = flex. */
|
|
15
16
|
display: none;
|
|
16
17
|
}
|
|
17
|
-
.uix-peek[open] { transform: none; display: flex; flex-direction: column; }
|
|
18
|
+
.uix-peek[open] { transform: none; display: flex; flex-direction: column; transition-duration: var(--uix-dur-slow); }
|
|
18
19
|
@starting-style { .uix-peek[open] { transform: translateX(100%); } }
|
|
19
20
|
.uix-peek::backdrop {
|
|
20
21
|
background: var(--uix-scrim); opacity: 0;
|
|
21
|
-
transition: opacity var(--uix-dur-slow) var(--uix-ease-out), overlay var(--uix-dur-slow) allow-discrete, display var(--uix-dur-slow) allow-discrete;
|
|
22
|
+
transition: opacity var(--uix-dur-exit-slow) var(--uix-ease-out), overlay var(--uix-dur-exit-slow) allow-discrete, display var(--uix-dur-exit-slow) allow-discrete;
|
|
22
23
|
}
|
|
23
|
-
.uix-peek[open]::backdrop { opacity: 1; }
|
|
24
|
+
.uix-peek[open]::backdrop { opacity: 1; transition-duration: var(--uix-dur-slow); }
|
|
24
25
|
@starting-style { .uix-peek[open]::backdrop { opacity: 0; } }
|
|
25
26
|
|
|
26
27
|
.uix-peek__header { display: flex; align-items: center; gap: var(--uix-space-3); padding: var(--uix-space-4) var(--uix-space-4); border-bottom: 1px solid var(--uix-border); }
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/* uix popover — generic anchored surface via the native Popover API; animated with @starting-style.
|
|
2
|
-
Enter
|
|
3
|
-
|
|
2
|
+
Enter: fade + scale(.95 → 1) growing out of the trigger — useAnchoredPosition writes the anchor
|
|
3
|
+
point as --uix-anchor-origin — at --uix-dur-fast. Exit: the same, back in, at --uix-dur-exit-fast
|
|
4
|
+
(faster than the enter, still ease-out). data-uix-placing holds the first frame until it is placed;
|
|
5
|
+
a popover opened from the keyboard carries [data-uix-key-opened] and does not animate (motion.css). */
|
|
4
6
|
@layer uix.components {
|
|
5
7
|
.uix-popover { margin: 0; padding: var(--uix-space-2); background: var(--uix-surface-2); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); box-shadow: var(--uix-shadow-popover); color: var(--uix-text); }
|
|
6
8
|
[popover].uix-popover {
|
|
7
|
-
--popover-from:
|
|
8
|
-
opacity: 0; transform: var(--popover-from);
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
--popover-from: scale(.95);
|
|
10
|
+
opacity: 0; transform: var(--popover-from); transform-origin: var(--uix-anchor-origin, top left);
|
|
11
|
+
/* the closed state's durations are the exit's */
|
|
12
|
+
transition: opacity var(--uix-dur-exit-fast) var(--uix-ease-out), transform var(--uix-dur-exit-fast) var(--uix-ease-out),
|
|
13
|
+
overlay var(--uix-dur-exit-fast) allow-discrete, display var(--uix-dur-exit-fast) allow-discrete;
|
|
11
14
|
}
|
|
12
|
-
[popover].uix-popover
|
|
13
|
-
[popover].uix-popover[data-placement^="left"] { --popover-from: translateX(var(--uix-lift)); }
|
|
14
|
-
[popover].uix-popover[data-placement^="right"] { --popover-from: translateX(calc(-1 * var(--uix-lift))); }
|
|
15
|
-
[popover].uix-popover:popover-open { opacity: 1; transform: none; }
|
|
15
|
+
[popover].uix-popover:popover-open { opacity: 1; transform: none; transition-duration: var(--uix-dur-fast); }
|
|
16
16
|
/* A closed popover is display:none by the UA stylesheet only, so a component that sets its
|
|
17
17
|
own display (.uix-popconfirm, .uix-filter-popover: grid) stayed laid out while closed: an
|
|
18
18
|
invisible fixed box at the viewport's top-left that took clicks (HAR-2029). The display
|
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
/* Node fills must be opaque: edges run to node centres, so a translucent tint lets them strike through the label. */
|
|
10
10
|
.uix-relationship-graph__node[data-selected] rect,.uix-relationship-graph__node[data-highlighted] rect { fill:color-mix(in srgb, var(--uix-accent) 16%, var(--uix-surface)); stroke:var(--uix-accent); stroke-width:3; } .uix-relationship-graph__node[data-conflicted] rect { fill:color-mix(in srgb, var(--uix-danger) 20%, var(--uix-surface)); stroke:var(--uix-danger); stroke-dasharray:4 2; }
|
|
11
11
|
.uix-relationship-graph__node:focus-visible rect { stroke:var(--uix-ring); stroke-width:4; }
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
/* One row per item, symbol and words centred on each other (operator review 2026-10-23: the dashed
|
|
13
|
+
samples and swatches sat off the text's middle). Items wrap whole on a narrow screen. */
|
|
14
|
+
.uix-relationship-graph__legend { display:flex; flex-wrap:wrap; align-items:center; gap:var(--uix-space-2) var(--uix-space-4); margin:0; padding:0; list-style:none; color:var(--uix-text-muted); font-size:var(--uix-text-meta); line-height:1.4; }
|
|
15
|
+
.uix-relationship-graph__legend li { display:inline-flex; align-items:center; gap:var(--uix-space-2); min-width:0; }
|
|
16
|
+
.uix-relationship-graph__legend-mark { flex:none; box-sizing:border-box; width:.8rem; height:.8rem; border:2px solid var(--uix-graph-type-color, var(--uix-chart-1)); border-radius:var(--uix-radius-pill); }
|
|
14
17
|
/* TENSOR RX-125 (UIX-14): an edge whose type is in the legend draws that type's colour. */
|
|
15
18
|
.uix-relationship-graph__edge[data-typed] { stroke:var(--uix-graph-type-color, var(--uix-border-strong)); }
|
|
16
19
|
.uix-relationship-graph__list { padding:var(--uix-space-3); border:1px solid var(--uix-border); border-radius:var(--uix-radius-lg); background:var(--uix-surface); }
|
|
@@ -36,6 +39,11 @@
|
|
|
36
39
|
.uix-relationship-graph--layered .uix-relationship-graph__toolbar button:disabled { opacity:.5; cursor:not-allowed; }
|
|
37
40
|
.uix-relationship-graph__viewport { position:relative; height:var(--uix-relationship-graph-height, 22rem); overflow:hidden; border:1px solid var(--uix-border); border-radius:var(--uix-radius-lg); background:var(--uix-bg-subtle); cursor:grab; touch-action:pan-y; }
|
|
38
41
|
.uix-relationship-graph__viewport:active { cursor:grabbing; }
|
|
42
|
+
/* The map is a pan surface: a press starts a pan, never a text selection or a native drag of the
|
|
43
|
+
words (operator review 2026-10-23: a ghost of the labels followed the pointer). The list below
|
|
44
|
+
stays selectable; screen readers read the names as before. */
|
|
45
|
+
.uix-relationship-graph__viewport { -webkit-user-select:none; user-select:none; -webkit-touch-callout:none; }
|
|
46
|
+
.uix-relationship-graph__viewport :is(img, svg, a) { -webkit-user-drag:none; }
|
|
39
47
|
.uix-relationship-graph__stage { position:absolute; top:0; left:0; transform-origin:0 0; }
|
|
40
48
|
.uix-relationship-graph__stage[data-animate] { transition:transform var(--uix-dur) var(--uix-ease-out); }
|
|
41
49
|
.uix-relationship-graph__edges { position:absolute; top:0; left:0; overflow:visible; pointer-events:none; }
|
|
@@ -43,12 +51,22 @@
|
|
|
43
51
|
instead of stacking a translucent stroke to black (HAR-1387). */
|
|
44
52
|
.uix-relationship-graph__link path { fill:none; stroke:color-mix(in srgb, var(--uix-text) 28%, var(--uix-bg-subtle)); stroke-width:1.5px; }
|
|
45
53
|
.uix-relationship-graph__link polygon { fill:color-mix(in srgb, var(--uix-text) 45%, var(--uix-bg-subtle)); }
|
|
46
|
-
|
|
54
|
+
/* Edge labels: drawn after every line, each on a chip of the map's surface, in the kit's small
|
|
55
|
+
text at >= 4.5:1 on the chip; a tone is the chip's border, never the words' colour. */
|
|
56
|
+
/* zoomed out, a label keeps its on-screen size: scaled by 1 / zoom about its own centre (React
|
|
57
|
+
sets --uix-graph-label-scale on the stage and places the labels at that size) */
|
|
58
|
+
.uix-relationship-graph__edge-labels { pointer-events:none; }
|
|
59
|
+
.uix-relationship-graph__edge-label { transform-box:fill-box; transform-origin:center; transform:scale(var(--uix-graph-label-scale, 1)); }
|
|
60
|
+
.uix-relationship-graph__edge-label rect { fill:var(--uix-bg-subtle); stroke:var(--uix-border-strong); stroke-width:1px; }
|
|
61
|
+
.uix-relationship-graph__edge-label[data-tone] rect { stroke:var(--uix-graph-tone); }
|
|
62
|
+
.uix-relationship-graph__edge-label text { fill:var(--uix-text-muted); font-family:var(--uix-font-sans); font-size:var(--uix-text-meta); }
|
|
63
|
+
.uix-relationship-graph__edge-label[data-active] rect { stroke:var(--uix-accent); }
|
|
64
|
+
.uix-relationship-graph__edge-label[data-active] text { fill:var(--uix-text); }
|
|
65
|
+
.uix-relationship-graph__edge-label[data-dimmed] { opacity:.35; }
|
|
47
66
|
.uix-relationship-graph__link[data-kind="lateral"] path, .uix-relationship-graph__link[data-kind="cycle"] path { stroke-dasharray:5 4; }
|
|
48
67
|
.uix-relationship-graph__link[data-emphasis] path { stroke:color-mix(in srgb, var(--uix-text) 55%, var(--uix-bg-subtle)); }
|
|
49
68
|
.uix-relationship-graph__link[data-active] path { stroke:var(--uix-accent); stroke-width:2.5px; }
|
|
50
69
|
.uix-relationship-graph__link[data-active] polygon { fill:var(--uix-accent); }
|
|
51
|
-
.uix-relationship-graph__link[data-active] text { fill:var(--uix-text); }
|
|
52
70
|
.uix-relationship-graph__link[data-conflicted] path { stroke:var(--uix-danger); }
|
|
53
71
|
.uix-relationship-graph__link[data-dimmed] { opacity:.35; }
|
|
54
72
|
.uix-relationship-graph__column { position:absolute; top:var(--uix-space-4); overflow:hidden; color:var(--uix-text-muted); font-size:var(--uix-text-meta); white-space:nowrap; text-overflow:ellipsis; text-transform:uppercase; letter-spacing:.04em; pointer-events:none; }
|
|
@@ -74,8 +92,11 @@
|
|
|
74
92
|
/* The hint, the zoom level and the cluster share one row along the map's bottom edge (TENSOR
|
|
75
93
|
HAR-2512: positioned apart, "Zoom 75 %" was drawn over the hint on a 500–600 px map). The hint
|
|
76
94
|
takes only the room the controls leave: it ellipsises, then hides; then the zoom level gives
|
|
77
|
-
way; the cluster always stays. The row lets the pointer through to the map.
|
|
78
|
-
|
|
95
|
+
way; the cluster always stays. The row lets the pointer through to the map.
|
|
96
|
+
The row is a band across the map's whole bottom edge, on the map's own background with a rule
|
|
97
|
+
above it: the map ends at the rule, so no node is ever half under a pill (PR #166 review:
|
|
98
|
+
"Application 83" and "Rack 27" were). Fit and the first view leave its height free. */
|
|
99
|
+
.uix-relationship-graph__footer { position:absolute; right:0; bottom:0; left:0; z-index:var(--uix-z-raised, 1); display:flex; align-items:center; justify-content:flex-end; gap:var(--uix-space-3); padding:var(--uix-space-2) var(--uix-space-3); border-top:1px solid var(--uix-border); background:var(--uix-bg-subtle); container:uix-relationship-graph-footer / inline-size; pointer-events:none; }
|
|
79
100
|
.uix-relationship-graph__controls { display:flex; flex:0 1 auto; align-items:center; gap:var(--uix-space-2); min-width:0; pointer-events:auto; }
|
|
80
101
|
.uix-relationship-graph__control-cluster { display:flex; flex:none; padding:var(--uix-space-1); border:1px solid var(--uix-border-strong); border-radius:var(--uix-radius-md); background:var(--uix-surface); box-shadow:var(--uix-shadow-sm); }
|
|
81
102
|
.uix-relationship-graph__control { display:inline-grid; place-items:center; width:32px; height:32px; padding:0; border:0; border-radius:var(--uix-radius-sm); background:transparent; color:var(--uix-text-muted); cursor:pointer; }
|
|
@@ -109,9 +130,9 @@
|
|
|
109
130
|
.uix-relationship-graph__edge[data-tone] { stroke:var(--uix-graph-tone); }
|
|
110
131
|
/* a toned node: its border and a bar on its leading edge */
|
|
111
132
|
.uix-relationship-graph__item[data-tone] { border-color:var(--uix-graph-tone); box-shadow:inset 4px 0 0 var(--uix-graph-tone); }
|
|
112
|
-
.uix-relationship-graph__legend li[data-tone]
|
|
113
|
-
.uix-relationship-graph__legend li:is([data-shape="edge"],[data-variant]):not([data-shape="node"])
|
|
114
|
-
.uix-relationship-graph__legend li[data-variant="dashed"]
|
|
115
|
-
.uix-relationship-graph__legend li[data-shape="node"]
|
|
116
|
-
@media (forced-colors: active) { .uix-relationship-graph__link path { stroke:CanvasText; } .uix-relationship-graph__link polygon { fill:CanvasText; } .uix-relationship-graph__item { border-color:ButtonText; } }
|
|
133
|
+
.uix-relationship-graph__legend li[data-tone] .uix-relationship-graph__legend-mark { border-color:var(--uix-graph-tone); }
|
|
134
|
+
.uix-relationship-graph__legend li:is([data-shape="edge"],[data-variant]):not([data-shape="node"]) .uix-relationship-graph__legend-mark { width:1.5rem; height:2px; border-width:2px 0 0; border-radius:0; }
|
|
135
|
+
.uix-relationship-graph__legend li[data-variant="dashed"] .uix-relationship-graph__legend-mark { border-top-style:dashed; }
|
|
136
|
+
.uix-relationship-graph__legend li[data-shape="node"] .uix-relationship-graph__legend-mark { width:1rem; height:.75rem; border-width:1px 1px 1px 4px; border-radius:var(--uix-radius-xs); }
|
|
137
|
+
@media (forced-colors: active) { .uix-relationship-graph__edge-label rect { fill:Canvas; stroke:CanvasText; } .uix-relationship-graph__edge-label text { fill:CanvasText; } .uix-relationship-graph__link path { stroke:CanvasText; } .uix-relationship-graph__link polygon { fill:CanvasText; } .uix-relationship-graph__item { border-color:ButtonText; } }
|
|
117
138
|
}
|
|
@@ -34,9 +34,13 @@
|
|
|
34
34
|
display: flex; flex-direction: column; max-height: min(28rem, 70vh); overflow-y: auto; overscroll-behavior: contain;
|
|
35
35
|
padding: var(--uix-space-1); background: var(--uix-surface-2); border: 1px solid var(--uix-border);
|
|
36
36
|
border-radius: var(--uix-radius-md); box-shadow: var(--uix-shadow-popover);
|
|
37
|
-
|
|
37
|
+
/* Enters once per opening, growing from the field (a transition from @starting-style, not a
|
|
38
|
+
keyframe): the React SearchSuggest sets [data-settled] after the first show, so a list that
|
|
39
|
+
hides and reappears while the user types comes back in place (HAR-2578). */
|
|
40
|
+
transform-origin: var(--uix-anchor-origin, top center);
|
|
41
|
+
transition: opacity var(--uix-dur-fast) var(--uix-ease-out), transform var(--uix-dur-fast) var(--uix-ease-out);
|
|
38
42
|
}
|
|
39
|
-
@
|
|
43
|
+
@starting-style { .uix-search-suggest__popup:not([hidden], [data-settled]) { opacity: 0; transform: scale(.95); } }
|
|
40
44
|
|
|
41
45
|
.uix-search-suggest__header {
|
|
42
46
|
display: flex; align-items: baseline; justify-content: space-between; gap: var(--uix-space-3);
|
|
@@ -109,10 +113,6 @@
|
|
|
109
113
|
.uix-search-suggest__option { min-height: 44px; padding-block: var(--uix-space-3); }
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
@media (prefers-reduced-motion: reduce) {
|
|
113
|
-
.uix-search-suggest__popup:not([hidden]) { animation: none; }
|
|
114
|
-
}
|
|
115
|
-
|
|
116
116
|
@media (forced-colors: active) {
|
|
117
117
|
.uix-search-suggest__option[data-active], .uix-search-suggest__footer[data-active] { outline: 2px solid Highlight; outline-offset: -2px; }
|
|
118
118
|
.uix-search-suggest__match { text-decoration: underline; }
|