modern-loaders 1.0.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,115 @@
1
+ # @sumanrana/modern-loaders
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.
5
+
6
+ ```bash
7
+ npm i @sumanrana/modern-loaders
8
+ ```
9
+
10
+ ```tsx
11
+ import { Loader } from "@sumanrana/modern-loaders";
12
+
13
+ <Loader />
14
+ <Loader variant="wave" size={72} speed={0.8} colors={["#f97316", "#ef4444", "#a855f7"]} />
15
+ ```
16
+
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"`.)
19
+
20
+ ## Props
21
+
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`) |
29
+
30
+ Any other `div` prop (`className`, `style`, `id`, data-attributes…) passes straight through.
31
+
32
+ ## The 10 variants
33
+
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 |
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.
50
+
51
+ ## Styling
52
+
53
+ Everything is driven by CSS custom properties, so you can theme without the `colors` prop:
54
+
55
+ ```css
56
+ .my-loader { --c1: #6366f1; --c2: #ec4899; --c3: #22d3ee; --size: 64px; --speed: .9s; }
57
+ ```
58
+
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.
62
+
63
+ ## Development
64
+
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
69
+ ```
70
+
71
+ ## Publishing the package
72
+
73
+ The scope in `name` must be one your npm account owns. Check first:
74
+
75
+ ```bash
76
+ npm whoami # your username
77
+ npm view @sumanrana/modern-loaders # 404 = the name is free
78
+ ```
79
+
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:
82
+
83
+ ```bash
84
+ npm login
85
+ npm publish --access public # required for scoped packages
86
+ ```
87
+
88
+ `prepublishOnly` runs the build, and `files` limits the tarball to `dist/` + this README.
89
+
90
+ ## Deploying the demo
91
+
92
+ ```bash
93
+ npm run build:demo # → dist-demo/
94
+ npm run preview:demo # check it locally
95
+ ```
96
+
97
+ Then push `dist-demo/` to any static host:
98
+
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
103
+ ```
104
+
105
+ For GitHub Pages the site lives under `/<repo>/`, so build with a matching base:
106
+
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
110
+ ```
111
+
112
+ ## Browser support
113
+
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.
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});require('./index.css');const r=require("react/jsx-runtime"),g=["aurora","orbit","rings","wave","bars","blob","cube","spiral","bar","grid"],j={aurora:0,orbit:3,rings:3,wave:5,bars:5,blob:0,cube:2,spiral:0,bar:0,grid:9},o=(a,s)=>typeof a=="number"?`${a}${s}`:a;function t({variant:a="aurora",size:s=48,speed:b=1,colors:i,label:e="Loading",className:u,style:d,...c}){const n={"--size":o(s,"px"),"--speed":o(b,"s")};i!=null&&i.length&&(n["--c1"]=i[0],n["--c2"]=i[1%i.length],n["--c3"]=i[2%i.length]);const v=j[a];return r.jsx("div",{...c,className:["ldr",`ldr--${a}`,u].filter(Boolean).join(" "),style:{...n,...d},role:e?"status":void 0,"aria-label":e??void 0,"aria-hidden":e?void 0:!0,children:Array.from({length:v},(y,l)=>r.jsx("span",{},l))})}const x=a=>r.jsx(t,{variant:"aurora",...a}),p=a=>r.jsx(t,{variant:"orbit",...a}),A=a=>r.jsx(t,{variant:"rings",...a}),B=a=>r.jsx(t,{variant:"wave",...a}),L=a=>r.jsx(t,{variant:"bars",...a}),R=a=>r.jsx(t,{variant:"blob",...a}),S=a=>r.jsx(t,{variant:"cube",...a}),f=a=>r.jsx(t,{variant:"spiral",...a}),h=a=>r.jsx(t,{variant:"bar",...a}),m=a=>r.jsx(t,{variant:"grid",...a});exports.Aurora=x;exports.Bar=h;exports.Bars=L;exports.Blob=R;exports.Cube=S;exports.Grid=m;exports.LOADER_VARIANTS=g;exports.Loader=t;exports.Orbit=p;exports.Rings=A;exports.Spiral=f;exports.Wave=B;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/Loader.tsx"],"sourcesContent":["import type { CSSProperties, HTMLAttributes } from \"react\";\nimport \"./loaders.css\";\n\nexport const LOADER_VARIANTS = [\n \"aurora\",\n \"orbit\",\n \"rings\",\n \"wave\",\n \"bars\",\n \"blob\",\n \"cube\",\n \"spiral\",\n \"bar\",\n \"grid\",\n] as const;\n\nexport type LoaderVariant = (typeof LOADER_VARIANTS)[number];\n\n/** How many <span> children each variant's CSS expects. */\nconst CELLS: Record<LoaderVariant, number> = {\n aurora: 0,\n orbit: 3,\n rings: 3,\n wave: 5,\n bars: 5,\n blob: 0,\n cube: 2,\n spiral: 0,\n bar: 0,\n grid: 9,\n};\n\nexport interface LoaderProps extends Omit<HTMLAttributes<HTMLDivElement>, \"color\"> {\n /** Which loader to render. @default \"aurora\" */\n variant?: LoaderVariant;\n /** Box size. A number is treated as pixels. @default 48 */\n size?: number | string;\n /** Base animation duration. A number is treated as seconds. @default 1 */\n speed?: number | string;\n /**\n * One to three colors. Fewer than three are cycled, so a single color still\n * produces a coherent (monochrome) loader.\n */\n colors?: string[];\n /** Announced to screen readers. Pass `null` to render the loader as decorative. */\n label?: string | null;\n}\n\nconst unit = (v: number | string | undefined, suffix: string) =>\n typeof v === \"number\" ? `${v}${suffix}` : v;\n\nexport function Loader({\n variant = \"aurora\",\n size = 48,\n speed = 1,\n colors,\n label = \"Loading\",\n className,\n style,\n ...rest\n}: LoaderProps) {\n const vars: CSSProperties & Record<string, string | undefined> = {\n \"--size\": unit(size, \"px\"),\n \"--speed\": unit(speed, \"s\"),\n };\n\n if (colors?.length) {\n // Cycle whatever was given across the three slots the CSS reads.\n vars[\"--c1\"] = colors[0];\n vars[\"--c2\"] = colors[1 % colors.length];\n vars[\"--c3\"] = colors[2 % colors.length];\n }\n\n const cells = CELLS[variant];\n\n return (\n <div\n {...rest}\n className={[\"ldr\", `ldr--${variant}`, className].filter(Boolean).join(\" \")}\n style={{ ...vars, ...style }}\n role={label ? \"status\" : undefined}\n aria-label={label ?? undefined}\n aria-hidden={label ? undefined : true}\n >\n {Array.from({ length: cells }, (_, i) => (\n <span key={i} />\n ))}\n </div>\n );\n}\n\n/** Convenience wrappers, so `<Aurora />` works as well as `<Loader variant=\"aurora\" />`. */\ntype VariantProps = Omit<LoaderProps, \"variant\">;\n\nexport const Aurora = (p: VariantProps) => <Loader variant=\"aurora\" {...p} />;\nexport const Orbit = (p: VariantProps) => <Loader variant=\"orbit\" {...p} />;\nexport const Rings = (p: VariantProps) => <Loader variant=\"rings\" {...p} />;\nexport const Wave = (p: VariantProps) => <Loader variant=\"wave\" {...p} />;\nexport const Bars = (p: VariantProps) => <Loader variant=\"bars\" {...p} />;\nexport const Blob = (p: VariantProps) => <Loader variant=\"blob\" {...p} />;\nexport const Cube = (p: VariantProps) => <Loader variant=\"cube\" {...p} />;\nexport const Spiral = (p: VariantProps) => <Loader variant=\"spiral\" {...p} />;\nexport const Bar = (p: VariantProps) => <Loader variant=\"bar\" {...p} />;\nexport const Grid = (p: VariantProps) => <Loader variant=\"grid\" {...p} />;\n"],"names":["LOADER_VARIANTS","CELLS","unit","v","suffix","Loader","variant","size","speed","colors","label","className","style","rest","vars","cells","jsx","_","i","Aurora","p","Orbit","Rings","Wave","Bars","Blob","Cube","Spiral","Bar","Grid"],"mappings":"qHAGaA,EAAkB,CAC7B,SACA,QACA,QACA,OACA,OACA,OACA,OACA,SACA,MACA,MACF,EAKMC,EAAuC,CAC3C,OAAQ,EACR,MAAO,EACP,MAAO,EACP,KAAM,EACN,KAAM,EACN,KAAM,EACN,KAAM,EACN,OAAQ,EACR,IAAK,EACL,KAAM,CACR,EAkBMC,EAAO,CAACC,EAAgCC,IAC5C,OAAOD,GAAM,SAAW,GAAGA,CAAC,GAAGC,CAAM,GAAKD,EAErC,SAASE,EAAO,CACrB,QAAAC,EAAU,SACV,KAAAC,EAAO,GACP,MAAAC,EAAQ,EACR,OAAAC,EACA,MAAAC,EAAQ,UACR,UAAAC,EACA,MAAAC,EACA,GAAGC,CACL,EAAgB,CACd,MAAMC,EAA2D,CAC/D,SAAUZ,EAAKK,EAAM,IAAI,EACzB,UAAWL,EAAKM,EAAO,GAAG,CAAA,EAGxBC,GAAA,MAAAA,EAAQ,SAEVK,EAAK,MAAM,EAAIL,EAAO,CAAC,EACvBK,EAAK,MAAM,EAAIL,EAAO,EAAIA,EAAO,MAAM,EACvCK,EAAK,MAAM,EAAIL,EAAO,EAAIA,EAAO,MAAM,GAGzC,MAAMM,EAAQd,EAAMK,CAAO,EAE3B,OACEU,EAAAA,IAAC,MAAA,CACE,GAAGH,EACJ,UAAW,CAAC,MAAO,QAAQP,CAAO,GAAIK,CAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,EACzE,MAAO,CAAE,GAAGG,EAAM,GAAGF,CAAA,EACrB,KAAMF,EAAQ,SAAW,OACzB,aAAYA,GAAS,OACrB,cAAaA,EAAQ,OAAY,GAEhC,SAAA,MAAM,KAAK,CAAE,OAAQK,CAAA,EAAS,CAACE,EAAGC,IACjCF,EAAAA,IAAC,OAAA,CAAA,EAAUE,CAAG,CACf,CAAA,CAAA,CAGP,CAKO,MAAMC,EAAUC,GAAoBJ,MAACX,GAAO,QAAQ,SAAU,GAAGe,CAAA,CAAG,EAC9DC,EAAUD,GAAoBJ,MAACX,GAAO,QAAQ,QAAU,GAAGe,CAAA,CAAG,EAC9DE,EAAUF,GAAoBJ,MAACX,GAAO,QAAQ,QAAU,GAAGe,CAAA,CAAG,EAC9DG,EAAUH,GAAoBJ,MAACX,GAAO,QAAQ,OAAU,GAAGe,CAAA,CAAG,EAC9DI,EAAUJ,GAAoBJ,MAACX,GAAO,QAAQ,OAAU,GAAGe,CAAA,CAAG,EAC9DK,EAAUL,GAAoBJ,MAACX,GAAO,QAAQ,OAAU,GAAGe,CAAA,CAAG,EAC9DM,EAAUN,GAAoBJ,MAACX,GAAO,QAAQ,OAAU,GAAGe,CAAA,CAAG,EAC9DO,EAAUP,GAAoBJ,MAACX,GAAO,QAAQ,SAAU,GAAGe,CAAA,CAAG,EAC9DQ,EAAUR,GAAoBJ,MAACX,GAAO,QAAQ,MAAU,GAAGe,CAAA,CAAG,EAC9DS,EAAUT,GAAoBJ,MAACX,GAAO,QAAQ,OAAU,GAAGe,CAAA,CAAG"}
package/dist/index.css ADDED
@@ -0,0 +1 @@
1
+ .ldr{--size: 48px;--speed: 1s;--c1: #6366f1;--c2: #ec4899;--c3: #22d3ee;--track: color-mix(in oklab, var(--c1) 18%, transparent);width:var(--size);height:var(--size);flex:none;display:inline-block;position:relative;box-sizing:border-box}.ldr *,.ldr *:before,.ldr *:after{box-sizing:border-box}.ldr--aurora{border-radius:50%;background:conic-gradient(from 0turn,var(--c1),var(--c2),var(--c3),var(--c1));-webkit-mask:radial-gradient(farthest-side,#0000 85%,#000 86%);mask:radial-gradient(farthest-side,#0000 85%,#000 86%);animation:ldr-spin var(--speed) linear infinite}.ldr--aurora:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:inherit;background:inherit;filter:blur(calc(var(--size) / 8));opacity:.55;animation:ldr-breathe calc(var(--speed) * 2) ease-in-out infinite}.ldr--orbit span{position:absolute;top:0;left:50%;width:24%;height:24%;margin-left:-12%;border-radius:50%;transform-origin:50% calc(var(--size) / 2);animation:ldr-spin var(--speed) cubic-bezier(.6,.1,.4,.9) infinite}.ldr--orbit span:nth-child(1){background:var(--c1)}.ldr--orbit span:nth-child(2){background:var(--c2);animation-delay:calc(var(--speed) * -.08);opacity:.75;scale:.8}.ldr--orbit span:nth-child(3){background:var(--c3);animation-delay:calc(var(--speed) * -.16);opacity:.5;scale:.6}.ldr--rings span{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:50%;border:calc(var(--size) / 16) solid;animation:ldr-ripple calc(var(--speed) * 1.8) cubic-bezier(0,.2,.8,1) infinite}.ldr--rings span:nth-child(1){border-color:var(--c1)}.ldr--rings span:nth-child(2){border-color:var(--c2);animation-delay:calc(var(--speed) * -.6)}.ldr--rings span:nth-child(3){border-color:var(--c3);animation-delay:calc(var(--speed) * -1.2)}.ldr--wave{display:inline-flex;align-items:center;justify-content:space-between;width:calc(var(--size) * 1.6)}.ldr--wave span{width:15%;aspect-ratio:1;border-radius:50%;animation:ldr-wave calc(var(--speed) * 1.1) cubic-bezier(.4,0,.6,1) infinite}.ldr--wave span:nth-child(1){background:var(--c1)}.ldr--wave span:nth-child(2){background:color-mix(in oklab,var(--c1),var(--c2));animation-delay:calc(var(--speed) * .1)}.ldr--wave span:nth-child(3){background:var(--c2);animation-delay:calc(var(--speed) * .2)}.ldr--wave span:nth-child(4){background:color-mix(in oklab,var(--c2),var(--c3));animation-delay:calc(var(--speed) * .3)}.ldr--wave span:nth-child(5){background:var(--c3);animation-delay:calc(var(--speed) * .4)}.ldr--bars{display:inline-flex;align-items:center;width:calc(var(--size) * 1.25);gap:calc(var(--size) / 11)}.ldr--bars span{flex:1;height:100%;border-radius:99px;background:linear-gradient(var(--c1),var(--c2),var(--c3));animation:ldr-eq calc(var(--speed) * 1.2) ease-in-out infinite}.ldr--bars span:nth-child(2){animation-delay:calc(var(--speed) * -1)}.ldr--bars span:nth-child(3){animation-delay:calc(var(--speed) * -.85)}.ldr--bars span:nth-child(4){animation-delay:calc(var(--speed) * -.7)}.ldr--bars span:nth-child(5){animation-delay:calc(var(--speed) * -.55)}.ldr--blob{background:linear-gradient(135deg,var(--c1),var(--c2) 50%,var(--c3));background-size:200% 200%;animation:ldr-morph calc(var(--speed) * 2.4) ease-in-out infinite,ldr-spin calc(var(--speed) * 3) ease-in-out infinite,ldr-shift calc(var(--speed) * 2) ease-in-out infinite}.ldr--cube{perspective:calc(var(--size) * 4)}.ldr--cube span{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:calc(var(--size) / 7);backface-visibility:hidden;animation:ldr-flip calc(var(--speed) * 2) cubic-bezier(.6,.05,.4,.95) infinite}.ldr--cube span:nth-child(1){background:linear-gradient(135deg,var(--c1),var(--c2))}.ldr--cube span:nth-child(2){background:linear-gradient(135deg,var(--c2),var(--c3));transform:rotateY(180deg)}.ldr--spiral:before,.ldr--spiral:after{content:"";position:absolute;border-radius:50%;border:calc(var(--size) / 14) solid transparent}.ldr--spiral:before{top:0;right:0;bottom:0;left:0;border-top-color:var(--c1);border-right-color:var(--c2);animation:ldr-spin var(--speed) cubic-bezier(.5,.1,.5,.9) infinite}.ldr--spiral:after{top:28%;right:28%;bottom:28%;left:28%;border-bottom-color:var(--c3);border-left-color:var(--c2);animation:ldr-spin calc(var(--speed) * .75) linear infinite reverse}.ldr--bar{width:calc(var(--size) * 3.4);height:calc(var(--size) / 6);border-radius:99px;background:var(--track);overflow:hidden}.ldr--bar:after{content:"";position:absolute;inset:0 auto 0 0;width:40%;border-radius:inherit;background:linear-gradient(90deg,var(--c1),var(--c2),var(--c3));animation:ldr-slide calc(var(--speed) * 1.5) cubic-bezier(.65,.05,.35,.95) infinite}.ldr--grid{display:grid;grid-template-columns:repeat(3,1fr);gap:calc(var(--size) / 12)}.ldr--grid span{border-radius:calc(var(--size) / 10);animation:ldr-pop calc(var(--speed) * 1.3) ease-in-out infinite}.ldr--grid span:nth-child(1){background:var(--c1);animation-delay:calc(var(--speed) * 0)}.ldr--grid span:nth-child(2),.ldr--grid span:nth-child(4){background:color-mix(in oklab,var(--c1),var(--c2));animation-delay:calc(var(--speed) * .1)}.ldr--grid span:nth-child(3),.ldr--grid span:nth-child(5),.ldr--grid span:nth-child(7){background:var(--c2);animation-delay:calc(var(--speed) * .2)}.ldr--grid span:nth-child(6),.ldr--grid span:nth-child(8){background:color-mix(in oklab,var(--c2),var(--c3));animation-delay:calc(var(--speed) * .3)}.ldr--grid span:nth-child(9){background:var(--c3);animation-delay:calc(var(--speed) * .4)}@keyframes ldr-spin{to{transform:rotate(1turn)}}@keyframes ldr-breathe{0%,to{opacity:.35}50%{opacity:.7}}@keyframes ldr-ripple{0%{transform:scale(.15);opacity:1}to{transform:scale(1);opacity:0}}@keyframes ldr-wave{0%,60%,to{transform:translateY(0);opacity:.55}30%{transform:translateY(-80%);opacity:1}}@keyframes ldr-eq{0%,to{transform:scaleY(.22);opacity:.55}50%{transform:scaleY(1);opacity:1}}@keyframes ldr-morph{0%,to{border-radius:50%}25%{border-radius:62% 38% 46% 54%/60% 55% 45% 40%}50%{border-radius:24%}75%{border-radius:38% 62% 60% 40%/42% 38% 62% 58%}}@keyframes ldr-shift{0%,to{background-position:0% 50%}50%{background-position:100% 50%}}@keyframes ldr-flip{0%{transform:rotateY(0) rotateX(0)}50%{transform:rotateY(180deg) rotateX(0)}to{transform:rotateY(180deg) rotateX(180deg)}}@keyframes ldr-slide{0%{left:-40%;width:40%}50%{left:25%;width:55%}to{left:100%;width:40%}}@keyframes ldr-pop{0%,70%,to{transform:scale(.62);opacity:.5}35%{transform:scale(1);opacity:1}}@media(prefers-reduced-motion:reduce){.ldr,.ldr:before,.ldr:after,.ldr span{animation-duration:4s!important;animation-timing-function:linear!important}}
@@ -0,0 +1,50 @@
1
+ import { HTMLAttributes } from 'react';
2
+ import { JSX as JSX_2 } from 'react';
3
+
4
+ export declare const Aurora: (p: VariantProps) => JSX_2.Element;
5
+
6
+ export declare const Bar: (p: VariantProps) => JSX_2.Element;
7
+
8
+ export declare const Bars: (p: VariantProps) => JSX_2.Element;
9
+
10
+ declare const Blob_2: (p: VariantProps) => JSX_2.Element;
11
+ export { Blob_2 as Blob }
12
+
13
+ export declare const Cube: (p: VariantProps) => JSX_2.Element;
14
+
15
+ export declare const Grid: (p: VariantProps) => JSX_2.Element;
16
+
17
+ export declare function Loader({ variant, size, speed, colors, label, className, style, ...rest }: LoaderProps): JSX_2.Element;
18
+
19
+ export declare const LOADER_VARIANTS: readonly ["aurora", "orbit", "rings", "wave", "bars", "blob", "cube", "spiral", "bar", "grid"];
20
+
21
+ export declare interface LoaderProps extends Omit<HTMLAttributes<HTMLDivElement>, "color"> {
22
+ /** Which loader to render. @default "aurora" */
23
+ variant?: LoaderVariant;
24
+ /** Box size. A number is treated as pixels. @default 48 */
25
+ size?: number | string;
26
+ /** Base animation duration. A number is treated as seconds. @default 1 */
27
+ speed?: number | string;
28
+ /**
29
+ * One to three colors. Fewer than three are cycled, so a single color still
30
+ * produces a coherent (monochrome) loader.
31
+ */
32
+ colors?: string[];
33
+ /** Announced to screen readers. Pass `null` to render the loader as decorative. */
34
+ label?: string | null;
35
+ }
36
+
37
+ export declare type LoaderVariant = (typeof LOADER_VARIANTS)[number];
38
+
39
+ export declare const Orbit: (p: VariantProps) => JSX_2.Element;
40
+
41
+ export declare const Rings: (p: VariantProps) => JSX_2.Element;
42
+
43
+ export declare const Spiral: (p: VariantProps) => JSX_2.Element;
44
+
45
+ /** Convenience wrappers, so `<Aurora />` works as well as `<Loader variant="aurora" />`. */
46
+ declare type VariantProps = Omit<LoaderProps, "variant">;
47
+
48
+ export declare const Wave: (p: VariantProps) => JSX_2.Element;
49
+
50
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ import { jsx as r } from "react/jsx-runtime";
2
+ import './index.css';const h = [
3
+ "aurora",
4
+ "orbit",
5
+ "rings",
6
+ "wave",
7
+ "bars",
8
+ "blob",
9
+ "cube",
10
+ "spiral",
11
+ "bar",
12
+ "grid"
13
+ ], l = {
14
+ aurora: 0,
15
+ orbit: 3,
16
+ rings: 3,
17
+ wave: 5,
18
+ bars: 5,
19
+ blob: 0,
20
+ cube: 2,
21
+ spiral: 0,
22
+ bar: 0,
23
+ grid: 9
24
+ }, o = (a, s) => typeof a == "number" ? `${a}${s}` : a;
25
+ function n({
26
+ variant: a = "aurora",
27
+ size: s = 48,
28
+ speed: b = 1,
29
+ colors: t,
30
+ label: e = "Loading",
31
+ className: c,
32
+ style: d,
33
+ ...v
34
+ }) {
35
+ const i = {
36
+ "--size": o(s, "px"),
37
+ "--speed": o(b, "s")
38
+ };
39
+ t != null && t.length && (i["--c1"] = t[0], i["--c2"] = t[1 % t.length], i["--c3"] = t[2 % t.length]);
40
+ const u = l[a];
41
+ return /* @__PURE__ */ r(
42
+ "div",
43
+ {
44
+ ...v,
45
+ className: ["ldr", `ldr--${a}`, c].filter(Boolean).join(" "),
46
+ style: { ...i, ...d },
47
+ role: e ? "status" : void 0,
48
+ "aria-label": e ?? void 0,
49
+ "aria-hidden": e ? void 0 : !0,
50
+ children: Array.from({ length: u }, (p, g) => /* @__PURE__ */ r("span", {}, g))
51
+ }
52
+ );
53
+ }
54
+ const m = (a) => /* @__PURE__ */ r(n, { variant: "aurora", ...a }), A = (a) => /* @__PURE__ */ r(n, { variant: "orbit", ...a }), L = (a) => /* @__PURE__ */ r(n, { variant: "rings", ...a }), B = (a) => /* @__PURE__ */ r(n, { variant: "wave", ...a }), w = (a) => /* @__PURE__ */ r(n, { variant: "bars", ...a }), x = (a) => /* @__PURE__ */ r(n, { variant: "blob", ...a }), y = (a) => /* @__PURE__ */ r(n, { variant: "cube", ...a }), R = (a) => /* @__PURE__ */ r(n, { variant: "spiral", ...a }), S = (a) => /* @__PURE__ */ r(n, { variant: "bar", ...a }), $ = (a) => /* @__PURE__ */ r(n, { variant: "grid", ...a });
55
+ export {
56
+ m as Aurora,
57
+ S as Bar,
58
+ w as Bars,
59
+ x as Blob,
60
+ y as Cube,
61
+ $ as Grid,
62
+ h as LOADER_VARIANTS,
63
+ n as Loader,
64
+ A as Orbit,
65
+ L as Rings,
66
+ R as Spiral,
67
+ B as Wave
68
+ };
69
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/Loader.tsx"],"sourcesContent":["import type { CSSProperties, HTMLAttributes } from \"react\";\nimport \"./loaders.css\";\n\nexport const LOADER_VARIANTS = [\n \"aurora\",\n \"orbit\",\n \"rings\",\n \"wave\",\n \"bars\",\n \"blob\",\n \"cube\",\n \"spiral\",\n \"bar\",\n \"grid\",\n] as const;\n\nexport type LoaderVariant = (typeof LOADER_VARIANTS)[number];\n\n/** How many <span> children each variant's CSS expects. */\nconst CELLS: Record<LoaderVariant, number> = {\n aurora: 0,\n orbit: 3,\n rings: 3,\n wave: 5,\n bars: 5,\n blob: 0,\n cube: 2,\n spiral: 0,\n bar: 0,\n grid: 9,\n};\n\nexport interface LoaderProps extends Omit<HTMLAttributes<HTMLDivElement>, \"color\"> {\n /** Which loader to render. @default \"aurora\" */\n variant?: LoaderVariant;\n /** Box size. A number is treated as pixels. @default 48 */\n size?: number | string;\n /** Base animation duration. A number is treated as seconds. @default 1 */\n speed?: number | string;\n /**\n * One to three colors. Fewer than three are cycled, so a single color still\n * produces a coherent (monochrome) loader.\n */\n colors?: string[];\n /** Announced to screen readers. Pass `null` to render the loader as decorative. */\n label?: string | null;\n}\n\nconst unit = (v: number | string | undefined, suffix: string) =>\n typeof v === \"number\" ? `${v}${suffix}` : v;\n\nexport function Loader({\n variant = \"aurora\",\n size = 48,\n speed = 1,\n colors,\n label = \"Loading\",\n className,\n style,\n ...rest\n}: LoaderProps) {\n const vars: CSSProperties & Record<string, string | undefined> = {\n \"--size\": unit(size, \"px\"),\n \"--speed\": unit(speed, \"s\"),\n };\n\n if (colors?.length) {\n // Cycle whatever was given across the three slots the CSS reads.\n vars[\"--c1\"] = colors[0];\n vars[\"--c2\"] = colors[1 % colors.length];\n vars[\"--c3\"] = colors[2 % colors.length];\n }\n\n const cells = CELLS[variant];\n\n return (\n <div\n {...rest}\n className={[\"ldr\", `ldr--${variant}`, className].filter(Boolean).join(\" \")}\n style={{ ...vars, ...style }}\n role={label ? \"status\" : undefined}\n aria-label={label ?? undefined}\n aria-hidden={label ? undefined : true}\n >\n {Array.from({ length: cells }, (_, i) => (\n <span key={i} />\n ))}\n </div>\n );\n}\n\n/** Convenience wrappers, so `<Aurora />` works as well as `<Loader variant=\"aurora\" />`. */\ntype VariantProps = Omit<LoaderProps, \"variant\">;\n\nexport const Aurora = (p: VariantProps) => <Loader variant=\"aurora\" {...p} />;\nexport const Orbit = (p: VariantProps) => <Loader variant=\"orbit\" {...p} />;\nexport const Rings = (p: VariantProps) => <Loader variant=\"rings\" {...p} />;\nexport const Wave = (p: VariantProps) => <Loader variant=\"wave\" {...p} />;\nexport const Bars = (p: VariantProps) => <Loader variant=\"bars\" {...p} />;\nexport const Blob = (p: VariantProps) => <Loader variant=\"blob\" {...p} />;\nexport const Cube = (p: VariantProps) => <Loader variant=\"cube\" {...p} />;\nexport const Spiral = (p: VariantProps) => <Loader variant=\"spiral\" {...p} />;\nexport const Bar = (p: VariantProps) => <Loader variant=\"bar\" {...p} />;\nexport const Grid = (p: VariantProps) => <Loader variant=\"grid\" {...p} />;\n"],"names":["LOADER_VARIANTS","CELLS","unit","v","suffix","Loader","variant","size","speed","colors","label","className","style","rest","vars","cells","jsx","_","i","Aurora","p","Orbit","Rings","Wave","Bars","Blob","Cube","Spiral","Bar","Grid"],"mappings":";AAGO,MAAMA,IAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKMC,IAAuC;AAAA,EAC3C,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,MAAM;AACR,GAkBMC,IAAO,CAACC,GAAgCC,MAC5C,OAAOD,KAAM,WAAW,GAAGA,CAAC,GAAGC,CAAM,KAAKD;AAErC,SAASE,EAAO;AAAA,EACrB,SAAAC,IAAU;AAAA,EACV,MAAAC,IAAO;AAAA,EACP,OAAAC,IAAQ;AAAA,EACR,QAAAC;AAAA,EACA,OAAAC,IAAQ;AAAA,EACR,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,GAAGC;AACL,GAAgB;AACd,QAAMC,IAA2D;AAAA,IAC/D,UAAUZ,EAAKK,GAAM,IAAI;AAAA,IACzB,WAAWL,EAAKM,GAAO,GAAG;AAAA,EAAA;AAG5B,EAAIC,KAAA,QAAAA,EAAQ,WAEVK,EAAK,MAAM,IAAIL,EAAO,CAAC,GACvBK,EAAK,MAAM,IAAIL,EAAO,IAAIA,EAAO,MAAM,GACvCK,EAAK,MAAM,IAAIL,EAAO,IAAIA,EAAO,MAAM;AAGzC,QAAMM,IAAQd,EAAMK,CAAO;AAE3B,SACE,gBAAAU;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAGH;AAAA,MACJ,WAAW,CAAC,OAAO,QAAQP,CAAO,IAAIK,CAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,MACzE,OAAO,EAAE,GAAGG,GAAM,GAAGF,EAAA;AAAA,MACrB,MAAMF,IAAQ,WAAW;AAAA,MACzB,cAAYA,KAAS;AAAA,MACrB,eAAaA,IAAQ,SAAY;AAAA,MAEhC,UAAA,MAAM,KAAK,EAAE,QAAQK,EAAA,GAAS,CAACE,GAAGC,MACjC,gBAAAF,EAAC,QAAA,CAAA,GAAUE,CAAG,CACf;AAAA,IAAA;AAAA,EAAA;AAGP;AAKO,MAAMC,IAAS,CAACC,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,UAAU,GAAGe,EAAA,CAAG,GAC9DC,IAAS,CAACD,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,SAAU,GAAGe,EAAA,CAAG,GAC9DE,IAAS,CAACF,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,SAAU,GAAGe,EAAA,CAAG,GAC9DG,IAAS,CAACH,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,QAAU,GAAGe,EAAA,CAAG,GAC9DI,IAAS,CAACJ,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,QAAU,GAAGe,EAAA,CAAG,GAC9DK,IAAS,CAACL,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,QAAU,GAAGe,EAAA,CAAG,GAC9DM,IAAS,CAACN,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,QAAU,GAAGe,EAAA,CAAG,GAC9DO,IAAS,CAACP,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,UAAU,GAAGe,EAAA,CAAG,GAC9DQ,IAAS,CAACR,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,OAAU,GAAGe,EAAA,CAAG,GAC9DS,IAAS,CAACT,MAAoB,gBAAAJ,EAACX,KAAO,SAAQ,QAAU,GAAGe,EAAA,CAAG;"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "modern-loaders",
3
+ "version": "1.0.0",
4
+ "description": "10 modern, colorful, animated React loaders — pure CSS, zero runtime dependencies",
5
+ "author": "suman",
6
+ "license": "ISC",
7
+ "type": "module",
8
+ "sideEffects": [
9
+ "**/*.css"
10
+ ],
11
+ "main": "./dist/index.cjs",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js",
18
+ "require": "./dist/index.cjs"
19
+ },
20
+ "./styles.css": "./dist/index.css"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md"
25
+ ],
26
+ "keywords": [
27
+ "react",
28
+ "loader",
29
+ "spinner",
30
+ "loading",
31
+ "css",
32
+ "animation",
33
+ "skeleton"
34
+ ],
35
+ "scripts": {
36
+ "dev": "vite",
37
+ "build": "tsc --noEmit && vite build",
38
+ "preview": "vite preview",
39
+ "typecheck": "tsc --noEmit",
40
+ "prepublishOnly": "npm run build",
41
+ "build:demo": "vite build --config vite.demo.config.ts",
42
+ "preview:demo": "vite preview --config vite.demo.config.ts"
43
+ },
44
+ "peerDependencies": {
45
+ "react": ">=17",
46
+ "react-dom": ">=17"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.10.2",
50
+ "@types/react": "^18.3.12",
51
+ "@types/react-dom": "^18.3.1",
52
+ "@vitejs/plugin-react": "^4.3.4",
53
+ "react": "^18.3.1",
54
+ "react-dom": "^18.3.1",
55
+ "typescript": "^5.7.2",
56
+ "vite": "^6.0.5",
57
+ "vite-plugin-dts": "^4.4.0",
58
+ "vite-plugin-lib-inject-css": "^2.2.0"
59
+ }
60
+ }