loading-state-zoo 0.1.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/README.md ADDED
@@ -0,0 +1,588 @@
1
+ # loading-state-zoo
2
+
3
+ **17 loading patterns, one tiny package.** Dots, spinners, shapes, bars and skeletons that tell the user "please wait" — made silky smooth and ready to use in **React**, **plain websites**, and **iOS / Android apps**.
4
+
5
+ Think of it as a small zoo of loading animals. You pick an animal, put it in your app's cage, and it starts moving right away. You can even change its **color**, **size** and **speed** — no scissors or glue required.
6
+
7
+ - **No heavy setup.** Works everywhere a web view works. Zero required runtime dependencies.
8
+ - **Private by design.** Patterns live inside their own shadow DOM — their styles can never leak into your app, and your styles never break them.
9
+ - **Smooth.** Only `transform` and `opacity` ever animate, so it stays at 60fps even on phones.
10
+ - **Tunable.** Every color, size, speed AND every keyframe number is customizable (see [Colors & sizes](#colors-and-sizes) and [Tune the keyframes](#tune-the-keyframes)).
11
+
12
+ > Looking for AI-friendly docs? See **[docs/AI.md](./docs/AI.md)** and the machine-readable **[docs/components.json](./docs/components.json)**.
13
+
14
+ ---
15
+
16
+ ## The animals in the zoo
17
+
18
+ | Name | Kind | Good for |
19
+ | --- | --- | --- |
20
+ | [Bouncing dots](#bouncing-dots) | Dots | Loading a feed or a small section |
21
+ | [Typing dots](#typing-dots) | Dots | Chat apps, message "typing…" feel |
22
+ | [Wave dots](#wave-dots) | Dots | Waiting for a refresh or sync |
23
+ | [Helix dots](#helix-dots) | Dots | Data processing, fun & playful |
24
+ | [Dot grid wave](#dot-grid-wave) | Dots | Brick/grid placeholders, dashboards |
25
+ | [Arc spinner](#arc-spinner) | Spinner | Classic circular loading |
26
+ | [Material double arc](#material-double-arc) | Spinner | Android/Material look and feel |
27
+ | [Scan sweep](#scan-sweep) | Spinner | Radar/sonar, scanning, searching |
28
+ | [Orbit dots](#orbit-dots) | Spinner | Floaty, lightweight loading |
29
+ | [Orbit ring dots](#orbit-ring-dots) | Spinner | Cosmetic ring, data-heavy load |
30
+ | [Eyes](#eyes) | Shape | Fun state, "watching" waiting |
31
+ | [Morphing square](#morphing-square) | Shape | Modern brand moments |
32
+ | [Breathing pulse](#breathing-pulse) | Shape | Subtle, gentle "alive" loading |
33
+ | [Equalizer](#equalizer) | Bars & rings | Music, audio, waveform |
34
+ | [Progress ring](#progress-ring) | Bars & rings | Showing exact percent done |
35
+ | [Indeterminate progress bar](#indeterminate-progress-bar) | Bars & rings | "Doing something, no % yet" bar |
36
+ | [Skeleton shimmer](#skeleton-shimmer) | Skeleton | Placeholder while real content loads |
37
+
38
+ ---
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ npm install loading-state-zoo
44
+ ```
45
+
46
+ One package, three styles of use:
47
+
48
+ | Entry point | What you get |
49
+ | --- | --- |
50
+ | `loading-state-zoo` | Web components (works everywhere) |
51
+ | `loading-state-zoo/react` | The React components |
52
+ | `loading-state-zoo/theme.css` | Plain CSS + markup (no JavaScript) |
53
+
54
+ ---
55
+
56
+ ## Quick start: React
57
+
58
+ ```tsx
59
+ import { BouncingDots, ArcSpinner, SkeletonShimmer, ProgressRing } from "loading-state-zoo/react";
60
+
61
+ export function MyLoader() {
62
+ return (
63
+ <>
64
+ <BouncingDots />
65
+ <ArcSpinner size={64} color="#0a84ff" />
66
+ <SkeletonShimmer width={240} height={20} />
67
+ <ProgressRing percent={65} size={56} strokeWidth={6} />
68
+ </>
69
+ );
70
+ }
71
+ ```
72
+
73
+ That's it. Importing the package **automatically registers** all 17 elements for you (it's safe to import in many places — duplicates are skipped). It is also safe on the server (SSR/Next.js): on Node there is no browser, so nothing registers and nothing breaks.
74
+
75
+ If you prefer to register manually:
76
+
77
+ ```tsx
78
+ import { defineAll } from "loading-state-zoo"; // or from "loading-state-zoo/react"
79
+ defineAll();
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Quick start: plain website
85
+
86
+ Add the package from a CDN (or bundle it) and write the tags directly in your HTML.
87
+
88
+ ```html
89
+ <script type="module">
90
+ import "https://unpkg.com/loading-state-zoo";
91
+ </script>
92
+
93
+ <div style="display:flex; gap:24px; align-items:center">
94
+ <lz-wave-dots></lz-wave-dots>
95
+ <lz-orbit-dots size="56" color="#30d158"></lz-orbit-dots>
96
+ <lz-scan-sweep size="56" color="#0a84ff"></lz-scan-sweep>
97
+ <lz-morphing-square accent="#ff9f0a" speed="0.7"></lz-morphing-square>
98
+ </div>
99
+ ```
100
+
101
+ Mix and match any of the 17 tags — see the full list below.
102
+
103
+ ---
104
+
105
+ ## Quick start: CSS only (no JavaScript at all)
106
+
107
+ Sometimes you just want markup and CSS — great for email-ish constraints, or for shipping the *exact* look to an iOS/Android WebView without any JS.
108
+
109
+ ```html
110
+ <link rel="stylesheet" href="https://unpkg.com/loading-state-zoo/theme.css" />
111
+
112
+ <div style="--lz-color:#0a84ff; --lz-speed:0.8">
113
+ <span class="lz-dot-grid-wave" style="--lz-size:8px; --lz-grid-gap:14px">
114
+ <i class="lz-dot"></i><i class="lz-dot"></i><i class="lz-dot"></i>
115
+ <i class="lz-dot"></i><i class="lz-dot"></i><i class="lz-dot"></i>
116
+ </span>
117
+ </div>
118
+ ```
119
+
120
+ Every pattern also ships as a `.lz-<pattern>` CSS class with `.lz-<part>` children. The part markup is identical to what the web components render — open any pattern on [the demo site](https://loading-state-zoo.pages.dev), inspect it, and copy the inner HTML.
121
+
122
+ ---
123
+
124
+ ## Quick start: iOS & Android (WebView)
125
+
126
+ Any of the three styles above (web components **or** the CSS classes) run inside a `WKWebView` (iOS) or an Android `WebView`. The patterns are just HTML + CSS — no framework runtime, no bundler magic, nothing native to install.
127
+
128
+ 1. Bundle the ESM file into your app's assets:
129
+ - iOS: copy `dist/index.js` into your Xcode target and load it in `WKWebView`.
130
+ - Android: copy it into `assets/` and inject it with `WebView#evaluateJavascript`, or load it as a local `<script>`.
131
+ 2. Or skip JS entirely and embed the HTML + `theme.css` directly (see above).
132
+
133
+ ---
134
+
135
+ ## Colors and sizes
136
+
137
+ **Yes — every component's color and size are customizable**, three different ways. You can also change the accent color, stroke thickness, dot count and more.
138
+
139
+ ### 1. By attribute (easiest)
140
+
141
+ ```html
142
+ <lz-bouncing-dots size="12" color="#30d158"></lz-bouncing-dots>
143
+ <lz-arc-spinner size="64" stroke-width="6" color="#ff9f0a"></lz-arc-spinner>
144
+ <lz-progress-ring percent="70" size="56" stroke-width="6" color="#0a84ff" track-color="rgba(255,255,255,0.2)"></lz-progress-ring>
145
+ ```
146
+
147
+ ```tsx
148
+ import { BouncingDots, ArcSpinner, ProgressRing } from "loading-state-zoo/react";
149
+ <>
150
+ <BouncingDots size={12} color="#30d158" />
151
+ <ArcSpinner size={64} strokeWidth={6} color="#ff9f0a" />
152
+ <ProgressRing percent={70} size={56} strokeWidth={6} color="#0a84ff" trackColor="rgba(255,255,255,0.2)" />
153
+ </>
154
+ ```
155
+
156
+ ### 2. By CSS variable (theme whole groups)
157
+
158
+ Set a variable on a wrapper element and **every pattern inside** inherits it.
159
+
160
+ ```html
161
+ <div style="--lz-color:#0a84ff; --lz-accent:#30d158; --lz-speed:0.75">
162
+ <lz-wave-dots></lz-wave-dots>
163
+ <lz-orbit-dots></lz-orbit-dots>
164
+ <lz-morphing-square></lz-morphing-square>
165
+ </div>
166
+ ```
167
+
168
+ ### 3. Common attributes (available on every pattern)
169
+
170
+ | Attribute | React prop | What it does | Default |
171
+ | --- | --- | --- | --- |
172
+ | `size` | `size` | Main dimension in px (dot diameter, or spinner/box size) | pattern-specific |
173
+ | `color` | `color` | Main foreground color (dots, arcs, bars, eyes) | `#f5f5f7` |
174
+ | `accent` | `accent` | Accent/brand color (morph square, progress bar, ring fill) | `#0a84ff` |
175
+ | `speed` | `speed` | Animation speed — `0.5` half speed, `2` double speed | `1` |
176
+ | `width` | `width` | Width in px (bars & skeletons) | pattern-specific |
177
+ | `height` | `height` | Height in px (bars & skeletons) | pattern-specific |
178
+
179
+ ### Shared CSS variables (all patterns)
180
+
181
+ | Variable | What it does | Default |
182
+ | --- | --- | --- |
183
+ | `--lz-color` | Foreground color everywhere | `#f5f5f7` |
184
+ | `--lz-accent` | Accent color | `#0a84ff` |
185
+ | `--lz-speed` | Duration multiplier | `1` |
186
+ | `--lz-surface` | Track / skeleton background | `#1c1c1e` |
187
+ | `--lz-stroke` | Ring thickness (px) | `4px` |
188
+
189
+ ---
190
+
191
+ ## Tune the keyframes
192
+
193
+ Every number inside every animation is also a CSS variable. Change how far dots bounce, how much a pulse grows, how fast eyes blink — on one pattern or on a whole app.
194
+
195
+ ```html
196
+ <div style="--lz-bounce:-16px; --lz-typing-peak:1.5; --lz-helix:-18px">
197
+ <lz-bouncing-dots></lz-bouncing-dots>
198
+ <lz-typing-dots></lz-typing-dots>
199
+ <lz-helix-dots></lz-helix-dots>
200
+ </div>
201
+ ```
202
+
203
+ | Variable | Pattern it changes | What it is | Default |
204
+ | --- | --- | --- | --- |
205
+ | `--lz-bounce` | Bouncing dots | How high the dots jump | `-10px` |
206
+ | `--lz-typing-min` | Typing dots | Smallest dot size | `0.35` |
207
+ | `--lz-typing-peak` | Typing dots | Biggest dot size | `1.15` |
208
+ | `--lz-wave-min` | Wave dots | Smallest dot size | `0.35` |
209
+ | `--lz-wave-peak` | Wave dots | Biggest dot size | `1.1` |
210
+ | `--lz-helix` | Helix dots | How far dots bob up | `-12px` |
211
+ | `--lz-grid-min` | Dot grid wave | Smallest dot size | `0.3` |
212
+ | `--lz-grid-fade` | Dot grid wave | Fade of resting dots | `0.15` |
213
+ | `--lz-breath` | Breathing pulse | How much it grows | `1.14` |
214
+ | `--lz-morph` | Morphing square | Smallest scale | `0.72` |
215
+ | `--lz-morph-radius` | Morphing square | Corner roundness at mid-morph | `17%` |
216
+ | `--lz-gaze-from` | Eyes | Pupil start offset | `-3px` |
217
+ | `--lz-gaze-to` | Eyes | Pupil end offset | `3px` |
218
+ | `--lz-blink` | Eyes | How closed the blink gets | `0.08` |
219
+ | `--lz-sheen-from` | Skeleton shimmer | Sheen start position | `-120%` |
220
+ | `--lz-sheen-to` | Skeleton shimmer | Sheen end position | `220%` |
221
+ | `--lz-orbit-fade` | Orbit ring dots | Fade of resting dots | `0.12` |
222
+ | `--lz-eq-min` | Equalizer | Shortest bar height | `0.3` |
223
+ | `--lz-slide-from` | Indeterminate bar | Slider start position | `-100%` |
224
+ | `--lz-slide-to` | Indeterminate bar | Slider end position | `300%` |
225
+
226
+ ---
227
+
228
+ ## Full component reference
229
+
230
+ Each section lists:
231
+ - **What it is** — plain words
232
+ - **Good for** — when to pick it
233
+ - **HTML** — the tag + attributes
234
+ - **React** — the component + props
235
+ - **Extra attributes & variables**
236
+
237
+ ---
238
+
239
+ ### Bouncing dots
240
+
241
+ **What it is:** three round dots that hop up and down, one after another. **Good for:** waiting on a list, feed, or small section refresh.
242
+
243
+ ```html
244
+ <lz-bouncing-dots></lz-bouncing-dots>
245
+ <lz-bouncing-dots size="12" color="#0a84ff"></lz-bouncing-dots>
246
+ ```
247
+
248
+ ```tsx
249
+ <BouncingDots />
250
+ <BouncingDots size={12} color="#0a84ff" />
251
+ ```
252
+
253
+ Attributes: `size` (dot size, default `8`) · `color` · `speed`.
254
+ Variables: `--lz-color` · `--lz-speed` · `--lz-bounce`.
255
+
256
+ ---
257
+
258
+ ### Typing dots
259
+
260
+ **What it is:** three dots that blink in a soft typing rhythm. **Good for:** chat, messaging, "typing…" moments.
261
+
262
+ ```html
263
+ <lz-typing-dots></lz-typing-dots>
264
+ ```
265
+
266
+ ```tsx
267
+ <TypingDots />
268
+ ```
269
+
270
+ Attributes: `size` (default `8`) · `color` · `speed`.
271
+ Variables: `--lz-typing-min` · `--lz-typing-peak`.
272
+
273
+ ---
274
+
275
+ ### Wave dots
276
+
277
+ **What it is:** four dots that swell like a gentle wave. **Good for:** refresh, sync, pull-in-progress.
278
+
279
+ ```html
280
+ <lz-wave-dots></lz-wave-dots>
281
+ ```
282
+
283
+ ```tsx
284
+ <WaveDots />
285
+ ```
286
+
287
+ Attributes: `size` (default `8`) · `color` · `speed`.
288
+ Variables: `--lz-wave-min` · `--lz-wave-peak`.
289
+
290
+ ---
291
+
292
+ ### Helix dots
293
+
294
+ **What it is:** a long row of dots that bob like a travelling wave. **Good for:** data crunching, fun playful waits.
295
+
296
+ ```html
297
+ <lz-helix-dots count="14"></lz-helix-dots>
298
+ ```
299
+
300
+ ```tsx
301
+ <HelixDots count={14} />
302
+ ```
303
+
304
+ Attributes: `size` (default `6`) · `count` (default `10`) · `color` · `speed`.
305
+ Variables: `--lz-helix`.
306
+
307
+ ---
308
+
309
+ ### Dot grid wave
310
+
311
+ **What it is:** a grid of dots that ripple outward from a corner. **Good for:** brick layouts, dashboards, big empty panels.
312
+
313
+ ```html
314
+ <lz-dot-grid-wave rows="4" cols="6" size="8" gap="14"></lz-dot-grid-wave>
315
+ ```
316
+
317
+ ```tsx
318
+ <DotGridWave rows={4} cols={6} size={8} gap={14} />
319
+ ```
320
+
321
+ Attributes: `size` (default `6`) · `rows` (default `4`) · `cols` (default `5`) · `gap` (default `12`) · `color` · `speed`.
322
+ Variables: `--lz-grid-gap` · `--lz-grid-min` · `--lz-grid-fade`.
323
+
324
+ ---
325
+
326
+ ### Arc spinner
327
+
328
+ **What it is:** one spinning "pac-man" ring. The classic. **Good for:** any circular wait.
329
+
330
+ ```html
331
+ <lz-arc-spinner size="64" stroke-width="6" color="#0a84ff"></lz-arc-spinner>
332
+ ```
333
+
334
+ ```tsx
335
+ <ArcSpinner size={64} strokeWidth={6} color="#0a84ff" />
336
+ ```
337
+
338
+ Attributes: `size` (default `44`) · `stroke-width` (default `4`) · `color` · `speed`.
339
+ Variables: `--lz-stroke`.
340
+
341
+ ---
342
+
343
+ ### Material double arc
344
+
345
+ **What it is:** two spinning arcs, opposite directions — the Material Design look. **Good for:** Android-style apps, "processing".
346
+
347
+ ```html
348
+ <lz-material-double-arc size="56" stroke-width="5"></lz-material-double-arc>
349
+ ```
350
+
351
+ ```tsx
352
+ <MaterialDoubleArc size={56} strokeWidth={5} />
353
+ ```
354
+
355
+ Attributes: `size` (default `44`) · `stroke-width` (default `4`) · `color` · `color-weak` (second arc tint) · `speed`.
356
+ Variables: `--lz-stroke` · `--lz-color-weak`.
357
+
358
+ ---
359
+
360
+ ### Scan sweep
361
+
362
+ **What it is:** concentric rings with a sweeping beam — like radar. **Good for:** scanning, searching, syncing data.
363
+
364
+ ```html
365
+ <lz-scan-sweep size="64" color="#30d158"></lz-scan-sweep>
366
+ ```
367
+
368
+ ```tsx
369
+ <ScanSweep size={64} color="#30d158" />
370
+ ```
371
+
372
+ Attributes: `size` (default `44`) · `color` · `color-weak` · `color-sweep` · `speed`.
373
+ Variables: `--lz-track` · `--lz-track-weak` · `--lz-color-weak` · `--lz-color-sweep`.
374
+
375
+ ---
376
+
377
+ ### Orbit dots
378
+
379
+ **What it is:** three dots glide around an invisible circle. **Good for:** light, floaty waits.
380
+
381
+ ```html
382
+ <lz-orbit-dots size="56" color="#0a84ff"></lz-orbit-dots>
383
+ ```
384
+
385
+ ```tsx
386
+ <OrbitDots size={56} color="#0a84ff" />
387
+ ```
388
+
389
+ Attributes: `size` (default `44`) · `color` · `speed`.
390
+ Variables: `--lz-color-weak` (dot tint).
391
+
392
+ ---
393
+
394
+ ### Orbit ring dots
395
+
396
+ **What it is:** eight dots on a ring that fade in a chasing sequence. **Good for:** elegant circular waits, brand moments.
397
+
398
+ ```html
399
+ <lz-orbit-ring-dots size="56" dot-size="10"></lz-orbit-ring-dots>
400
+ ```
401
+
402
+ ```tsx
403
+ <OrbitRingDots size={56} dotSize={10} />
404
+ ```
405
+
406
+ Attributes: `size` (default `44`) · `dot-size` (default derived) · `color` · `speed`.
407
+ Variables: `--lz-dot-size` · `--lz-orbit-fade`.
408
+
409
+ ---
410
+
411
+ ### Eyes
412
+
413
+ **What it is:** two cartoon eyes that look side to side and blink. **Good for:** fun apps, "watching" moments, kids' stuff.
414
+
415
+ ```html
416
+ <lz-eyes size="56"></lz-eyes>
417
+ ```
418
+
419
+ ```tsx
420
+ <Eyes size={56} />
421
+ ```
422
+
423
+ Attributes: `size` (default `44`) · `color` (sclera) · `speed`.
424
+ Variables: `--lz-pupil` (pupil color) · `--lz-gaze-from` · `--lz-gaze-to` · `--lz-blink`.
425
+
426
+ ---
427
+
428
+ ### Morphing square
429
+
430
+ **What it is:** a glowing square that swells, rounds and spins. **Good for:** modern splash states, brand colors.
431
+
432
+ ```html
433
+ <lz-morphing-square accent="#bf5af2" size="56"></lz-morphing-square>
434
+ ```
435
+
436
+ ```tsx
437
+ <MorphingSquare accent="#bf5af2" size={56} />
438
+ ```
439
+
440
+ Attributes: `size` (default `44`) · `accent` (default `#0a84ff`) · `speed`.
441
+ Variables: `--lz-glow` (glow color/shadow) · `--lz-morph` · `--lz-morph-radius`.
442
+
443
+ ---
444
+
445
+ ### Breathing pulse
446
+
447
+ **What it is:** one soft circle that gently grows and shrinks. **Good for:** subtle "alive" waits, background states.
448
+
449
+ ```html
450
+ <lz-breathing-pulse size="48" color="#30d158"></lz-breathing-pulse>
451
+ ```
452
+
453
+ ```tsx
454
+ <BreathingPulse size={48} color="#30d158" />
455
+ ```
456
+
457
+ Attributes: `size` (default `36`) · `color` · `speed`.
458
+ Variables: `--lz-breath`.
459
+
460
+ ---
461
+
462
+ ### Equalizer
463
+
464
+ **What it is:** four bars dancing like a music equalizer. **Good for:** audio, music, waveform loading.
465
+
466
+ ```html
467
+ <lz-equalizer size="24" bar-width="4" gap="4" color="#ff375f"></lz-equalizer>
468
+ ```
469
+
470
+ ```tsx
471
+ <Equalizer size={24} barWidth={4} gap={4} color="#ff375f" />
472
+ ```
473
+
474
+ Attributes: `size` (bar height, default `22`) · `bar-width` (default `3`) · `gap` (default `3`) · `color` · `speed`.
475
+ Variables: `--lz-bar-width` · `--lz-gap` · `--lz-eq-min`.
476
+
477
+ ---
478
+
479
+ ### Progress ring
480
+
481
+ **What it is:** a ring that fills to a percentage. **Good for:** uploads, downloads, tasks with a known percent. *(Not animated by itself — you drive it with the `percent` attribute.)*
482
+
483
+ ```html
484
+ <lz-progress-ring percent="65" size="56" stroke-width="6" color="#0a84ff" track-color="rgba(255,255,255,0.15)"></lz-progress-ring>
485
+ ```
486
+
487
+ ```tsx
488
+ <ProgressRing percent={65} size={56} strokeWidth={6} color="#0a84ff" trackColor="rgba(255,255,255,0.15)" />
489
+ ```
490
+
491
+ Attributes: `size` (default `44`) · `stroke-width` (default `4`) · `percent` (default `0`, clamped to 0–100) · `color` (fill) · `track-color` · `speed`.
492
+ Variables: `--lz-stroke` · `--lz-track`.
493
+
494
+ Accessibility: it exposes `role="progressbar"` plus `aria-valuenow/min/max`, and updates them when `percent` changes.
495
+
496
+ ---
497
+
498
+ ### Indeterminate progress bar
499
+
500
+ **What it is:** a bar with a slider that slides back and forth. Classic "we're working on it". **Good for:** long unknown tasks.
501
+
502
+ ```html
503
+ <lz-indeterminate-progress-bar width="220" height="8" accent="#0a84ff"></lz-indeterminate-progress-bar>
504
+ ```
505
+
506
+ ```tsx
507
+ <IndeterminateProgressBar width={220} height={8} accent="#0a84ff" />
508
+ ```
509
+
510
+ Attributes: `width` (default `160`) · `height` (default `6`) · `accent` (default `#0a84ff`) · `track-color` · `speed`.
511
+ Variables: `--lz-accent` · `--lz-surface` · `--lz-slide-from` · `--lz-slide-to`.
512
+
513
+ ---
514
+
515
+ ### Skeleton shimmer
516
+
517
+ **What it is:** a rounded block with a light that sweeps across. **Good for:** placeholders while real content loads (images, profiles, cards).
518
+
519
+ ```html
520
+ <lz-skeleton-shimmer width="240" height="24"></lz-skeleton-shimmer>
521
+ ```
522
+
523
+ ```tsx
524
+ <SkeletonShimmer width={240} height={24} />
525
+ ```
526
+
527
+ Attributes: `width` (default `120`) · `height` (default `28`) · `track-color` · `speed`.
528
+ Variables: `--lz-surface` · `--lz-sheen` (sweep light color) · `--lz-radius` · `--lz-sheen-from` · `--lz-sheen-to`.
529
+
530
+ ---
531
+
532
+ ## Accessibility
533
+
534
+ - Every pattern is announced to screen readers: dots/spinners/bars use `role="status"` with `aria-label="Loading"`.
535
+ - The progress ring uses `role="progressbar"` with live `aria-valuenow`, `aria-valuemin` and `aria-valuemax`.
536
+ - **Reduced motion:** the package does *not* force-disable animations, so you keep full control:
537
+
538
+ ```css
539
+ @media (prefers-reduced-motion: reduce) {
540
+ lz-bouncing-dots, lz-wave-dots,
541
+ lz-arc-spinner, lz-orbit-dots /* …and so on… */ {
542
+ animation-duration: 4s; /* or hide: animation: none */
543
+ }
544
+ }
545
+ ```
546
+
547
+ ---
548
+
549
+ ## Performance & best practices
550
+
551
+ - Only `transform` and `opacity` animate — the compositor handles them, so the main thread stays free.
552
+ - Fewer moving parts = cheaper. Prefer dots (Bouncing, Wave) for areas that appear often; keep spinners (Arc, Material) for a few spots.
553
+ - Swap `percent` on the progress ring directly from your state — no animation frame loops needed.
554
+ - The web components have **zero global CSS** — they can be dropped into the messiest existing page without side effects.
555
+
556
+ ---
557
+
558
+ ## FAQ
559
+
560
+ **Q: Do I need Tailwind or a bundler?** No. The package is standalone.
561
+
562
+ **Q: Does it collect any data?** No. Nothing tracks, stores, or sends user data. It's just drawing.
563
+
564
+ **Q: Do I need a privacy policy for this package?** No. It's a code library, not a service, and it collects nothing.
565
+
566
+ **Q: Can I use it in my commercial app?** Yes — MIT license.
567
+
568
+ **Q: Can I change sizes and colors?** Yes — attributes, React props, or CSS variables. See [Colors and sizes](#colors-and-sizes).
569
+
570
+ **Q: iOS/Android native, not WebView?** Not natively — but every modern nav/load UI is fine in a WebView; use the [CSS-only approach](#quick-start--css-only-no-javascript-at-all) for the smallest footprint.
571
+
572
+ ---
573
+
574
+ ## For AI assistants (LLMs, coding agents)
575
+
576
+ A condensed, machine-honest summary lives in **[docs/AI.md](./docs/AI.md)** and the full structured spec for every component in **[docs/components.json](./docs/components.json)**.
577
+
578
+ Key facts an AI should always know:
579
+
580
+ 1. **Auto-registration:** importing any entry point registers all custom elements exactly once. Do not call `defineAll()` again.
581
+ 2. **Entry points:** `loading-state-zoo` (web components), `loading-state-zoo/react` (React), `loading-state-zoo/theme.css` (plain CSS).
582
+ 3. **React prop ≈ attribute**, but multi-word attributes are camelCased in React (`stroke-width` → `strokeWidth`, `track-color` → `trackColor`).
583
+ 4. **Theming = CSS variables.** `--lz-color`, `--lz-accent`, `--lz-speed` on a parent inherit into all patterns inside it.
584
+ 5. **Keyframe numbers are variables too.** There is no need to fork CSS to change motion — set `--lz-bounce`, `--lz-typing-peak`, etc.
585
+
586
+ ## License
587
+
588
+ MIT — free to use in commercial projects, no attribution required (but appreciated).