@wallarm-org/design-system 2.2.0-rc-fix-overflow-list-api.2 → 2.3.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.
@@ -13,8 +13,9 @@ export interface AppShellProps extends HTMLAttributes<HTMLDivElement>, TestableP
13
13
  onRevealed?: () => void;
14
14
  appeared?: boolean;
15
15
  /**
16
- * The Frame's ambient layer — soft blooms, grain and a dot cluster behind the header and
17
- * rail. On by default; `false` leaves the flat Frame token fill.
16
+ * The Frame's ambient layer — drifting blooms, grain and a dot cluster behind the header and
17
+ * rail. On by default; `false` leaves the flat Frame token fill. Still under
18
+ * `prefers-reduced-motion: reduce`.
18
19
  */
19
20
  ambient?: boolean;
20
21
  }
@@ -2,7 +2,7 @@ import type { FC } from 'react';
2
2
  /**
3
3
  * The Frame's ambient layer. The Frame is the shell's gray zone — the header
4
4
  * bar plus the rail, everything around the canvas — and Ambient is the life in
5
- * it: soft blooms, grain and a dot cluster pooled bottom-left of the
5
+ * it: slow-drifting blooms, grain and a dot cluster pooled bottom-left of the
6
6
  * rail. Purely decorative, painted under every region (see
7
7
  * `theme/components/app-shell.css`). Internal to `AppShell`; toggled with its
8
8
  * `ambient` prop.
@@ -12,7 +12,6 @@ const AppShellAmbient = ()=>{
12
12
  let t5;
13
13
  let t6;
14
14
  let t7;
15
- let t8;
16
15
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
17
16
  t0 = /*#__PURE__*/ jsx("div", {
18
17
  className: "app-shell-ambient-bloom app-shell-ambient-bloom-1"
@@ -38,9 +37,6 @@ const AppShellAmbient = ()=>{
38
37
  t7 = /*#__PURE__*/ jsx("div", {
39
38
  className: "app-shell-ambient-grain"
40
39
  });
41
- t8 = /*#__PURE__*/ jsx("div", {
42
- className: "app-shell-ambient-dots"
43
- });
44
40
  $[0] = t0;
45
41
  $[1] = t1;
46
42
  $[2] = t2;
@@ -49,7 +45,6 @@ const AppShellAmbient = ()=>{
49
45
  $[5] = t5;
50
46
  $[6] = t6;
51
47
  $[7] = t7;
52
- $[8] = t8;
53
48
  } else {
54
49
  t0 = $[0];
55
50
  t1 = $[1];
@@ -59,8 +54,23 @@ const AppShellAmbient = ()=>{
59
54
  t5 = $[5];
60
55
  t6 = $[6];
61
56
  t7 = $[7];
62
- t8 = $[8];
63
57
  }
58
+ let t8;
59
+ if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
60
+ t8 = /*#__PURE__*/ jsx("div", {
61
+ className: "app-shell-ambient-dots",
62
+ children: /*#__PURE__*/ jsx("div", {
63
+ className: "app-shell-ambient-dots-noise-a",
64
+ children: /*#__PURE__*/ jsx("div", {
65
+ className: "app-shell-ambient-dots-noise-b",
66
+ children: /*#__PURE__*/ jsx("div", {
67
+ className: "app-shell-ambient-dots-grid"
68
+ })
69
+ })
70
+ })
71
+ });
72
+ $[8] = t8;
73
+ } else t8 = $[8];
64
74
  let t9;
65
75
  if ($[9] !== testId) {
66
76
  t9 = /*#__PURE__*/ jsxs("div", {
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "2.1.3",
3
- "generatedAt": "2026-10-08T09:30:41.343Z",
2
+ "version": "2.2.0",
3
+ "generatedAt": "2026-10-08T10:17:05.571Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Accordion",
@@ -4236,7 +4236,7 @@
4236
4236
  "name": "ambient",
4237
4237
  "type": "boolean | undefined",
4238
4238
  "required": false,
4239
- "description": "The Frame's ambient layer — soft blooms, grain and a dot cluster behind the header and\nrail. On by default; `false` leaves the flat Frame token fill.",
4239
+ "description": "The Frame's ambient layer — drifting blooms, grain and a dot cluster behind the header and\nrail. On by default; `false` leaves the flat Frame token fill. Still under\n`prefers-reduced-motion: reduce`.",
4240
4240
  "defaultValue": "true"
4241
4241
  },
4242
4242
  {
@@ -8,19 +8,28 @@
8
8
  * Three stacked layers, all decorative, all under the regions:
9
9
  * 1. blooms — seven large soft gray spots: four in the rail (two lifts, two
10
10
  * shades) and three flat ellipses crossing
11
- * the top edge so the header carries the same light. This is a
12
- * still frame of the "mesh gradient" of the 21st.dev builder, in
13
- * the shell's own tones.
11
+ * the top edge so the header carries the same light. They wander
12
+ * on 60–90 s loops, stretching as they move. This is the "mesh
13
+ * gradient" of the 21st.dev builder, in the shell's own tones.
14
14
  * 2. grain — desaturated fractal noise tiled as a background image, so it
15
15
  * rasterises once.
16
16
  * 3. dots — a 12px halftone raster (the auth-page scanner's grid) pooled
17
17
  * in the bottom-left of the rail, fading upward to nothing;
18
- * two noise masks break it into patches, so the grid is never
19
- * fully there.
18
+ * patches of it surface and dissolve under two drifting noise
19
+ * masks, so the grid is never fully there.
20
20
  *
21
- * Nothing here animates (ENG-911): endless 60–150 s drift loops kept a CPU
22
- * core busy on every idle tab just to repaint a background nobody watches.
23
- * Softness is baked into the gradient stops rather than `filter: blur()`.
21
+ * Performance (ENG-911). Everything that moves animates `transform` /
22
+ * `opacity` only, so the compositor shifts already-rasterised layers and
23
+ * nothing is repainted. The dots used to animate `mask-position`, which Chrome
24
+ * repaints on the main thread every frame (about a third of the old idle CPU);
25
+ * the masks now ride on their own layers instead. Softness is baked into the
26
+ * gradient stops rather than `filter: blur()`: a blur on a moving layer is
27
+ * re-run by the GPU every frame, a gradient is rasterised once.
28
+ *
29
+ * Motion is opt-in: it runs only under `prefers-reduced-motion:
30
+ * no-preference`, set on each `bloom-N` itself (a shared rule would lose to
31
+ * the equally specific, later `bloom-N`). Otherwise the ambient is the
32
+ * still first frame.
24
33
  *
25
34
  * The canvas (`AppShellRemote`) sits on top, so none of this shows under page
26
35
  * content. Colours are theme tokens in semantic.css (light and dark), derived
@@ -61,7 +70,7 @@
61
70
  }
62
71
 
63
72
  /* One soft spot. Each `app-shell-ambient-bloom-N` only sets its colour (`--c`), shape,
64
- size and position; the falloff below approximates a Gaussian so no
73
+ size, position and drift path; the falloff below approximates a Gaussian so no
65
74
  `filter: blur()` is needed. */
66
75
  @utility app-shell-ambient-bloom {
67
76
  --c: transparent;
@@ -84,6 +93,10 @@
84
93
  height: 760px;
85
94
  left: -280px;
86
95
  bottom: -240px;
96
+
97
+ @media (prefers-reduced-motion: no-preference) {
98
+ animation: app-shell-ambient-drift-1 60s ease-in-out infinite;
99
+ }
87
100
  }
88
101
 
89
102
  @utility app-shell-ambient-bloom-2 {
@@ -92,6 +105,10 @@
92
105
  height: 680px;
93
106
  left: -140px;
94
107
  top: 8%;
108
+
109
+ @media (prefers-reduced-motion: no-preference) {
110
+ animation: app-shell-ambient-drift-2 74s ease-in-out infinite;
111
+ }
95
112
  }
96
113
 
97
114
  @utility app-shell-ambient-bloom-3 {
@@ -100,6 +117,10 @@
100
117
  height: 540px;
101
118
  left: -170px;
102
119
  top: 46%;
120
+
121
+ @media (prefers-reduced-motion: no-preference) {
122
+ animation: app-shell-ambient-drift-3 66s ease-in-out infinite;
123
+ }
103
124
  }
104
125
 
105
126
  @utility app-shell-ambient-bloom-4 {
@@ -108,6 +129,10 @@
108
129
  height: 620px;
109
130
  left: 30px;
110
131
  bottom: -190px;
132
+
133
+ @media (prefers-reduced-motion: no-preference) {
134
+ animation: app-shell-ambient-drift-4 82s ease-in-out infinite;
135
+ }
111
136
  }
112
137
 
113
138
  /* Header strip: three wide, flat ellipses crossing the top edge so the bar
@@ -121,6 +146,10 @@
121
146
  height: 540px;
122
147
  left: 22%;
123
148
  top: -360px;
149
+
150
+ @media (prefers-reduced-motion: no-preference) {
151
+ animation: app-shell-ambient-drift-5 90s ease-in-out infinite;
152
+ }
124
153
  }
125
154
 
126
155
  @utility app-shell-ambient-bloom-6 {
@@ -130,6 +159,10 @@
130
159
  height: 580px;
131
160
  right: -300px;
132
161
  top: -390px;
162
+
163
+ @media (prefers-reduced-motion: no-preference) {
164
+ animation: app-shell-ambient-drift-6 78s ease-in-out infinite;
165
+ }
133
166
  }
134
167
 
135
168
  @utility app-shell-ambient-bloom-7 {
@@ -139,6 +172,141 @@
139
172
  height: 500px;
140
173
  left: 20px;
141
174
  top: -350px;
175
+
176
+ @media (prefers-reduced-motion: no-preference) {
177
+ animation: app-shell-ambient-drift-7 70s ease-in-out infinite;
178
+ }
179
+ }
180
+
181
+ /* Drift paths. Each spot wanders through four waypoints instead of a straight
182
+ back-and-forth, stretching along the direction it moves (a liquid smearing
183
+ on a surface) and breathing a little in opacity. Transform + opacity only,
184
+ so the rasterised gradient is never repainted, just composited. */
185
+ @keyframes app-shell-ambient-drift-1 {
186
+ 0% {
187
+ transform: translate(0, 0) rotate(0deg) scale(1, 1);
188
+ opacity: 1;
189
+ }
190
+ 33% {
191
+ transform: translate(70px, -90px) rotate(6deg) scale(1.16, 0.92);
192
+ opacity: 0.9;
193
+ }
194
+ 66% {
195
+ transform: translate(30px, -160px) rotate(-4deg) scale(0.94, 1.14);
196
+ opacity: 1;
197
+ }
198
+ 100% {
199
+ transform: translate(0, 0) rotate(0deg) scale(1, 1);
200
+ opacity: 1;
201
+ }
202
+ }
203
+ @keyframes app-shell-ambient-drift-2 {
204
+ 0% {
205
+ transform: translate(0, 0) rotate(0deg) scale(1, 1);
206
+ opacity: 0.95;
207
+ }
208
+ 33% {
209
+ transform: translate(-50px, 110px) rotate(-8deg) scale(0.9, 1.18);
210
+ opacity: 1;
211
+ }
212
+ 66% {
213
+ transform: translate(40px, 170px) rotate(5deg) scale(1.2, 0.9);
214
+ opacity: 0.88;
215
+ }
216
+ 100% {
217
+ transform: translate(0, 0) rotate(0deg) scale(1, 1);
218
+ opacity: 0.95;
219
+ }
220
+ }
221
+ @keyframes app-shell-ambient-drift-3 {
222
+ 0% {
223
+ transform: translate(0, 0) rotate(0deg) scale(1, 1);
224
+ opacity: 1;
225
+ }
226
+ 33% {
227
+ transform: translate(60px, -120px) rotate(10deg) scale(1.22, 0.88);
228
+ opacity: 0.85;
229
+ }
230
+ 66% {
231
+ transform: translate(-30px, -200px) rotate(-6deg) scale(0.92, 1.16);
232
+ opacity: 1;
233
+ }
234
+ 100% {
235
+ transform: translate(0, 0) rotate(0deg) scale(1, 1);
236
+ opacity: 1;
237
+ }
238
+ }
239
+ @keyframes app-shell-ambient-drift-4 {
240
+ 0% {
241
+ transform: translate(0, 0) rotate(0deg) scale(1, 1);
242
+ opacity: 0.9;
243
+ }
244
+ 33% {
245
+ transform: translate(-80px, -60px) rotate(-7deg) scale(1.18, 0.9);
246
+ opacity: 1;
247
+ }
248
+ 66% {
249
+ transform: translate(-40px, -140px) rotate(4deg) scale(0.9, 1.2);
250
+ opacity: 0.86;
251
+ }
252
+ 100% {
253
+ transform: translate(0, 0) rotate(0deg) scale(1, 1);
254
+ opacity: 0.9;
255
+ }
256
+ }
257
+ @keyframes app-shell-ambient-drift-5 {
258
+ 0% {
259
+ transform: translate(0, 0) scale(1, 1);
260
+ opacity: 1;
261
+ }
262
+ 33% {
263
+ transform: translate(160px, 40px) scale(1.14, 0.94);
264
+ opacity: 0.85;
265
+ }
266
+ 66% {
267
+ transform: translate(260px, 10px) scale(0.96, 1.1);
268
+ opacity: 1;
269
+ }
270
+ 100% {
271
+ transform: translate(0, 0) scale(1, 1);
272
+ opacity: 1;
273
+ }
274
+ }
275
+ @keyframes app-shell-ambient-drift-6 {
276
+ 0% {
277
+ transform: translate(0, 0) scale(1, 1);
278
+ opacity: 0.95;
279
+ }
280
+ 33% {
281
+ transform: translate(-200px, 30px) scale(1.12, 0.92);
282
+ opacity: 1;
283
+ }
284
+ 66% {
285
+ transform: translate(-320px, 60px) scale(0.94, 1.12);
286
+ opacity: 0.85;
287
+ }
288
+ 100% {
289
+ transform: translate(0, 0) scale(1, 1);
290
+ opacity: 0.95;
291
+ }
292
+ }
293
+ @keyframes app-shell-ambient-drift-7 {
294
+ 0% {
295
+ transform: translate(0, 0) scale(1, 1);
296
+ opacity: 1;
297
+ }
298
+ 33% {
299
+ transform: translate(120px, 50px) scale(1.16, 0.9);
300
+ opacity: 0.88;
301
+ }
302
+ 66% {
303
+ transform: translate(200px, 20px) scale(0.92, 1.12);
304
+ opacity: 1;
305
+ }
306
+ 100% {
307
+ transform: translate(0, 0) scale(1, 1);
308
+ opacity: 1;
309
+ }
142
310
  }
143
311
 
144
312
  @utility app-shell-ambient-grain {
@@ -151,33 +319,82 @@
151
319
  }
152
320
 
153
321
  /* Dot cluster. Confined to the bottom-left of the Frame (the only place it is
154
- ever visible). Three masks intersect: the vertical fade, and two tiles of
155
- low-frequency noise. Dots show only where both noise tiles are open, so the
156
- grid reads as scattered patches. */
322
+ ever visible). Three masks intersect: the vertical fade here, and two tiles
323
+ of low-frequency noise on the nested `dots-noise-*` layers. Dots show only
324
+ where both noise tiles are open, so patches surface and dissolve in place
325
+ over a couple of minutes instead of sliding across.
326
+
327
+ The noise tiles move by `transform`, not `mask-position`, so the compositor
328
+ slides them and nothing repaints. Each layer carries its mask with it, and
329
+ the grid counter-moves by the sum of the two, so the dots themselves stay
330
+ put. The noise layers overhang the box by 1200px (a multiple of both the
331
+ 400px tile and the 12px grid) so their edges never show mid-loop. */
157
332
  @utility app-shell-ambient-dots {
158
333
  position: absolute;
159
334
  left: 0;
160
335
  bottom: 0;
161
336
  width: min(100%, 480px);
162
337
  height: min(560px, 64vh);
338
+ overflow: hidden;
339
+ mask-image: linear-gradient(
340
+ to top,
341
+ #000 0,
342
+ rgb(0 0 0 / 0.75) 22%,
343
+ rgb(0 0 0 / 0.4) 48%,
344
+ rgb(0 0 0 / 0.12) 74%,
345
+ transparent 94%
346
+ );
347
+ }
348
+
349
+ @utility app-shell-ambient-dots-noise-a {
350
+ position: absolute;
351
+ inset: -1200px;
352
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.011' numOctaves='2' seed='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 0 -6.2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
353
+ mask-size: 400px 400px;
354
+
355
+ @media (prefers-reduced-motion: no-preference) {
356
+ animation: app-shell-ambient-dots-noise-a 150s linear infinite;
357
+ }
358
+ }
359
+
360
+ @utility app-shell-ambient-dots-noise-b {
361
+ position: absolute;
362
+ inset: 0;
363
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.014' numOctaves='2' seed='11' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 0 -6.2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
364
+ mask-size: 400px 400px;
365
+
366
+ @media (prefers-reduced-motion: no-preference) {
367
+ animation: app-shell-ambient-dots-noise-b 150s linear infinite;
368
+ }
369
+ }
370
+
371
+ @utility app-shell-ambient-dots-grid {
372
+ position: absolute;
373
+ inset: 0;
163
374
  background-image: radial-gradient(circle at center, var(--color-component-app-shell-ambient-dot) 0.9px, transparent 1.1px);
164
375
  background-size: 12px 12px;
165
376
  background-position: 6px 6px;
166
- mask-image:
167
- linear-gradient(
168
- to top,
169
- #000 0,
170
- rgb(0 0 0 / 0.75) 22%,
171
- rgb(0 0 0 / 0.4) 48%,
172
- rgb(0 0 0 / 0.12) 74%,
173
- transparent 94%
174
- ),
175
- url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.011' numOctaves='2' seed='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 0 -6.2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"),
176
- url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.014' numOctaves='2' seed='11' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 0 -6.2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
177
- mask-size:
178
- 100% 100%,
179
- 400px 400px,
180
- 400px 400px;
181
- mask-repeat: no-repeat, repeat, repeat;
182
- mask-composite: intersect;
377
+
378
+ @media (prefers-reduced-motion: no-preference) {
379
+ animation: app-shell-ambient-dots-grid 150s linear infinite;
380
+ }
381
+ }
382
+
383
+ /* Whole-tile offsets (multiples of 400px) so the loop is seamless. Noise A
384
+ travels (400, -400), noise B (-800, 400) in total — so (-1200, 800) relative
385
+ to A, which it sits inside — and the grid undoes B's total. */
386
+ @keyframes app-shell-ambient-dots-noise-a {
387
+ to {
388
+ transform: translate(400px, -400px);
389
+ }
390
+ }
391
+ @keyframes app-shell-ambient-dots-noise-b {
392
+ to {
393
+ transform: translate(-1200px, 800px);
394
+ }
395
+ }
396
+ @keyframes app-shell-ambient-dots-grid {
397
+ to {
398
+ transform: translate(800px, -400px);
399
+ }
183
400
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "2.2.0-rc-fix-overflow-list-api.2",
3
+ "version": "2.3.0",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",