@tensor_1/tokens 2.46.0 → 2.47.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensor_1/tokens",
3
- "version": "2.46.0",
3
+ "version": "2.47.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": [
@@ -0,0 +1,47 @@
1
+ /* uix busy region — keep what is on screen while it is fetched again (HAR-2582, motion
2
+ standard §2.5). A refetch dims the content under an indeterminate bar along the top instead
3
+ of swapping it for skeletons, so nothing the person was looking at vanishes. The React
4
+ BusyBar sets [data-shown] after 300 ms and holds it 500 ms; the parent reads it with :has(),
5
+ so `BusyRegion` and `TableWrap refreshing` stay server components. Depends only on --uix-*
6
+ tokens. */
7
+ @layer uix.components {
8
+ .uix-busy-region { position: relative; }
9
+ .uix-busy-region__content { transition: opacity var(--uix-dur-exit) var(--uix-ease-out); }
10
+ .uix-busy-region:has(> .uix-busy-bar[data-shown]) > .uix-busy-region__content { opacity: .6; transition-duration: var(--uix-dur); }
11
+
12
+ .uix-busy-bar {
13
+ position: absolute; inset-block-start: 0; inset-inline: 0; z-index: var(--uix-z-sticky);
14
+ display: block; height: 2px; overflow: hidden; pointer-events: none;
15
+ opacity: 0; transition: opacity var(--uix-dur-exit) var(--uix-ease-out);
16
+ }
17
+ .uix-busy-bar[data-shown] { opacity: 1; transition-duration: var(--uix-dur); }
18
+ .uix-busy-bar::before {
19
+ content: ""; position: absolute; inset-block: 0; inset-inline-start: 0; width: 40%;
20
+ background: var(--uix-accent); transform: translateX(-100%);
21
+ }
22
+ /* constant motion is linear (§2.4) */
23
+ .uix-busy-bar[data-shown]::before { animation: uix-busy-sweep var(--uix-loop-progress) linear infinite; }
24
+ @keyframes uix-busy-sweep { from { transform: translateX(-100%); } to { transform: translateX(250%); } }
25
+ /* right to left: the stripe starts at the right (inline-start) edge and sweeps leftwards */
26
+ @keyframes uix-busy-sweep-rtl { from { transform: translateX(100%); } to { transform: translateX(-250%); } }
27
+ .uix-busy-bar[data-shown]:dir(rtl)::before { animation-name: uix-busy-sweep-rtl; }
28
+
29
+ /* Reduced motion: no sweep; the full bar pulses (the kit's uix-pulse), so "still working" survives (§2.7). */
30
+ @media (prefers-reduced-motion: reduce) {
31
+ .uix-busy-bar::before { width: 100%; transform: none; }
32
+ /* !important: the kit's reduced-motion policy ends keyframes at once; a still-working signal opts back in */
33
+ .uix-busy-bar[data-shown]::before, .uix-busy-bar[data-shown]:dir(rtl)::before { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; }
34
+ }
35
+ @media (forced-colors: active) {
36
+ .uix-busy-bar::before { background: Highlight; }
37
+ }
38
+
39
+ /* A refreshing table: the bar sticks to the top of the scrolling wrap (over the sticky
40
+ header) and takes no room: it is 0 tall and draws its 2 px stripe below its own edge. It
41
+ clips sideways only (overflow-x: clip), so the sweep never widens the wrap's scroll area.
42
+ The rows dim; the header stays. */
43
+ .uix-table-wrap > .uix-busy-bar { position: sticky; inset-inline-start: 0; height: 0; overflow-x: clip; overflow-y: visible; z-index: calc(var(--uix-z-sticky) + 1); }
44
+ .uix-table-wrap > .uix-busy-bar::before { inset-block: 0 auto; height: 2px; }
45
+ .uix-table-wrap > .uix-table > tbody { transition: opacity var(--uix-dur-exit) var(--uix-ease-out); }
46
+ .uix-table-wrap:has(> .uix-busy-bar[data-shown]) > .uix-table > tbody { opacity: .6; transition-duration: var(--uix-dur); }
47
+ }
@@ -52,20 +52,49 @@
52
52
  .uix-btn--wrap.uix-btn--lg:not(.uix-btn--icon) { min-height: 44px; }
53
53
  .uix-btn--wrap.uix-btn--link { min-height: 0; padding-block: 0; }
54
54
 
55
- .uix-btn[data-loading] { color: transparent !important; position: relative; pointer-events: none; }
55
+ /* Pending (React `loading`, HAR-2580): the button stops taking presses at once but looks the
56
+ same for the first 300 ms; a fast action shows nothing new. It keeps full colour while it
57
+ works (a spinner, never a greyed-out control), which overrides the aria-disabled dim. */
58
+ .uix-btn[data-pending], .uix-btn:has(> [data-uix-btn-busy]) { opacity: 1; pointer-events: none; }
59
+
60
+ /* The spinner: `data-loading` (plain markup, shows at once) or React's delayed busy marker. */
61
+ .uix-btn:has(> [data-uix-btn-busy]), .uix-btn[data-loading] { color: transparent !important; position: relative; pointer-events: none; }
56
62
  /* The label is hidden (color: transparent), so the ring cannot inherit
57
63
  currentColor from the button; each variant names its own foreground.
58
64
  TENSOR RX-125 (UIX-01): the ring was on-accent for every variant, so a
59
65
  loading secondary/outline/ghost button rendered blank. */
60
- .uix-btn[data-loading]::after {
66
+ .uix-btn:has(> [data-uix-btn-busy])::after, .uix-btn[data-loading]::after {
61
67
  content: ""; position: absolute; width: 1em; height: 1em;
62
68
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
63
69
  color: var(--uix-text); animation: uix-spin var(--uix-loop-spin) linear infinite;
64
70
  }
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; } }
66
- .uix-btn--primary[data-loading]::after { color: var(--uix-accent-fg); }
67
- .uix-btn--danger[data-loading]::after { color: var(--uix-danger-fg); }
68
- .uix-btn--link[data-loading]::after { color: var(--uix-link); }
71
+ @media (prefers-reduced-motion: reduce) { .uix-btn:has(> [data-uix-btn-busy])::after, .uix-btn[data-loading]::after { animation: uix-pulse var(--uix-loop-pulse) var(--uix-ease-in-out) infinite !important; } }
72
+ .uix-btn--primary:has(> [data-uix-btn-busy])::after, .uix-btn--primary[data-loading]::after { color: var(--uix-accent-fg); }
73
+ .uix-btn--danger:has(> [data-uix-btn-busy])::after, .uix-btn--danger[data-loading]::after { color: var(--uix-danger-fg); }
74
+ .uix-btn--link:has(> [data-uix-btn-busy])::after, .uix-btn--link[data-loading]::after { color: var(--uix-link); }
75
+ /* The result face hides with the label while the spinner shows. */
76
+ .uix-btn:has(> [data-uix-btn-busy]) .uix-btn__result { visibility: hidden; }
77
+
78
+ /* Result (React `result`, HAR-2581): the label and both result wordings share one grid cell,
79
+ so the button is always as wide as the widest and nothing beside it moves. A result
80
+ crossfades in over the label (opacity only), holds, and fades back. Text and icon carry
81
+ the meaning; colour only tints the icon. */
82
+ .uix-btn__face { display: inline-grid; justify-items: center; align-items: center; }
83
+ .uix-btn__face > * { grid-area: 1 / 1; display: inline-flex; align-items: center; gap: .5em; transition: opacity var(--uix-dur) var(--uix-ease-out); }
84
+ .uix-btn__result { opacity: 0; transition-duration: var(--uix-dur-exit); }
85
+ .uix-btn__face[data-result] > .uix-btn__label { opacity: 0; transition-duration: var(--uix-dur-exit-fast); }
86
+ .uix-btn__face[data-result="success"] > [data-kind="success"],
87
+ .uix-btn__face[data-result="error"] > [data-kind="error"] { opacity: 1; transition-duration: var(--uix-dur); }
88
+ .uix-btn--secondary .uix-btn__result[data-kind="success"] svg,
89
+ .uix-btn--outline .uix-btn__result[data-kind="success"] svg,
90
+ .uix-btn--ghost .uix-btn__result[data-kind="success"] svg { color: var(--uix-success-text); }
91
+ .uix-btn--secondary .uix-btn__result[data-kind="error"] svg,
92
+ .uix-btn--outline .uix-btn__result[data-kind="error"] svg,
93
+ .uix-btn--ghost .uix-btn__result[data-kind="error"] svg { color: var(--uix-danger-text); }
94
+ .uix-btn--wrap .uix-btn__face > * { white-space: normal; }
95
+ .uix-btn--xs .uix-btn__face > * { gap: .25em; }
96
+ /* An icon-only button keeps its square: the result is the mark alone (the words are spoken). */
97
+ .uix-btn--icon .uix-btn__result-text { display: none; }
69
98
 
70
99
  /* attached group */
71
100
  .uix-btn-group { display: inline-flex; }