@tensor_1/tokens 2.45.1 → 2.46.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.
@@ -116,6 +116,18 @@
116
116
  --uix-dur: 160ms;
117
117
  --uix-dur-slow: 240ms;
118
118
  --uix-dur-slower: 320ms;
119
+ --uix-dur-exit-fast: 80ms;
120
+ --uix-dur-exit: 110ms;
121
+ --uix-dur-exit-slow: 160ms;
122
+ --uix-dur-highlight: 2400ms;
123
+ --uix-delay-tooltip: 400ms;
124
+ --uix-delay-tooltip-skip: 300ms;
125
+ --uix-loop-spin: 600ms;
126
+ --uix-loop-progress: 1200ms;
127
+ --uix-loop-shimmer: 1400ms;
128
+ --uix-loop-ping: 1600ms;
129
+ --uix-loop-blink: 1100ms;
130
+ --uix-loop-pulse: 2000ms;
119
131
  --uix-lift: 4px;
120
132
  --uix-z-raised: 1;
121
133
  --uix-z-sticky: 3;
@@ -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" | "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" | "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>>;
@@ -117,6 +117,18 @@ export const cssVar = {
117
117
  "dur": "var(--uix-dur)",
118
118
  "dur-slow": "var(--uix-dur-slow)",
119
119
  "dur-slower": "var(--uix-dur-slower)",
120
+ "dur-exit-fast": "var(--uix-dur-exit-fast)",
121
+ "dur-exit": "var(--uix-dur-exit)",
122
+ "dur-exit-slow": "var(--uix-dur-exit-slow)",
123
+ "dur-highlight": "var(--uix-dur-highlight)",
124
+ "delay-tooltip": "var(--uix-delay-tooltip)",
125
+ "delay-tooltip-skip": "var(--uix-delay-tooltip-skip)",
126
+ "loop-spin": "var(--uix-loop-spin)",
127
+ "loop-progress": "var(--uix-loop-progress)",
128
+ "loop-shimmer": "var(--uix-loop-shimmer)",
129
+ "loop-ping": "var(--uix-loop-ping)",
130
+ "loop-blink": "var(--uix-loop-blink)",
131
+ "loop-pulse": "var(--uix-loop-pulse)",
120
132
  "lift": "var(--uix-lift)",
121
133
  "z-raised": "var(--uix-z-raised)",
122
134
  "z-sticky": "var(--uix-z-sticky)",
@@ -296,6 +308,18 @@ export const light = {
296
308
  "dur": "160ms",
297
309
  "dur-slow": "240ms",
298
310
  "dur-slower": "320ms",
311
+ "dur-exit-fast": "80ms",
312
+ "dur-exit": "110ms",
313
+ "dur-exit-slow": "160ms",
314
+ "dur-highlight": "2400ms",
315
+ "delay-tooltip": "400ms",
316
+ "delay-tooltip-skip": "300ms",
317
+ "loop-spin": "600ms",
318
+ "loop-progress": "1200ms",
319
+ "loop-shimmer": "1400ms",
320
+ "loop-ping": "1600ms",
321
+ "loop-blink": "1100ms",
322
+ "loop-pulse": "2000ms",
299
323
  "lift": "4px",
300
324
  "z-raised": "1",
301
325
  "z-sticky": "3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensor_1/tokens",
3
- "version": "2.45.1",
3
+ "version": "2.46.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": [
@@ -47,7 +47,7 @@
47
47
  "build:styles": "node scripts/build-styles.mjs",
48
48
  "build": "npm run build:tokens && npm run build:themes && npm run build:styles",
49
49
  "test:parity": "node scripts/check-parity.mjs",
50
- "test:contract": "node scripts/check-contract.mjs",
50
+ "test:contract": "node scripts/check-contract.mjs && node scripts/check-motion.mjs",
51
51
  "docs:serve": "serve -l 4178 ../..",
52
52
  "size:css": "node scripts/size-report.mjs --check",
53
53
  "size:css:update": "node scripts/size-report.mjs --update",
@@ -7,9 +7,9 @@
7
7
  grid-template-rows: var(--uix-topbar-h) 1fr;
8
8
  height: 100%; min-height: 360px;
9
9
  border: 1px solid var(--uix-border); border-radius: var(--uix-radius-lg); overflow: hidden;
10
- transition: grid-template-columns var(--uix-dur) var(--uix-ease-out), grid-template-rows var(--uix-dur) var(--uix-ease-out);
10
+ /* No transition: collapsing the nav is frequent, so the tracks switch instantly (grid tracks never animate). */
11
11
  }
12
- .uix-shell__sidebar { grid-row: 1 / 3; background: var(--uix-bg-subtle); border-right: 1px solid var(--uix-border); overflow-y: auto; overflow-x: hidden; visibility: visible; transition: visibility var(--uix-dur) var(--uix-ease-out); }
12
+ .uix-shell__sidebar { grid-row: 1 / 3; background: var(--uix-bg-subtle); border-right: 1px solid var(--uix-border); overflow-y: auto; overflow-x: hidden; visibility: visible; }
13
13
  .uix-shell__topbar { grid-column: 2; display: flex; align-items: center; gap: var(--uix-space-3); padding: 0 var(--uix-space-6); background: var(--uix-bg-app); border-bottom: 1px solid var(--uix-border); }
14
14
  .uix-shell__main { grid-column: 2; overflow: auto; padding: var(--uix-space-7); background: var(--uix-bg-app); }
15
15
 
@@ -19,7 +19,7 @@
19
19
  .uix-shell[data-nav="rail"], .uix-shell[data-collapsed] { grid-template-columns: var(--uix-sidebar-w-rail) 1fr; }
20
20
  .uix-shell[data-nav="hidden"] { grid-template-columns: 0 1fr; }
21
21
  /* the 0-width column still renders its links — visibility removes them from the tab
22
- order / accessibility tree while keeping the width transition (UIX-A11Y-2) */
22
+ order / accessibility tree (UIX-A11Y-2) */
23
23
  .uix-shell[data-nav="hidden"] .uix-shell__sidebar { visibility: hidden; }
24
24
 
25
25
  /* ── focus / immersive mode ───────────────────────────────────────────────────
@@ -6,23 +6,24 @@
6
6
  .uix-arrival {
7
7
  border-radius: var(--uix-radius-md);
8
8
  scroll-margin-block: var(--uix-space-10);
9
- animation: uix-arrival 2.4s var(--uix-ease-out) both;
9
+ animation: uix-arrival var(--uix-dur-highlight) var(--uix-ease-out) both;
10
10
  }
11
11
  @keyframes uix-arrival {
12
- 0%, 40% { background-color: var(--uix-brand-muted); box-shadow: 0 0 0 var(--uix-space-2) var(--uix-brand-muted); }
13
- 100% { background-color: transparent; box-shadow: 0 0 0 var(--uix-space-2) transparent; }
12
+ /* the outline carries the tint past the element's edge; only its colour fades */
13
+ 0%, 40% { background-color: var(--uix-brand-muted); outline: var(--uix-space-2) solid var(--uix-brand-muted); outline-offset: 0; }
14
+ 100% { background-color: transparent; outline: var(--uix-space-2) solid transparent; outline-offset: 0; }
14
15
  }
15
16
 
16
- /* no fade: a steady outline for two seconds, then nothing */
17
+ /* no fade: a steady outline for the highlight time, then nothing */
17
18
  @keyframes uix-arrival-static {
18
19
  0% { outline: 2px solid var(--uix-ring); outline-offset: var(--uix-space-1); }
19
20
  100% { outline: 2px solid transparent; outline-offset: var(--uix-space-1); }
20
21
  }
21
22
  @media (prefers-reduced-motion: reduce) {
22
- .uix-arrival { animation: uix-arrival-static 2s step-end both; }
23
+ .uix-arrival { animation: uix-arrival-static var(--uix-dur-highlight) step-end both; }
23
24
  }
24
25
 
25
26
  @media (forced-colors: active) {
26
- .uix-arrival { animation: uix-arrival-static 2s step-end both; }
27
+ .uix-arrival { animation: uix-arrival-static var(--uix-dur-highlight) step-end both; }
27
28
  }
28
29
  }
@@ -30,7 +30,8 @@
30
30
  .uix-avatar--editable { cursor: pointer; }
31
31
  .uix-avatar__edit { position: absolute; inset: 0; display: grid; place-items: center; background: var(--uix-scrim); color: var(--uix-text-reverse); opacity: 0; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
32
32
  .uix-avatar__edit svg { width: 45%; height: 45%; }
33
- .uix-avatar--editable:hover .uix-avatar__edit, .uix-avatar--editable:focus-visible .uix-avatar__edit { opacity: 1; }
33
+ .uix-avatar--editable:focus-visible .uix-avatar__edit { opacity: 1; }
34
+ @media (hover: hover) and (pointer: fine) { .uix-avatar--editable:hover .uix-avatar__edit { opacity: 1; } }
34
35
  .uix-avatar--empty { background: var(--uix-bg-subtle); border: 1.5px dashed var(--uix-border-strong); color: var(--uix-text-muted); }
35
36
  }
36
37
 
@@ -60,8 +60,9 @@
60
60
  .uix-btn[data-loading]::after {
61
61
  content: ""; position: absolute; width: 1em; height: 1em;
62
62
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
63
- color: var(--uix-text); animation: uix-spin .6s linear infinite;
63
+ color: var(--uix-text); animation: uix-spin var(--uix-loop-spin) linear infinite;
64
64
  }
65
+ @media (prefers-reduced-motion: reduce) { .uix-btn[data-loading]::after { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
65
66
  .uix-btn--primary[data-loading]::after { color: var(--uix-accent-fg); }
66
67
  .uix-btn--danger[data-loading]::after { color: var(--uix-danger-fg); }
67
68
  .uix-btn--link[data-loading]::after { color: var(--uix-link); }
@@ -8,8 +8,10 @@
8
8
  .uix-card__body { padding: var(--uix-space-6); }
9
9
  /* wraps at 320 px (WCAG 1.4.10) */
10
10
  .uix-card__footer { padding: var(--uix-space-4) var(--uix-space-6); border-top: 1px solid var(--uix-border); display: flex; flex-wrap: wrap; gap: var(--uix-space-2); justify-content: flex-end; }
11
- .uix-card--interactive { display: block; color: inherit; text-decoration: none; cursor: pointer; transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out), transform var(--uix-dur-fast) var(--uix-ease-out); }
12
- .uix-card--interactive:is(:hover, :focus-visible) { border-color: var(--uix-border-strong); box-shadow: var(--uix-shadow-md); text-decoration: none; transform: translateY(-1px); }
11
+ .uix-card--interactive { display: block; color: inherit; text-decoration: none; cursor: pointer; transition: border-color var(--uix-dur-fast) var(--uix-ease-out), transform var(--uix-dur-fast) var(--uix-ease-out); }
12
+ /* The shadow steps (box-shadow never animates); the lift is hover-only, and keyboard focus gets no movement. */
13
+ .uix-card--interactive:focus-visible { border-color: var(--uix-border-strong); box-shadow: var(--uix-shadow-md); text-decoration: none; }
14
+ @media (hover: hover) and (pointer: fine) { .uix-card--interactive:hover { border-color: var(--uix-border-strong); box-shadow: var(--uix-shadow-md); text-decoration: none; transform: translateY(-1px); } }
13
15
  .uix-card--interactive:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
14
16
  .uix-collapsible { border: 1px solid var(--uix-border); border-radius: var(--uix-radius-lg); background: var(--uix-surface); }
15
17
  .uix-collapsible__summary { display: flex; align-items: center; justify-content: space-between; gap: var(--uix-space-3); padding: var(--uix-space-4) var(--uix-space-5); cursor: pointer; list-style: none; }
@@ -67,7 +67,7 @@
67
67
  .uix-node--active, .uix-node--running { box-shadow: var(--uix-shadow-sm), 0 0 0 3px var(--tone-bg); }
68
68
 
69
69
  /* running node spins its icon */
70
- .uix-node--running .uix-node__icon svg { animation: uix-flow-spin 1s linear infinite; }
70
+ .uix-node--running .uix-node__icon svg { animation: uix-flow-spin var(--uix-loop-spin) linear infinite; }
71
71
 
72
72
  /* ---- decision / diamond ---- */
73
73
  .uix-node--decision {
@@ -175,8 +175,7 @@
175
175
  border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md);
176
176
  background: var(--uix-surface); color: var(--uix-text);
177
177
  font-size: var(--uix-text-meta); cursor: grab; box-shadow: var(--uix-shadow-sm);
178
- transition: box-shadow var(--uix-dur-fast) var(--uix-ease-out),
179
- transform var(--uix-dur-fast) var(--uix-ease-out);
178
+ transition: transform var(--uix-dur-fast) var(--uix-ease-out);
180
179
  }
181
180
  .uix-flow__chip svg { width: var(--uix-icon-sm); height: var(--uix-icon-sm); color: var(--uix-text-muted); flex: none; }
182
181
  .uix-flow__chip:active { cursor: grabbing; }
@@ -210,7 +209,8 @@
210
209
  @keyframes uix-flow-march { to { stroke-dashoffset: -18; } }
211
210
 
212
211
  @media (prefers-reduced-motion: reduce) {
213
- .uix-node--running .uix-node__icon svg,
212
+ /* a running node keeps showing it runs: the icon breathes instead of turning */
213
+ .uix-node--running .uix-node__icon svg { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; }
214
214
  .uix-flow:hover .uix-flow__edge,
215
215
  .uix-flow:focus-within .uix-flow__edge { animation: none; }
216
216
  }
@@ -1,12 +1,17 @@
1
- /* uix heartbeat / live status indicator — a pulsing ping. Respects reduced-motion (guard stops the ping). */
1
+ /* uix heartbeat / live status indicator — a pulsing ping. Under reduced motion the ping (it grows) goes
2
+ and the dot itself breathes, so "live" is still visible. */
2
3
  @layer uix.components {
3
4
  .uix-heartbeat { position: relative; display: inline-grid; place-items: center; width: 8px; height: 8px; flex: none; }
4
5
  .uix-heartbeat__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--uix-success); z-index: var(--uix-z-raised); }
5
- .uix-heartbeat__ping { position: absolute; inset: 0; border-radius: 50%; background: var(--uix-success); animation: uix-ping 1.6s var(--uix-ease-out) infinite; }
6
+ .uix-heartbeat__ping { position: absolute; inset: 0; border-radius: 50%; background: var(--uix-success); animation: uix-ping var(--uix-loop-ping) var(--uix-ease-out) infinite; }
6
7
  @keyframes uix-ping { 0% { transform: scale(1); opacity: .55; } 100% { transform: scale(2.8); opacity: 0; } }
7
8
  .uix-heartbeat--warning .uix-heartbeat__dot, .uix-heartbeat--warning .uix-heartbeat__ping { background: var(--uix-warning); }
8
9
  .uix-heartbeat--danger .uix-heartbeat__dot, .uix-heartbeat--danger .uix-heartbeat__ping { background: var(--uix-danger); }
9
10
  .uix-heartbeat--idle .uix-heartbeat__dot { background: var(--uix-text-muted); }
10
11
  .uix-heartbeat--idle .uix-heartbeat__ping { display: none; }
12
+ @media (prefers-reduced-motion: reduce) {
13
+ .uix-heartbeat__ping { display: none; }
14
+ .uix-heartbeat:not(.uix-heartbeat--idle) .uix-heartbeat__dot { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; }
15
+ }
11
16
  .uix-live { display: inline-flex; align-items: center; gap: var(--uix-space-2); font-size: var(--uix-text-meta); font-weight: 500; color: var(--uix-text-hushed); }
12
17
  }
@@ -19,7 +19,8 @@
19
19
  .uix-inline-edit__placeholder { color: var(--uix-text-muted); }
20
20
  /* 75% at rest keeps the pencil above 3:1 (the same floor as the view menu's quiet controls). */
21
21
  .uix-inline-edit__icon { display: inline-flex; flex: none; color: var(--uix-text-muted); opacity: .75; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
22
- .uix-inline-edit__view:is(:hover, :focus-visible) .uix-inline-edit__icon { opacity: 1; }
22
+ .uix-inline-edit__view:focus-visible .uix-inline-edit__icon { opacity: 1; }
23
+ @media (hover: hover) and (pointer: fine) { .uix-inline-edit__view:hover .uix-inline-edit__icon { opacity: 1; } }
23
24
  .uix-inline-edit__editor { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--uix-space-2); flex: 1; min-width: 0; }
24
25
  .uix-inline-edit__control { flex: 1 1 12rem; min-width: 0; }
25
26
  .uix-inline-edit__control > * { width: 100%; }
@@ -20,7 +20,7 @@
20
20
  height: var(--uix-control-h); width: 100%;
21
21
  padding: 0 var(--uix-space-3); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md);
22
22
  background: var(--uix-surface); color: var(--uix-text); font: inherit; font-size: var(--uix-text-body);
23
- transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out);
23
+ transition: border-color var(--uix-dur-fast) var(--uix-ease-out);
24
24
  }
25
25
  .uix-input::placeholder { color: var(--uix-text-muted); }
26
26
  .uix-input:hover { border-color: var(--uix-border-strong); }
@@ -5,9 +5,8 @@
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
  .uix-kanban__body { padding: 0 var(--uix-space-3) var(--uix-space-3); display: flex; flex-direction: column; gap: var(--uix-space-2); overflow-y: auto; }
8
- .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: box-shadow var(--uix-dur-fast) var(--uix-ease-out), transform var(--uix-dur-fast) var(--uix-ease-out); }
9
- .uix-kanban__card:hover { box-shadow: var(--uix-shadow-popover); }
10
- @media (hover: hover) and (pointer: fine) { .uix-kanban__card:hover { transform: translateY(calc(-1 * var(--uix-lift))); } }
8
+ .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
+ @media (hover: hover) and (pointer: fine) { .uix-kanban__card:hover { box-shadow: var(--uix-shadow-popover); transform: translateY(calc(-1 * var(--uix-lift))); } }
11
10
  .uix-kanban__card:active { cursor: grabbing; }
12
11
  .uix-kanban__card-title { font-weight: 500; font-size: var(--uix-text-body); margin-bottom: var(--uix-space-2); }
13
12
  .uix-kanban__card-meta { display: flex; align-items: center; gap: var(--uix-space-2); }
@@ -1,7 +1,11 @@
1
1
  /* uix meter / utilization bar (threshold tones) — depends only on --uix-* tokens. */
2
2
  @layer uix.components {
3
3
  .uix-meter { height: 8px; background: var(--uix-bg-active); border-radius: var(--uix-radius-pill); overflow: hidden; }
4
- .uix-meter__fill { height: 100%; border-radius: inherit; background: var(--uix-success); transition: width var(--uix-dur) var(--uix-ease-out); }
4
+ /* --val (0–1) slides a full-width fill into the clipped track: transform, never width (see progress.css).
5
+ A legacy inline `width: n%` still works: --val then defaults to 1 and nothing slides. */
6
+ .uix-meter__fill { width: 100%; height: 100%; border-radius: inherit; background: var(--uix-success);
7
+ transform: translateX(calc((var(--val, 1) - 1) * 100%)); transition: transform var(--uix-dur) var(--uix-ease-out); }
8
+ .uix-meter__fill:dir(rtl) { transform: translateX(calc((1 - var(--val, 1)) * 100%)); }
5
9
  .uix-meter__fill[data-tone="warning"] { background: var(--uix-warning); }
6
10
  .uix-meter__fill[data-tone="danger"] { background: var(--uix-danger); }
7
11
  .uix-meter__fill[data-tone="attention"] { background: var(--uix-attention); }
@@ -90,7 +90,7 @@
90
90
  border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md);
91
91
  background: var(--uix-surface); color: var(--uix-text); box-shadow: var(--uix-shadow-sm);
92
92
  text-align: left; cursor: pointer;
93
- transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out);
93
+ transition: border-color var(--uix-dur-fast) var(--uix-ease-out);
94
94
  }
95
95
  .uix-org-chart__item > .uix-org-chart__node:hover { border-color: var(--uix-border-strong); }
96
96
  .uix-org-chart__item[aria-selected="true"] > .uix-org-chart__node {
@@ -1,10 +1,16 @@
1
1
  /* uix progress bar + ring — depends only on --uix-* tokens. */
2
2
  @layer uix.components {
3
3
  .uix-progress { height: 8px; background: var(--uix-bg-active); border-radius: var(--uix-radius-pill); overflow: hidden; }
4
- .uix-progress__bar { height: 100%; background: var(--uix-accent); border-radius: inherit; transition: width var(--uix-dur) var(--uix-ease-out); }
4
+ /* --val (0–1) slides a full-width bar into the clipped track: transform, never width, and the rounded
5
+ leading end survives. A legacy inline `width: n%` still works (--val then defaults to 1, no slide). */
6
+ .uix-progress__bar { width: 100%; height: 100%; background: var(--uix-accent); border-radius: inherit;
7
+ transform: translateX(calc((var(--val, 1) - 1) * 100%)); transition: transform var(--uix-dur) var(--uix-ease-out); }
8
+ .uix-progress__bar:dir(rtl) { transform: translateX(calc((1 - var(--val, 1)) * 100%)); }
5
9
  .uix-progress--indeterminate { position: relative; }
6
- .uix-progress--indeterminate .uix-progress__bar { width: 40%; animation: uix-progress-slide 1.2s var(--uix-ease-in-out) infinite; }
10
+ .uix-progress--indeterminate .uix-progress__bar { width: 40%; animation: uix-progress-slide var(--uix-loop-progress) var(--uix-ease-in-out) infinite; }
7
11
  @keyframes uix-progress-slide { 0% { transform: translateX(-110%); } 100% { transform: translateX(280%); } }
12
+ /* reduced motion: the bar fills the track and breathes instead of sweeping */
13
+ @media (prefers-reduced-motion: reduce) { .uix-progress--indeterminate .uix-progress__bar { width: 100%; animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
8
14
 
9
15
  .uix-ring { width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
10
16
  background: conic-gradient(var(--uix-accent) calc(var(--val, .66) * 360deg), var(--uix-bg-active) 0); }
@@ -28,7 +28,7 @@
28
28
  .uix-radio-card {
29
29
  position: relative; display: flex; flex-direction: column; gap: var(--uix-space-3); min-width: 0; padding: var(--uix-space-4);
30
30
  border: 1px solid var(--uix-border-strong); border-radius: var(--uix-radius-lg); background: var(--uix-surface); color: var(--uix-text); cursor: pointer;
31
- transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out);
31
+ transition: border-color var(--uix-dur-fast) var(--uix-ease-out);
32
32
  }
33
33
  .uix-radio-card > input { position: absolute; opacity: 0; width: 1px; height: 1px; }
34
34
  .uix-radio-card:hover { border-color: var(--uix-text-muted); }
@@ -5,7 +5,7 @@
5
5
  .uix-rich-text {
6
6
  display: flex; flex-direction: column; min-width: 0;
7
7
  border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface);
8
- transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out);
8
+ transition: border-color var(--uix-dur-fast) var(--uix-ease-out);
9
9
  }
10
10
  .uix-rich-text:hover { border-color: var(--uix-border-strong); }
11
11
  .uix-rich-text:focus-within { border-color: var(--uix-ring); box-shadow: 0 0 0 3px var(--uix-brand-muted); }
@@ -66,7 +66,7 @@
66
66
  .uix-rich-text__content .ProseMirror-gapcursor { display: none; position: absolute; margin: 0; pointer-events: none; }
67
67
  .uix-rich-text__content .ProseMirror-gapcursor::after {
68
68
  content: ""; display: block; position: absolute; top: -2px; width: 20px; border-top: 1px solid var(--uix-text);
69
- animation: uix-rich-text-caret 1.1s steps(2, start) infinite;
69
+ animation: uix-rich-text-caret var(--uix-loop-blink) steps(2, start) infinite;
70
70
  }
71
71
  .uix-rich-text__content.ProseMirror-focused .ProseMirror-gapcursor { display: block; }
72
72
  .uix-rich-text__content.ProseMirror-hideselection *::selection { background: transparent; }
@@ -181,5 +181,7 @@
181
181
 
182
182
  @media (prefers-reduced-motion: reduce) {
183
183
  .uix-rich-text, .uix-rich-text__tool { transition: none; }
184
+ /* a steady gap cursor: no blink */
185
+ .uix-rich-text__content .ProseMirror-gapcursor::after { animation: none; }
184
186
  }
185
187
  }
@@ -7,12 +7,12 @@
7
7
  .uix-save-status__icon { width: var(--uix-space-2); height: var(--uix-space-2); flex-shrink: 0; border-radius: var(--uix-radius-pill); }
8
8
  .uix-save-status[data-state="idle"] .uix-save-status__icon { display: none; }
9
9
 
10
- .uix-save-status[data-state="saving"] .uix-save-status__icon { border: 1px solid var(--uix-border-strong); border-top-color: transparent; animation: uix-spin var(--uix-dur-slower) linear infinite; }
10
+ .uix-save-status[data-state="saving"] .uix-save-status__icon { border: 1px solid var(--uix-border-strong); border-top-color: transparent; animation: uix-spin var(--uix-loop-spin) linear infinite; }
11
11
  .uix-save-status[data-state="saved"] .uix-save-status__icon { background: var(--uix-success); }
12
12
  .uix-save-status[data-state="failed"] { color: var(--uix-danger-text); }
13
13
  .uix-save-status[data-state="failed"] .uix-save-status__icon { background: var(--uix-danger); }
14
14
 
15
15
  @media (prefers-reduced-motion: reduce) {
16
- .uix-save-status[data-state="saving"] .uix-save-status__icon { animation: none; }
16
+ .uix-save-status[data-state="saving"] .uix-save-status__icon { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; }
17
17
  }
18
18
  }
@@ -8,7 +8,7 @@
8
8
  background-color: var(--uix-surface); color: var(--uix-text); font: inherit; font-size: var(--uix-text-body); cursor: pointer;
9
9
  background-repeat: no-repeat; background-position: right 12px center;
10
10
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23737373' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
11
- transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out);
11
+ transition: border-color var(--uix-dur-fast) var(--uix-ease-out);
12
12
  }
13
13
  .uix-select:hover { border-color: var(--uix-border-strong); }
14
14
  .uix-select:focus { outline: none; border-color: var(--uix-ring); box-shadow: 0 0 0 3px var(--uix-brand-muted); }
@@ -40,11 +40,16 @@
40
40
  .uix-navitem[aria-current="page"] .uix-navitem__desc { color: var(--uix-text-hushed); }
41
41
  /* on the active tint, muted badge dips below AA — hushed composites darker on the tint */
42
42
  .uix-navitem[aria-current="page"] .uix-navitem__badge { color: var(--uix-text-hushed); }
43
- .uix-navitem__star { position: relative; display: grid; place-items: center; width: 22px; height: 22px; border: 0; background: transparent; color: var(--uix-text-muted); cursor: pointer; border-radius: var(--uix-radius-xs); opacity: 0; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
43
+ .uix-navitem__star { position: relative; display: grid; place-items: center; width: 22px; height: 22px; border: 0; background: transparent; color: var(--uix-text-muted); cursor: pointer; border-radius: var(--uix-radius-xs); transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
44
44
  /* invisible halo → 24px effective target with zero visual change (UIX-A11Y-2) */
45
45
  .uix-navitem__star::before { content: ""; position: absolute; inset: -1px; }
46
46
  .uix-navitem__star svg { width: 15px; height: 15px; }
47
- .uix-navitem:hover .uix-navitem__star, .uix-navitem:focus-within .uix-navitem__star, .uix-navitem__star:focus-visible { opacity: 1; }
47
+ /* Hidden until hover only where hover exists; a touch screen always shows the star. */
48
+ @media (hover: hover) and (pointer: fine) {
49
+ .uix-navitem__star { opacity: 0; }
50
+ .uix-navitem:hover .uix-navitem__star { opacity: 1; }
51
+ }
52
+ .uix-navitem:focus-within .uix-navitem__star, .uix-navitem__star:focus-visible { opacity: 1; }
48
53
  .uix-navitem__star[data-on] { opacity: 1; color: var(--uix-warning); }
49
54
  .uix-navitem__star[data-on] svg { fill: currentColor; }
50
55
 
@@ -64,9 +69,12 @@
64
69
  margin-left: auto; display: inline-flex; height: 20px; width: 20px; flex-shrink: 0;
65
70
  align-items: center; justify-content: center; border: 0; border-radius: calc(var(--uix-radius-xs) / 2);
66
71
  background: transparent; cursor: pointer; color: var(--uix-text-muted);
67
- opacity: 0; transition: opacity var(--uix-dur-fast) var(--uix-ease-out);
72
+ transition: opacity var(--uix-dur-fast) var(--uix-ease-out);
73
+ }
74
+ @media (hover: hover) and (pointer: fine) {
75
+ .uix-favourites__menu-trigger { opacity: 0; }
76
+ .uix-favourites__row:hover .uix-favourites__menu-trigger { opacity: 1; }
68
77
  }
69
- .uix-favourites__row:hover .uix-favourites__menu-trigger,
70
78
  .uix-favourites__row:focus-within .uix-favourites__menu-trigger,
71
79
  .uix-favourites__menu-trigger[aria-expanded="true"] { opacity: 1; }
72
80
  .uix-favourites__backdrop { position: fixed; inset: 0; cursor: default; background: transparent; z-index: calc(var(--uix-z-tooltip) + 10); }
@@ -91,12 +99,12 @@
91
99
  .uix-navgroup__trigger .uix-navgroup__chevron { margin-left: auto; transition: transform var(--uix-dur) var(--uix-ease-out); }
92
100
  .uix-navgroup__trigger[aria-expanded="false"] .uix-navgroup__chevron { transform: rotate(-90deg); }
93
101
  .uix-navgroup__chevron { width: var(--uix-icon-sm); height: var(--uix-icon-sm); }
94
- .uix-navgroup__panel { display: grid; grid-template-rows: 1fr; transition: grid-template-rows var(--uix-dur) var(--uix-ease-out); }
102
+ /* A nav group opens and closes instantly: it is toggled many times a day, and grid tracks never
103
+ animate. The chevron still turns, so the change is visible. */
104
+ .uix-navgroup__panel { display: grid; grid-template-rows: 1fr; }
95
105
  .uix-navgroup__trigger[aria-expanded="false"] + .uix-navgroup__panel { grid-template-rows: 0fr; }
96
- /* visibility keeps the 0fr-collapsed links out of the tab order / accessibility tree
97
- (UIX-A11Y-2); the transition delays hiding until the row collapse finishes, and
98
- visibility snaps back to visible instantly on expand, so nothing visibly glitches. */
99
- .uix-navgroup__panel > div { overflow: hidden; visibility: visible; transition: visibility var(--uix-dur) var(--uix-ease-out); }
106
+ /* visibility keeps the 0fr-collapsed links out of the tab order / accessibility tree (UIX-A11Y-2) */
107
+ .uix-navgroup__panel > div { overflow: hidden; visibility: visible; }
100
108
  .uix-navgroup__trigger[aria-expanded="false"] + .uix-navgroup__panel > div { visibility: hidden; }
101
109
  .uix-subitem { padding-left: var(--uix-space-5); }
102
110
  .uix-subitem .uix-navitem__label::before { content: ""; display: inline-block; width: 4px; height: 4px; border-radius: 50%; background: var(--uix-text-muted); margin-right: var(--uix-space-3); vertical-align: middle; }
@@ -1,6 +1,8 @@
1
1
  /* uix spinner + progress overlay — depends only on --uix-* tokens. */
2
2
  @layer uix.components {
3
- .uix-spinner { width: 20px; height: 20px; border: 2px solid var(--uix-border-strong); border-right-color: transparent; border-radius: 50%; display: inline-block; animation: uix-spin .6s linear infinite; }
3
+ .uix-spinner { width: 20px; height: 20px; border: 2px solid var(--uix-border-strong); border-right-color: transparent; border-radius: 50%; display: inline-block; animation: uix-spin var(--uix-loop-spin) linear infinite; }
4
+ /* reduced motion: the ring stops turning and breathes instead, so "still working" survives */
5
+ @media (prefers-reduced-motion: reduce) { .uix-spinner { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
4
6
  .uix-spinner--accent { border-color: var(--uix-accent); border-right-color: transparent; }
5
7
  .uix-spinner--lg { width: 32px; height: 32px; border-width: 3px; }
6
8
  /* HAR-1606: inline, beside a file name or inside a 24 px toggle; the 2 px stroke is kept. */
@@ -13,7 +13,8 @@
13
13
  .uix-empty__icon--warning { background: var(--uix-warning-bg); color: var(--uix-warning-text); }
14
14
  .uix-stack--compact { gap: var(--uix-space-2); }
15
15
 
16
- .uix-skeleton { background: linear-gradient(90deg, var(--uix-bg-subtle) 25%, var(--uix-bg-hover) 50%, var(--uix-bg-subtle) 75%); background-size: 200% 100%; animation: uix-shimmer 1.4s linear infinite; border-radius: var(--uix-radius-sm); }
16
+ .uix-skeleton { background: linear-gradient(90deg, var(--uix-bg-subtle) 25%, var(--uix-bg-hover) 50%, var(--uix-bg-subtle) 75%); background-size: 200% 100%; animation: uix-shimmer var(--uix-loop-shimmer) linear infinite; border-radius: var(--uix-radius-sm); }
17
+ @media (prefers-reduced-motion: reduce) { .uix-skeleton { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
17
18
  .uix-skeleton--text { height: 12px; border-radius: var(--uix-radius-pill); }
18
19
  .uix-skeleton--circle { border-radius: 50%; }
19
20
  .uix-operation { display: grid; gap: var(--uix-space-3); padding: var(--uix-space-4); border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface); }
@@ -47,9 +47,14 @@
47
47
  .uix-table[data-density="compact"] td, .uix-table--compact td { height: var(--uix-row-h-compact); }
48
48
  .uix-table[data-density="comfortable"] td { height: var(--uix-row-h-comfortable); }
49
49
 
50
- .uix-table__pin { border: 0; background: transparent; color: var(--uix-text-muted); cursor: pointer; padding: var(--uix-space-1); border-radius: var(--uix-radius-xs); opacity: 0; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
51
- /* :focus-visible reveal mirrors .uix-rowact so keyboard users can find the pin (UIX-A11Y-2) */
52
- .uix-table tbody tr:hover .uix-table__pin, .uix-table__pin:focus-visible, .uix-table__pin[data-on] { opacity: 1; }
50
+ .uix-table__pin { border: 0; background: transparent; color: var(--uix-text-muted); cursor: pointer; padding: var(--uix-space-1); border-radius: var(--uix-radius-xs); transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
51
+ /* Hidden until hover only where hover exists; a touch screen always shows the pin. The
52
+ :focus-visible reveal mirrors .uix-rowact so keyboard users can find it (UIX-A11Y-2). */
53
+ @media (hover: hover) and (pointer: fine) {
54
+ .uix-table__pin { opacity: 0; }
55
+ .uix-table tbody tr:hover .uix-table__pin { opacity: 1; }
56
+ }
57
+ .uix-table__pin:focus-visible, .uix-table__pin[data-on] { opacity: 1; }
53
58
  .uix-table__pin:hover { background: var(--uix-bg-active); color: var(--uix-text); }
54
59
  .uix-table__pin[data-on] { color: var(--uix-accent); }
55
60
  .uix-table__pin svg { width: 15px; height: 15px; }
@@ -112,10 +117,14 @@
112
117
  .uix-id-cell__btn:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 2px; border-radius: var(--uix-radius-xs); }
113
118
  .uix-id-cell__arrow {
114
119
  width: var(--uix-icon-sm); height: var(--uix-icon-sm); flex-shrink: 0; color: var(--uix-text-muted);
115
- opacity: 0; transition: opacity var(--uix-dur-fast) var(--uix-ease-out);
120
+ transition: opacity var(--uix-dur-fast) var(--uix-ease-out);
116
121
  }
117
122
  /* :focus-within as well: a keyboard user on the id link sees the arrow too (HAR-1814). */
118
- .uix-table tbody tr:hover .uix-id-cell__arrow, .uix-table tbody tr:focus-within .uix-id-cell__arrow { opacity: 1; }
123
+ @media (hover: hover) and (pointer: fine) {
124
+ .uix-id-cell__arrow { opacity: 0; }
125
+ .uix-table tbody tr:hover .uix-id-cell__arrow { opacity: 1; }
126
+ }
127
+ .uix-table tbody tr:focus-within .uix-id-cell__arrow { opacity: 1; }
119
128
 
120
129
  /* ── selection column + select-all ────────────────────────────────────────────── */
121
130
  .uix-table th.uix-table__selcol, .uix-table td.uix-table__selcell { width: 44px; padding-left: var(--uix-space-3); padding-right: 0; text-align: center; }
@@ -136,10 +145,15 @@
136
145
 
137
146
  /* ── row hover actions + kebab ─────────────────────────────────────────────────── */
138
147
  .uix-table__actions { width: 84px; text-align: right; white-space: nowrap; }
139
- .uix-rowact { border: 0; background: transparent; color: var(--uix-text-muted); cursor: pointer; padding: var(--uix-space-1); border-radius: var(--uix-radius-sm); opacity: 0; transition: opacity var(--uix-dur-fast) var(--uix-ease-out), background-color var(--uix-dur-fast) var(--uix-ease-out); }
148
+ .uix-rowact { border: 0; background: transparent; color: var(--uix-text-muted); cursor: pointer; padding: var(--uix-space-1); border-radius: var(--uix-radius-sm); transition: opacity var(--uix-dur-fast) var(--uix-ease-out), background-color var(--uix-dur-fast) var(--uix-ease-out); }
140
149
  .uix-rowact svg { width: 16px; height: 16px; display: block; }
141
- /* A row the keyboard is in shows its actions, as a hovered one does (HAR-1814). */
142
- .uix-table tbody tr:hover .uix-rowact, .uix-table tbody tr:focus-within .uix-rowact, .uix-rowact:focus-visible, .uix-rowact[aria-expanded="true"] { opacity: 1; }
150
+ /* Row actions hide until hover only where hover exists: on a touch screen they stay visible, so
151
+ hover is never the only way to reach them. A row the keyboard is in shows them too (HAR-1814). */
152
+ @media (hover: hover) and (pointer: fine) {
153
+ .uix-rowact { opacity: 0; }
154
+ .uix-table tbody tr:hover .uix-rowact { opacity: 1; }
155
+ }
156
+ .uix-table tbody tr:focus-within .uix-rowact, .uix-rowact:focus-visible, .uix-rowact[aria-expanded="true"] { opacity: 1; }
143
157
  .uix-rowact:hover { background: var(--uix-bg-active); color: var(--uix-text); }
144
158
 
145
159
  /* ── expandable inline row ─────────────────────────────────────────────────────── */
@@ -155,10 +169,13 @@
155
169
  @media (prefers-reduced-motion: reduce) { .uix-table__expand svg { transition: none; } }
156
170
 
157
171
  /* ── inline cell edit ──────────────────────────────────────────────────────────── */
158
- .uix-cell-edit { display: inline-flex; align-items: center; gap: var(--uix-space-2); cursor: text; border-radius: var(--uix-radius-sm); padding: var(--uix-space-1) var(--uix-space-2); margin: calc(-1 * var(--uix-space-1)) calc(-1 * var(--uix-space-2)); transition: background-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out); }
172
+ .uix-cell-edit { display: inline-flex; align-items: center; gap: var(--uix-space-2); cursor: text; border-radius: var(--uix-radius-sm); padding: var(--uix-space-1) var(--uix-space-2); margin: calc(-1 * var(--uix-space-1)) calc(-1 * var(--uix-space-2)); transition: background-color var(--uix-dur-fast) var(--uix-ease-out); }
159
173
  .uix-cell-edit:hover { background: var(--uix-bg-active); }
160
- .uix-cell-edit__pencil { width: 13px; height: 13px; color: var(--uix-text-muted); opacity: 0; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
161
- .uix-table tbody tr:hover .uix-cell-edit__pencil { opacity: 1; }
174
+ .uix-cell-edit__pencil { width: 13px; height: 13px; color: var(--uix-text-muted); transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
175
+ @media (hover: hover) and (pointer: fine) {
176
+ .uix-cell-edit__pencil { opacity: 0; }
177
+ .uix-table tbody tr:hover .uix-cell-edit__pencil { opacity: 1; }
178
+ }
162
179
  .uix-cell-edit[data-editing] { background: var(--uix-surface); box-shadow: 0 0 0 2px var(--uix-ring); cursor: default; }
163
180
  .uix-cell-edit__ok { color: var(--uix-success); width: 14px; height: 14px; opacity: 0; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
164
181
  .uix-cell-edit[data-saved] .uix-cell-edit__ok { opacity: 1; }
@@ -5,7 +5,7 @@
5
5
  border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md);
6
6
  background: var(--uix-surface); color: var(--uix-text); font: inherit; font-size: var(--uix-text-body);
7
7
  line-height: var(--uix-leading-body);
8
- transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out);
8
+ transition: border-color var(--uix-dur-fast) var(--uix-ease-out);
9
9
  }
10
10
  .uix-textarea::placeholder { color: var(--uix-text-muted); }
11
11
  .uix-textarea:hover { border-color: var(--uix-border-strong); }
@@ -6,7 +6,12 @@
6
6
  background: var(--uix-surface-2); border: 1px solid var(--uix-border); box-shadow: var(--uix-shadow-popover);
7
7
  animation: uix-slide-in-right var(--uix-dur-slow) var(--uix-ease-out);
8
8
  }
9
- .uix-toast[data-leaving] { animation: uix-toast-out var(--uix-dur) var(--uix-ease-in) forwards; }
9
+ .uix-toast[data-leaving] { animation: uix-toast-out var(--uix-dur-exit) var(--uix-ease-out) forwards; }
10
+ @media (prefers-reduced-motion: reduce) {
11
+ .uix-toast { animation: uix-fade-in var(--uix-dur) var(--uix-ease-out) !important; }
12
+ .uix-toast[data-leaving] { animation: uix-toast-fade-out var(--uix-dur-exit) var(--uix-ease-out) forwards !important; }
13
+ }
14
+ @keyframes uix-toast-fade-out { to { opacity: 0; } }
10
15
  @keyframes uix-toast-out { to { opacity: 0; transform: translateX(16px); } }
11
16
  .uix-toast__icon { flex: none; margin-top: 1px; }
12
17
  .uix-toast__icon svg { width: var(--uix-icon-md); height: var(--uix-icon-md); }
@@ -40,7 +40,8 @@
40
40
  opacity: 0; pointer-events: none; z-index: var(--uix-z-tooltip);
41
41
  transition: opacity var(--uix-dur-fast) var(--uix-ease-out), transform var(--uix-dur-fast) var(--uix-ease-out);
42
42
  }
43
- [data-uix-tip]:hover::after, [data-uix-tip]:focus-visible::after { opacity: 1; transform: translateX(-50%) translateY(0); }
43
+ [data-uix-tip]:focus-visible::after { opacity: 1; transform: translateX(-50%) translateY(0); }
44
+ @media (hover: hover) and (pointer: fine) { [data-uix-tip]:hover::after { opacity: 1; transform: translateX(-50%) translateY(0); } }
44
45
  /* when JS upgrades [data-uix-tip] to a top-layer bubble, drop the clipped CSS-only one */
45
46
  .uix-has-js-tip [data-uix-tip]::after { content: none; }
46
47
  }
@@ -46,7 +46,8 @@
46
46
  /* Grip and ⋯ are quiet until the row is hovered or holds focus, the grip is in use, or the menu is
47
47
  open — at 75%, the same WCAG 1.4.11 floor as the saved-views row (see below). */
48
48
  :is(.uix-view-menu__grip, .uix-view-menu__actions) { opacity: .75; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
49
- .uix-view-menu__col:is(:hover, :focus-within, [data-dragging]) :is(.uix-view-menu__grip, .uix-view-menu__actions),
49
+ @media (hover: hover) and (pointer: fine) { .uix-view-menu__col:hover :is(.uix-view-menu__grip, .uix-view-menu__actions) { opacity: 1; } }
50
+ .uix-view-menu__col:is(:focus-within, [data-dragging]) :is(.uix-view-menu__grip, .uix-view-menu__actions),
50
51
  .uix-view-menu__actions:has([aria-expanded="true"]) { opacity: 1; }
51
52
  /* Saved views: optional titled sections in one scrolling menu; a row is grip · name · overflow.
52
53
  Hover and selection paint the WHOLE row; the selected row is an accent tint, never a check glyph. */
@@ -69,7 +70,8 @@
69
70
  the grip is in use, or the overflow menu is open. Idle is 75% (not lower): the muted grip on the
70
71
  panel surface needs it to keep WCAG 1.4.11 3:1 non-text contrast at rest (3.19:1 light, 4.45:1 dark). */
71
72
  :is(.uix-saved-views__grip, .uix-saved-views__actions) { opacity: .75; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
72
- .uix-saved-views__row:is(:hover, :focus-within, [data-dragging]) :is(.uix-saved-views__grip, .uix-saved-views__actions),
73
+ @media (hover: hover) and (pointer: fine) { .uix-saved-views__row:hover :is(.uix-saved-views__grip, .uix-saved-views__actions) { opacity: 1; } }
74
+ .uix-saved-views__row:is(:focus-within, [data-dragging]) :is(.uix-saved-views__grip, .uix-saved-views__actions),
73
75
  .uix-saved-views__actions:has([aria-expanded="true"]) { opacity: 1; }
74
76
  .uix-saved-views__pin { display: grid; place-items: center; width: 28px; height: 28px; border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text-muted); cursor: pointer; }
75
77
  .uix-saved-views__pin:is(:hover, :focus-visible), .uix-saved-views__pin[data-on] { color: var(--uix-warning-text); background: var(--uix-bg-hover); }