modern-loaders 1.0.0 → 1.2.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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 suman
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,115 +1,568 @@
1
- # @sumanrana/modern-loaders
1
+ <div align="center">
2
2
 
3
- 10 modern, colorful, animated loaders for React. Pure CSS animations behind a typed
4
- component API — **zero runtime dependencies**, ~2 kB JS + ~1.7 kB CSS gzipped.
3
+ # modern-loaders
4
+
5
+ **212 colorful, animated loaders for React.**
6
+
7
+ Pure CSS motion behind one typed component — no runtime dependencies, no SVG, no images.
8
+
9
+ [![npm](https://img.shields.io/npm/v/modern-loaders?color=6366f1&label=npm)](https://www.npmjs.com/package/modern-loaders)
10
+ [![size](https://img.shields.io/bundlephobia/minzip/modern-loaders?color=ec4899&label=gzipped)](https://bundlephobia.com/package/modern-loaders)
11
+ [![types](https://img.shields.io/npm/types/modern-loaders?color=22d3ee)](https://www.npmjs.com/package/modern-loaders)
12
+ [![license](https://img.shields.io/npm/l/modern-loaders?color=8b5cf6)](./LICENSE)
13
+
14
+ </div>
15
+
16
+ ---
5
17
 
6
18
  ```bash
7
- npm i @sumanrana/modern-loaders
19
+ npm i modern-loaders
8
20
  ```
9
21
 
10
22
  ```tsx
11
- import { Loader } from "@sumanrana/modern-loaders";
23
+ import { Loader } from "modern-loaders";
12
24
 
13
25
  <Loader />
14
26
  <Loader variant="wave" size={72} speed={0.8} colors={["#f97316", "#ef4444", "#a855f7"]} />
15
27
  ```
16
28
 
17
- The CSS ships inside the bundle and injects itself — there is no separate stylesheet import.
18
- (If your setup strips side-effect imports, `import "@sumanrana/modern-loaders/styles.css"`.)
29
+ That is the whole setup. **The stylesheet is bundled and injects itself** — no CSS import to
30
+ remember, nothing to configure in Vite, Next.js, CRA, or Tailwind.
19
31
 
20
- ## Props
32
+ <sub>If your bundler strips side-effect imports, add <code>import "modern-loaders/styles.css"</code>.</sub>
21
33
 
22
- | prop | type | default | notes |
23
- |-----------|--------------------|-------------|--------------------------------------------------------------|
24
- | `variant` | `LoaderVariant` | `"aurora"` | one of the 10 below |
25
- | `size` | `number \| string` | `48` | a number means pixels |
26
- | `speed` | `number \| string` | `1` | a number means seconds |
27
- | `colors` | `string[]` | Nebula | 1–3 colors; fewer are cycled, so one color gives a monochrome loader |
28
- | `label` | `string \| null` | `"Loading"` | screen-reader text; `null` marks the loader decorative (`aria-hidden`) |
34
+ ## Why this one
29
35
 
30
- Any other `div` prop (`className`, `style`, `id`, data-attributes…) passes straight through.
36
+ - **212 variants across 20 families** spinners, dots, bars, shapes, progress, grids, pulses, organic, skeletons, dimensional, neon, liquid, glitch, particles, futuristic, elastic, minimal, gradient, kinetic, and premium.
37
+ - **Three props to learn.** `size`, `speed`, `colors`. Everything else has a sensible default.
38
+ - **Zero dependencies.** ~4 kB of JS. All motion is CSS keyframes — no rAF loop, no JS on the animation path.
39
+ - **Fully typed.** `variant` autocompletes every id, and a typo is a compile error.
40
+ - **Accessible by default.** Each loader is a labelled `role="status"` and honours `prefers-reduced-motion`.
41
+ - **Themeable without a build step.** Every value is a CSS custom property you can override.
31
42
 
32
- ## The 10 variants
43
+ ## Usage
33
44
 
34
- | variant | look |
35
- |------------|-----------------------------------------------|
36
- | `aurora` | multi-color conic sweep with a soft glow |
37
- | `orbit` | three-color comet trail |
38
- | `rings` | sonar pulses in three hues |
39
- | `wave` | rainbow dots riding a sine |
40
- | `bars` | gradient equalizer |
41
- | `blob` | morphing gradient squircle |
42
- | `cube` | 3D flip, a gradient per face |
43
- | `spiral` | two arcs spinning opposite directions |
44
- | `bar` | indeterminate gradient progress track |
45
- | `grid` | 3×3 cells pulsing on a diagonal |
45
+ ### Props
46
46
 
47
- Each also has a named export, so `<Aurora size={64} />` works as well as
48
- `<Loader variant="aurora" size={64} />`. `LOADER_VARIANTS` is exported as a readonly
49
- tuple if you want to map over them.
47
+ | prop | type | default | description |
48
+ |---|---|---|---|
49
+ | `variant` | `LoaderVariant` | `"aurora"` | Which loader to render any of the 212 ids below. |
50
+ | `size` | `number \| string` | `48` | Overall size. A number means pixels; strings pass through (`"3rem"`). |
51
+ | `speed` | `number \| string` | `1` | Base duration. A number means seconds; lower is faster. |
52
+ | `colors` | `string[]` | indigo / pink / cyan | One to three colors. Fewer are cycled, so a single color gives a clean monochrome loader. |
53
+ | `label` | `string \| null` | `"Loading"` | Screen-reader text. Pass `null` to mark the loader decorative. |
50
54
 
51
- ## Styling
55
+ Every other `div` prop — `className`, `style`, `id`, `onClick`, `data-*` — passes straight through.
52
56
 
53
- Everything is driven by CSS custom properties, so you can theme without the `colors` prop:
57
+ ### Which loader should I use?
54
58
 
55
- ```css
56
- .my-loader { --c1: #6366f1; --c2: #ec4899; --c3: #22d3ee; --size: 64px; --speed: .9s; }
57
- ```
59
+ | situation | reach for | why |
60
+ |---|---|---|
61
+ | Page or section loading | **Skeletons** | Shows the layout arriving. Beats a spinner on perceived speed. |
62
+ | Button or inline action | **Dots**, or a spinner at `size={16}` | Fits the line box without shifting text. |
63
+ | Upload, install, multi-step work | **Progress** | Says "work is moving", not just "wait". |
64
+ | Live, polling, listening | **Pulses** | Reads as a heartbeat rather than a block. |
65
+ | Splash, empty state, 404 | **Shapes**, **Organic**, **Dimensional** | Personality is worth the pixels here. |
66
+ | Onboarding, upload, sync | **Premium** | Restrained, product-grade motion that won't date. |
67
+ | Dense UI — toolbars, table cells | **Minimal** | Small and low-contrast; won't pull the eye off the data. |
68
+ | Games, launch screens, dev tools | **Neon**, **Futuristic**, **Glitch** | Loud on purpose. Best on a dark surface. |
69
+ | Playful or brand-forward moments | **Liquid**, **Particles**, **Elastic**, **Kinetic** | Physical motion people actually watch. |
58
70
 
59
- `--track` (the muted rail behind `bar`) derives from `--c1` via `color-mix()`; override it
60
- for a different tint. Under `prefers-reduced-motion: reduce` every animation slows to one
61
- calm 4s linear cycle rather than freezing — a stopped loader reads as a broken app.
71
+ ### Recipes
62
72
 
63
- ## Development
73
+ **A button that keeps its width while loading**
64
74
 
65
- ```bash
66
- npm run dev # demo playground at localhost:5173
67
- npm run build # typecheck + emit dist/ (ESM + CJS + .d.ts + CSS)
68
- npm run typecheck
75
+ ```tsx
76
+ <button disabled={busy}>
77
+ {busy
78
+ ? <Loader variant="pulse-dots" size={16} colors={["currentColor"]} label={null} />
79
+ : "Save"}
80
+ </button>
69
81
  ```
70
82
 
71
- ## Publishing the package
83
+ `colors={["currentColor"]}` inherits the button's own text color. `label={null}` stops screen
84
+ readers announcing "Loading" twice when the button already conveys it.
72
85
 
73
- The scope in `name` must be one your npm account owns. Check first:
86
+ **A skeleton shaped like the thing you're waiting for**
74
87
 
75
- ```bash
76
- npm whoami # your username
77
- npm view @sumanrana/modern-loaders # 404 = the name is free
88
+ ```tsx
89
+ {isLoading ? <Loader variant="sk-card" size={64} /> : <ArticleCard {...article} />}
78
90
  ```
79
91
 
80
- If the scope isn't yours, change `name` to `@<your-npm-user>/react-modern-loaders`
81
- (or an unscoped name that's still free), then:
92
+ **A full-page overlay**
82
93
 
83
- ```bash
84
- npm login
85
- npm publish --access public # required for scoped packages
94
+ ```tsx
95
+ <div style={{ position: "fixed", inset: 0, display: "grid", placeItems: "center" }}>
96
+ <Loader variant="aurora" size={80} label="Loading your dashboard" />
97
+ </div>
86
98
  ```
87
99
 
88
- `prepublishOnly` runs the build, and `files` limits the tarball to `dist/` + this README.
100
+ **Build your own picker** the manifest is exported:
89
101
 
90
- ## Deploying the demo
102
+ ```tsx
103
+ import { VARIANTS, Loader } from "modern-loaders";
91
104
 
92
- ```bash
93
- npm run build:demo # dist-demo/
94
- npm run preview:demo # check it locally
105
+ VARIANTS.filter(v => v.group === "Skeletons")
106
+ .map(v => <Loader key={v.id} variant={v.id} />);
95
107
  ```
96
108
 
97
- Then push `dist-demo/` to any static host:
109
+ ## All 212 variants
98
110
 
99
- ```bash
100
- npx vercel deploy dist-demo --prod
101
- npx netlify-cli deploy --dir dist-demo --prod
102
- npx wrangler pages deploy dist-demo
111
+ Expand a family to see its variants. Every id maps to a `.ldr--<id>` CSS class.
112
+
113
+ <details>
114
+ <summary><b>Spinners</b> · 10 Rings and arcs — the classic shape, modernised.</summary>
115
+
116
+ | variant | what it looks like |
117
+ |---|---|
118
+ | `aurora` | multi-color conic sweep with a soft glow |
119
+ | `spiral` | two arcs spinning opposite directions |
120
+ | `ring-dash` | a dashed ring drawing itself |
121
+ | `ring-gap` | solid ring with a travelling gap |
122
+ | `comet` | gradient tail chasing a bright head |
123
+ | `arc-trio` | three nested arcs at different speeds |
124
+ | `halo` | still ring with a glow orbiting it |
125
+ | `sweep` | radar-style cone sweeping a disc |
126
+ | `dot-ring` | eight dots fading around a circle |
127
+ | `petal` | radiating rounded blades, iOS-style but colorful |
128
+
129
+ </details>
130
+
131
+ <details>
132
+ <summary><b>Dots</b> · 10 — Small, quiet, and good inline with text.</summary>
133
+
134
+ | variant | what it looks like |
135
+ |---|---|
136
+ | `wave` | rainbow dots riding a sine |
137
+ | `orbit` | three-color comet trail |
138
+ | `bounce` | three dots with squash on landing |
139
+ | `fade-dots` | three dots handing off opacity |
140
+ | `pulse-dots` | three dots breathing in sequence |
141
+ | `typing` | chat-bubble typing indicator |
142
+ | `chase` | six dots circling with a scale pulse |
143
+ | `snake` | dots sliding along a track |
144
+ | `swap` | two dots trading places |
145
+ | `juggle` | three dots arcing over each other |
146
+
147
+ </details>
148
+
149
+ <details>
150
+ <summary><b>Bars</b> · 10 — Equalizer-style motion with a strong rhythm.</summary>
151
+
152
+ | variant | what it looks like |
153
+ |---|---|
154
+ | `bars` | gradient equalizer |
155
+ | `ladder` | bars lighting up bottom to top |
156
+ | `stretch` | bars scaling from the centre with a color shift |
157
+ | `flip-bars` | bars flipping on the X axis |
158
+ | `slide-bars` | stacked bars sliding sideways |
159
+ | `drop-bars` | bars falling and rebounding |
160
+ | `meter` | signal-strength staircase |
161
+ | `wave-bars` | seven bars translating through a sine |
162
+ | `split-bars` | pairs opening away from the centre |
163
+ | `scan-bars` | a highlight sweeping across a static row |
164
+
165
+ </details>
166
+
167
+ <details>
168
+ <summary><b>Shapes</b> · 10 — Geometry that morphs, folds, and turns.</summary>
169
+
170
+ | variant | what it looks like |
171
+ |---|---|
172
+ | `blob` | morphing gradient squircle |
173
+ | `cube` | 3D flip, a gradient per face |
174
+ | `triangle` | spinning gradient triangle |
175
+ | `hexagon` | hexagon pulsing and turning |
176
+ | `diamond` | rotated square breathing |
177
+ | `star` | five-point star turning |
178
+ | `squircle` | square relaxing into a circle |
179
+ | `folding` | four quadrants folding away in turn |
180
+ | `pentagon` | pentagon rocking on its base |
181
+ | `shape-shift` | square ↔ circle with a hue sweep |
182
+
183
+ </details>
184
+
185
+ <details>
186
+ <summary><b>Progress</b> · 10 — Wide, indeterminate tracks for page and upload states.</summary>
187
+
188
+ | variant | what it looks like |
189
+ |---|---|
190
+ | `bar` | indeterminate runner |
191
+ | `bar-stripes` | barber-pole fill |
192
+ | `bar-dual` | two runners passing each other |
193
+ | `bar-segments` | discrete blocks filling in turn |
194
+ | `bar-pill` | a pill bouncing wall to wall |
195
+ | `bar-glow` | runner with a light bloom |
196
+ | `bar-wave` | gradient sliding under a fixed window |
197
+ | `bar-dots` | dots marching along a rail |
198
+ | `bar-snake` | thin runner that stretches and contracts |
199
+ | `bar-fill` | repeatedly filling left to right |
200
+
201
+ </details>
202
+
203
+ <details>
204
+ <summary><b>Grids</b> · 10 — Cell fields that pulse, flip, and cascade.</summary>
205
+
206
+ | variant | what it looks like |
207
+ |---|---|
208
+ | `grid` | 3x3 cells pulsing on a diagonal |
209
+ | `checker` | alternating cells trading places |
210
+ | `cascade` | a wave rolling from the top-left corner |
211
+ | `tiles` | cells flipping like a split-flap board |
212
+ | `pixel` | 4x4 field blinking in scattered order |
213
+ | `matrix` | rows sweeping downward |
214
+ | `mosaic` | cells cycling through the palette |
215
+ | `ripple-grid` | a pulse spreading from the centre cell |
216
+ | `domino` | cells tipping over one after another |
217
+ | `sparkle` | cells twinkling at different sizes |
218
+
219
+ </details>
220
+
221
+ <details>
222
+ <summary><b>Pulses</b> · 10 — Expanding rings for live, waiting, or listening states.</summary>
223
+
224
+ | variant | what it looks like |
225
+ |---|---|
226
+ | `rings` | sonar pulses in three hues |
227
+ | `radar` | sweeping cone over a ring |
228
+ | `sonar` | filled discs expanding outward |
229
+ | `echo` | rings that expand then snap back |
230
+ | `shockwave` | a ring that thins as it grows |
231
+ | `bloom` | soft gradient discs blooming open |
232
+ | `droplet` | a dot falling into its own ripple |
233
+ | `concentric` | three static rings breathing together |
234
+ | `aura` | a soft glow breathing behind a core |
235
+ | `beacon` | steady dot with an expanding halo |
236
+
237
+ </details>
238
+
239
+ <details>
240
+ <summary><b>Organic</b> · 10 — Playful, physical motion with personality.</summary>
241
+
242
+ | variant | what it looks like |
243
+ |---|---|
244
+ | `heart` | a heartbeat: a rotated square with two round lobes |
245
+ | `moon` | a crescent turning through its phases |
246
+ | `sun` | a core with rays reaching out |
247
+ | `flame` | a flickering teardrop |
248
+ | `bubble` | bubbles rising and popping |
249
+ | `jelly` | squash and stretch |
250
+ | `spring` | a coil compressing and releasing |
251
+ | `float` | an orb bobbing over its shadow |
252
+ | `leaf` | a teardrop tumbling as it turns |
253
+ | `windmill` | four blades turning together |
254
+
255
+ </details>
256
+
257
+ <details>
258
+ <summary><b>Skeletons</b> · 10 — Content-shaped placeholders — use these over spinners for page loads.</summary>
259
+
260
+ | variant | what it looks like |
261
+ |---|---|
262
+ | `shimmer` | a single sweeping block |
263
+ | `sk-text` | three lines of copy |
264
+ | `sk-card` | thumbnail with two lines beside it |
265
+ | `sk-avatar` | a circle and a name line |
266
+ | `sk-image` | a framed image placeholder |
267
+ | `sk-list` | stacked rows with leading bullets |
268
+ | `sk-table` | a header row above body rows |
269
+ | `sk-chip` | a row of tag placeholders |
270
+ | `sk-paragraph` | four lines of body copy |
271
+ | `sk-media` | a 16:9 block with a caption under it |
272
+
273
+ </details>
274
+
275
+ <details>
276
+ <summary><b>Dimensional</b> · 10 — Depth, 3D rotation, and orbital motion.</summary>
277
+
278
+ | variant | what it looks like |
279
+ |---|---|
280
+ | `helix` | two strands weaving past each other |
281
+ | `pendulum` | a Newton's cradle |
282
+ | `atom` | electron shells around a nucleus |
283
+ | `gyro` | nested rings turning on different axes |
284
+ | `coil` | stacked rings travelling down a spring |
285
+ | `vortex` | arcs drawn into a funnel |
286
+ | `prism` | three gradient triangles orbiting a centre |
287
+ | `torus` | a ring tumbling in 3D |
288
+ | `cylinder` | stacked ellipses rolling downward |
289
+ | `galaxy` | dots spiralling out from the core |
290
+
291
+ </details>
292
+
293
+ <details>
294
+ <summary><b>Neon</b> · 12 — Light that hums, blooms, and flickers alight.</summary>
295
+
296
+ | variant | what it looks like |
297
+ |---|---|
298
+ | `neon-ring` | a glass tube that hums, with a lit filament turning inside it |
299
+ | `neon-pulse` | a white-hot filament breathing inside its own bloom |
300
+ | `neon-tube` | a light bouncing wall to wall inside a glass capsule |
301
+ | `neon-sign` | bars igniting one by one, with the stutter of a real tube |
302
+ | `laser` | a scan line crossing a screen, scanlines and all |
303
+ | `light-trail` | two lights chasing each other on nested tracks |
304
+ | `neon-arc` | a tube of light growing and shrinking as it turns |
305
+ | `plasma` | churning light held inside a containment ring |
306
+ | `strobe` | halos firing outward on hard steps, not a smooth ripple |
307
+ | `glow-dots` | a chain of bulbs handing the current along |
308
+ | `retro-grid` | a synthwave horizon rushing toward you |
309
+ | `firefly` | soft lights drifting and blinking out of step |
310
+
311
+ </details>
312
+
313
+ <details>
314
+ <summary><b>Liquid</b> · 11 — Fluid behaviour — filling, pouring, rippling, merging.</summary>
315
+
316
+ | variant | what it looks like |
317
+ |---|---|
318
+ | `liquid-fill` | a vessel filling and draining behind a rolling surface |
319
+ | `drip` | a drop swelling at a nozzle, letting go, and landing in the pool |
320
+ | `metaball` | two drops pulling a neck between them, then merging |
321
+ | `wave-tank` | two swells sloshing past each other at a steady level |
322
+ | `ripple-pool` | rings spreading across a surface seen at a low angle |
323
+ | `mercury` | a bead of liquid metal wobbling under its own weight |
324
+ | `lava` | heavy blobs climbing a warm capsule and sinking back |
325
+ | `ink` | a drop of colour blooming out through water |
326
+ | `splash` | a drop hitting the surface and throwing up a crown |
327
+ | `whirl` | liquid spun into a funnel, with the eye opening at the centre |
328
+ | `pour` | a stream running into a glass that fills, then empties |
329
+
330
+ </details>
331
+
332
+ <details>
333
+ <summary><b>Glitch</b> · 11 — Digital damage: tearing, static, dropout, corruption.</summary>
334
+
335
+ | variant | what it looks like |
336
+ |---|---|
337
+ | `glitch-block` | a mark tearing into its colour channels and snapping back |
338
+ | `glitch-text` | lines of copy slipping out of register mid-render |
339
+ | `rgb-split` | three channels drifting apart and recombining |
340
+ | `scanlines` | a CRT panel with the raster rolling through it |
341
+ | `static-noise` | untuned signal, hard-cut frame to frame |
342
+ | `datamosh` | horizontal slices sliding off their own frame |
343
+ | `corrupt-bar` | a progress track that keeps losing its place |
344
+ | `signal-loss` | reception bars dropping out and clawing back |
345
+ | `vhs` | tracking damage crawling up a taped frame |
346
+ | `pixel-drift` | a tile field losing alignment, then re-seating |
347
+ | `terminal` | a prompt typing a line, cursor still blinking |
348
+
349
+ </details>
350
+
351
+ <details>
352
+ <summary><b>Particles</b> · 11 — Fields of small things falling, bursting, drifting, swarming.</summary>
353
+
354
+ | variant | what it looks like |
355
+ |---|---|
356
+ | `confetti` | paper tumbling down through the frame |
357
+ | `firework` | sparks thrown out and pulled back down by gravity |
358
+ | `snow` | soft flakes settling with a lazy sideways drift |
359
+ | `embers` | sparks lifting off a fire and burning out |
360
+ | `starfield` | stars stretching into streaks as you jump to light speed |
361
+ | `dust` | motes turning slowly in a shaft of light |
362
+ | `burst` | a ring of dots detonating outward and snapping home |
363
+ | `swarm` | a flock tightening into a knot and scattering again |
364
+ | `rain` | streaks coming down onto a wet line |
365
+ | `magnet` | particles dragged into a core, then released |
366
+ | `nebula` | coloured gas turning slowly, with stars picking out through it |
367
+
368
+ </details>
369
+
370
+ <details>
371
+ <summary><b>Futuristic</b> · 12 — Reticles, reactors, and hardware that does not exist yet.</summary>
372
+
373
+ | variant | what it looks like |
374
+ |---|---|
375
+ | `hud-ring` | instrument rings ticking against each other |
376
+ | `reactor` | a white-hot core inside a segmented containment ring |
377
+ | `hologram` | a projection standing in its own light cone |
378
+ | `warp` | frames rushing past you down a tunnel |
379
+ | `targeting` | brackets closing onto a lock |
380
+ | `circuit` | pulses running the traces of a board |
381
+ | `barcode` | a code being read, line by line |
382
+ | `quantum` | a particle taking every position it might occupy |
383
+ | `portal` | a gateway swirling open, two currents against each other |
384
+ | `shield` | an energy field taking hits and holding |
385
+ | `satellite` | bodies sweeping their own inclined orbits |
386
+ | `teleport` | slices dematerialising and coming back together |
387
+
388
+ </details>
389
+
390
+ <details>
391
+ <summary><b>Elastic</b> · 11 — Spring physics with genuine overshoot and recoil.</summary>
392
+
393
+ | variant | what it looks like |
394
+ |---|---|
395
+ | `rubber-band` | a band strung between two pins and plucked |
396
+ | `bungee` | a weight dropping on a cord that stretches to catch it |
397
+ | `trampoline` | a ball landing on a sheet that bows and throws it back |
398
+ | `wobble` | a weighted shape rocking past level and settling back |
399
+ | `pogo` | a hop with the squash on landing and a shadow that keeps up |
400
+ | `elastic-bar` | a pill that stretches into the direction it is thrown |
401
+ | `catapult` | flung up and over, then reset out of sight |
402
+ | `yo-yo` | spinning down the string and climbing back up it |
403
+ | `elastic-ring` | a hoop squashed along an axis that keeps turning |
404
+ | `snap-dots` | a thread pulled taut until it lets go |
405
+ | `chain` | a swing running down a linked strand, one bead behind the last |
406
+
407
+ </details>
408
+
409
+ <details>
410
+ <summary><b>Minimal</b> · 11 — Quiet motion for dense UI, toolbars, and inline text.</summary>
411
+
412
+ | variant | what it looks like |
413
+ |---|---|
414
+ | `hairline` | a lit segment travelling a thin rule |
415
+ | `tick` | a hand stepping round a quiet dial |
416
+ | `quarter` | one arc, snapping a quarter-turn at a time |
417
+ | `dot-fade` | one dot, one slow breath |
418
+ | `underline` | a rule tracking back and forth beneath a word |
419
+ | `pill-swap` | a pill stretching to the far end and collecting itself |
420
+ | `bracket` | two marks closing in on the point between them |
421
+ | `slash` | a single stroke wiping through a frame |
422
+ | `micro-dots` | the plainest ellipsis there is |
423
+ | `ring-thin` | a hairline ring with one short arc gliding round it |
424
+ | `crosshair` | two hairlines finding each other |
425
+
426
+ </details>
427
+
428
+ <details>
429
+ <summary><b>Gradient</b> · 11 — Colour itself is the animation.</summary>
430
+
431
+ | variant | what it looks like |
432
+ |---|---|
433
+ | `mesh` | a soft mesh gradient drifting under itself |
434
+ | `hue-ring` | a colour wheel turning while the colours themselves travel |
435
+ | `gradient-orb` | a lit sphere with the highlight sliding over it |
436
+ | `chroma` | three colour fields overlapping and pulling apart |
437
+ | `ombre` | one long gradient revealed a band at a time |
438
+ | `iridescent` | an oil-slick sheen crawling across a disc |
439
+ | `sunrise` | a disc climbing over its own horizon |
440
+ | `refract` | a beam split into its colours by a prism |
441
+ | `gradient-border` | a lit frame with the colour running round the edge |
442
+ | `color-cycle` | one block, the gradient axis sweeping right round it |
443
+ | `northern` | light curtains folding across the sky |
444
+
445
+ </details>
446
+
447
+ <details>
448
+ <summary><b>Kinetic</b> · 11 — Mechanisms — gears, cranks, belts, beams, and escapements.</summary>
449
+
450
+ | variant | what it looks like |
451
+ |---|---|
452
+ | `gears` | two toothed wheels driving each other |
453
+ | `metronome` | a weighted arm keeping time |
454
+ | `hourglass` | sand running out, then the whole thing turns over |
455
+ | `piston` | a head driven up and down by a crank |
456
+ | `carousel` | panels turning past you on a ring |
457
+ | `clockwork` | three hands, three speeds, one dial |
458
+ | `abacus` | beads counted back and forth along their rails |
459
+ | `conveyor` | crates carried along and sent round again |
460
+ | `pie` | a wedge sweeping the full turn, then handing over to the next colour |
461
+ | `seesaw` | a beam tipping between two weights |
462
+ | `roller` | balls rolling round the inside of a track |
463
+
464
+ </details>
465
+
466
+ <details>
467
+ <summary><b>Premium</b> · 11 — Product-grade motion for onboarding, uploads, and sync.</summary>
468
+
469
+ | variant | what it looks like |
470
+ |---|---|
471
+ | `logo-morph` | a brand mark fanning apart and re-forming |
472
+ | `card-stack` | the top card dealt to the back, over and over |
473
+ | `progress-ring` | a track filling round, with the head lit |
474
+ | `pill-progress` | a soft track with a glide and a glass sweep over it |
475
+ | `glass-ring` | frosted glass with one specular arc travelling the rim |
476
+ | `orbit-avatar` | a team circling a hub, each face staying upright |
477
+ | `checklist` | tasks ticking off one after another |
478
+ | `upload-cloud` | chevrons lifting into the cloud |
479
+ | `sync` | two arrows chasing each other round the loop |
480
+ | `steps` | a stepper advancing one node at a time |
481
+ | `spark-line` | a chart drawing itself under a moving read head |
482
+
483
+ </details>
484
+
485
+ ## Theming
486
+
487
+ The `colors` prop covers most cases. For anything else, override the custom properties — they
488
+ cascade, so one rule themes a whole subtree:
489
+
490
+ ```css
491
+ .dashboard {
492
+ --c1: #6366f1; /* primary */
493
+ --c2: #ec4899; /* secondary */
494
+ --c3: #22d3ee; /* tertiary */
495
+ --size: 64px; /* box size */
496
+ --speed: .9s; /* base duration */
497
+
498
+ --track: #1e1e2e; /* rail behind progress bars */
499
+ --grad: linear-gradient(90deg, var(--c1), var(--c3)); /* shared gradient */
500
+ }
103
501
  ```
104
502
 
105
- For GitHub Pages the site lives under `/<repo>/`, so build with a matching base:
503
+ `--track` and `--grad` derive from `--c1`–`--c3` automatically; set them directly for finer control.
106
504
 
107
- ```bash
108
- DEMO_BASE=/loader/ npm run build:demo
109
- npx gh-pages -d dist-demo # or push dist-demo to a gh-pages branch
505
+ ## Accessibility
506
+
507
+ - Each loader renders as `role="status"` with `aria-label`, so assistive tech announces it once.
508
+ - A decorative loader — one sitting beside visible "Loading…" text — should take `label={null}`,
509
+ which switches it to `aria-hidden` and prevents a double announcement.
510
+ - Under `prefers-reduced-motion: reduce`, every animation slows to a single calm 4s linear cycle
511
+ rather than stopping. A frozen loader reads as a crashed app, which is worse than gentle motion.
512
+
513
+ ## Bundle size
514
+
515
+ | | raw | gzipped |
516
+ |---|---|---|
517
+ | JS | ~18 kB | **~4 kB** |
518
+ | CSS (all 212 variants) | ~166 kB | **~24 kB** |
519
+
520
+ One caveat, stated plainly: **the CSS is not tree-shakeable.** Class-based styles cannot be
521
+ dead-code eliminated, so importing a single variant still ships the sheet for all 212. At ~24 kB
522
+ gzipped that is a fair trade for most apps, but it is real weight. If you need only a handful and
523
+ every kilobyte counts, copy the specific `.ldr--*` blocks out of [`src/styles/`](./src/styles)
524
+ into your own CSS and skip the package — each block is self-contained apart from the shared
525
+ keyframes in [`00-base.css`](./src/styles/00-base.css).
526
+
527
+ ## Named exports
528
+
529
+ `Loader` is the main export. The original ten also have wrappers, kept for backward compatibility
530
+ with 1.0.x:
531
+
532
+ ```tsx
533
+ import { Aurora, Orbit, Rings, Wave, Bars, Blob, Cube, Spiral, Bar, Grid } from "modern-loaders";
110
534
  ```
111
535
 
536
+ Everything else is reached with `<Loader variant="…" />`. Two hundred named exports would bloat
537
+ the API surface for no real gain, and `variant` autocompletes just as well.
538
+
112
539
  ## Browser support
113
540
 
114
- Uses `mask`, `color-mix()`, and `aspect-ratio`: Chrome 111+, Safari 16.2+, Firefox 113+.
115
- Older browsers degrade to solid shapes rather than breaking.
541
+ Chrome 111+ · Safari 16.4+ · Firefox 113+ — the floor is set by `color-mix()`, alongside `mask`,
542
+ `aspect-ratio`, `clip-path`, and the individual `translate` / `rotate` / `scale` properties. Older
543
+ browsers degrade to solid shapes rather than breaking.
544
+
545
+ Five variants — `neon-arc`, `pie`, `progress-ring`, `gradient-border`, `color-cycle` — animate a
546
+ registered custom property via `@property` (Firefox 128+). Every use passes a fallback, so below
547
+ that they render a sensible static frame instead of disappearing.
548
+
549
+ ## Development
550
+
551
+ ```bash
552
+ npm run dev # showcase: search, 20 family filters, live size / speed / palette, detail sheet
553
+ npm run build # typecheck, then emit dist/ (ESM + CJS + .d.ts + CSS)
554
+ npm run build:demo # static showcase → dist-demo/
555
+ ```
556
+
557
+ Adding a variant takes two edits: a `.ldr--<id>` block in `src/styles/`, and a row in
558
+ [`src/variants.ts`](./src/variants.ts). The component, the types, the README catalogue, and the
559
+ showcase all read from that one manifest — `cells` is how many `<span>` children the CSS expects.
560
+
561
+ Two rules keep the motion composable inside a variant: put a whole radial chain
562
+ (`rotate(…) translateY(…)`) in a single `transform` so the offset happens in the rotated frame,
563
+ and reach for the standalone `translate` / `rotate` / `scale` properties when an element already
564
+ animates `transform` — they compose instead of overwriting it.
565
+
566
+ ## License
567
+
568
+ ISC © suman