@tensor_1/tokens 2.48.0 → 2.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +4 -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 +31 -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.49.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); }
|
|
@@ -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; }
|
|
@@ -27,4 +27,25 @@
|
|
|
27
27
|
shift its neighbours on every selection. */
|
|
28
28
|
.uix-segmented__option:is([aria-pressed="true"], [aria-checked="true"], [aria-current="page"]) { background: var(--uix-surface); color: var(--uix-text); box-shadow: var(--uix-shadow-sm), inset 0 0 0 1px var(--uix-text-muted); }
|
|
29
29
|
.uix-segmented__option:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
|
|
30
|
+
|
|
31
|
+
/* Sliding indicator (React Segmented, HAR-2589). At rest the chosen option shows its own lift.
|
|
32
|
+
When a click changes it, one lifted segment slides from the old option to the new one
|
|
33
|
+
(transform only); while it slides ([data-uix-indicator]) the new option's own lift steps
|
|
34
|
+
aside, then the indicator hides again. Keyboard changes jump. */
|
|
35
|
+
/* the indicator is the last child: isolate + z-index -1 paint it above the track and beneath
|
|
36
|
+
the options */
|
|
37
|
+
.uix-segmented { position: relative; isolation: isolate; }
|
|
38
|
+
/* A window that narrows mid-slide wraps the options before the ResizeObserver that ends the
|
|
39
|
+
slide has run, so for that frame the indicator sits at its old place, past the control's edge,
|
|
40
|
+
and widened the page by 19 px at 320 px in CI (display-preferences.spec, red on master since
|
|
41
|
+
#170). The track clips it; the margin keeps the options' focus ring and lift. */
|
|
42
|
+
.uix-segmented { overflow: clip; overflow-clip-margin: var(--uix-space-1); }
|
|
43
|
+
.uix-segmented__option { position: relative; }
|
|
44
|
+
/* the same look as a selected option: surface, lift and the 3:1 inset ring */
|
|
45
|
+
.uix-segmented__indicator { position: absolute; z-index: calc(-1 * var(--uix-z-raised)); pointer-events: none; transform-origin: 0 0; background: var(--uix-surface); box-shadow: var(--uix-shadow-sm), inset 0 0 0 1px var(--uix-text-muted); border-radius: calc(var(--uix-radius-md) - 3px); }
|
|
46
|
+
.uix-segmented__indicator[data-moving] { transition: transform var(--uix-dur) var(--uix-ease-in-out); }
|
|
47
|
+
@media (forced-colors: none) {
|
|
48
|
+
.uix-segmented[data-uix-indicator] .uix-segmented__option:is([aria-pressed="true"], [aria-checked="true"]) { background: transparent; box-shadow: none; }
|
|
49
|
+
}
|
|
50
|
+
@media (forced-colors: active) { .uix-segmented__indicator { display: none; } }
|
|
30
51
|
}
|
|
@@ -66,8 +66,15 @@
|
|
|
66
66
|
}
|
|
67
67
|
.uix-select__popup:not([hidden]) { display: flex; }
|
|
68
68
|
[popover].uix-select__popup:not(:popover-open) { display: none; }
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
/* grows out of the trigger (--uix-anchor-origin from useAnchoredPosition) at --uix-dur-fast; the
|
|
70
|
+
closed state carries the faster exit. Opened from the keyboard it does not animate (motion.css). */
|
|
71
|
+
[popover].uix-select__popup {
|
|
72
|
+
opacity: 0; transform: scale(.95); transform-origin: var(--uix-anchor-origin, top left);
|
|
73
|
+
transition: opacity var(--uix-dur-exit-fast) var(--uix-ease-out), transform var(--uix-dur-exit-fast) var(--uix-ease-out),
|
|
74
|
+
overlay var(--uix-dur-exit-fast) allow-discrete, display var(--uix-dur-exit-fast) allow-discrete;
|
|
75
|
+
}
|
|
76
|
+
[popover].uix-select__popup:popover-open { opacity: 1; transform: none; transition-duration: var(--uix-dur-fast); }
|
|
77
|
+
@starting-style { [popover].uix-select__popup:popover-open { opacity: 0; transform: scale(.95); } }
|
|
71
78
|
.uix-select__listbox { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; position: relative; }
|
|
72
79
|
.uix-select__popup .uix-listbox { min-width: 0; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none; }
|
|
73
80
|
.uix-select__popup .uix-listbox:focus { outline: none; }
|
|
@@ -27,3 +27,48 @@
|
|
|
27
27
|
@layer uix.components {
|
|
28
28
|
@media (prefers-reduced-motion: no-preference) { :root[data-motion="reduce"] .uix-skeleton { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
/* uix success state (HAR-2588): the outcome of a rare, important journey — a request
|
|
32
|
+
submitted. The mark draws once (the disc settles in, then the check is drawn), never repeats,
|
|
33
|
+
never bounces; reduced motion shows it at once. Then the title (focused on mount), the
|
|
34
|
+
reference, what happens next and the next action. */
|
|
35
|
+
@layer uix.components {
|
|
36
|
+
.uix-success { gap: var(--uix-space-4); color: var(--uix-text); }
|
|
37
|
+
.uix-success__mark { inline-size: var(--uix-space-12); block-size: var(--uix-space-12); }
|
|
38
|
+
.uix-success__mark svg { inline-size: 100%; block-size: 100%; overflow: visible; }
|
|
39
|
+
.uix-success__disc { fill: var(--uix-success-bg); stroke: var(--uix-success); stroke-width: 1.5; transform-box: fill-box; transform-origin: center; }
|
|
40
|
+
.uix-success__check { stroke: var(--uix-success-text); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1; stroke-dashoffset: 0; }
|
|
41
|
+
@keyframes uix-success-disc { from { opacity: 0; transform: scale(.85); } }
|
|
42
|
+
/* hidden until it starts drawing: a round cap on the zero-length dash would show as a dot */
|
|
43
|
+
@keyframes uix-success-check { 0% { stroke-dashoffset: 1; opacity: 0; } 1% { opacity: 1; } }
|
|
44
|
+
.uix-success__disc { animation: uix-success-disc var(--uix-dur-slow) var(--uix-ease-out) both; }
|
|
45
|
+
.uix-success__check { animation: uix-success-check var(--uix-dur-slower) var(--uix-ease-out) var(--uix-dur-fast) both; }
|
|
46
|
+
@media (prefers-reduced-motion: reduce) {
|
|
47
|
+
/* no settle, no drawing: the disc fades in with the check already there */
|
|
48
|
+
.uix-success__disc { animation: uix-fade-in var(--uix-dur) var(--uix-ease-out) both !important; }
|
|
49
|
+
.uix-success__check { animation: none !important; }
|
|
50
|
+
}
|
|
51
|
+
.uix-success__title { margin: 0; }
|
|
52
|
+
.uix-success__title:focus { outline: none; }
|
|
53
|
+
.uix-success__title:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: var(--uix-space-1); border-radius: var(--uix-radius-sm); }
|
|
54
|
+
.uix-success__body { max-inline-size: 52ch; color: var(--uix-text-muted); }
|
|
55
|
+
.uix-success__body > p { margin: 0; }
|
|
56
|
+
.uix-success__reference { margin: 0; color: var(--uix-text-muted); }
|
|
57
|
+
.uix-success__reference-label { font-size: var(--uix-text-meta); }
|
|
58
|
+
.uix-success__reference-value { display: inline-flex; align-items: center; gap: var(--uix-space-1); font-family: var(--uix-font-mono); font-weight: 600; color: var(--uix-text); }
|
|
59
|
+
.uix-success__next { inline-size: min(100%, 28rem); padding: var(--uix-space-4); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface); text-align: start; }
|
|
60
|
+
.uix-success__next-title { margin: 0 0 var(--uix-space-2); font-size: var(--uix-text-body); font-weight: 600; }
|
|
61
|
+
.uix-success__steps { margin: 0; padding-inline-start: var(--uix-space-5); display: grid; gap: var(--uix-space-2); color: var(--uix-text-muted); }
|
|
62
|
+
@media (forced-colors: active) {
|
|
63
|
+
.uix-success__disc { fill: Canvas; stroke: CanvasText; }
|
|
64
|
+
.uix-success__check { stroke: CanvasText; }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* the in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">): the mark is simply there */
|
|
69
|
+
@layer uix.components {
|
|
70
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
71
|
+
:root[data-motion="reduce"] .uix-success__disc { animation: uix-fade-in var(--uix-dur) var(--uix-ease-out) both !important; }
|
|
72
|
+
:root[data-motion="reduce"] .uix-success__check { animation: none !important; }
|
|
73
|
+
}
|
|
74
|
+
}
|