@shimmer-from-structure/svelte 1.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,728 @@
1
+ # ✨ Shimmer From Structure
2
+
3
+ A **React, Vue & Svelte** shimmer/skeleton library that **automatically adapts to your component's runtime structure**. Unlike traditional shimmer libraries that require pre-defined skeleton structures, this library analyzes your actual component's DOM at runtime and generates a shimmer effect that perfectly matches its layout.
4
+
5
+ ![React](https://img.shields.io/badge/React-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB)
6
+ ![Vue](https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D)
7
+ ![Svelte](https://img.shields.io/badge/Svelte-ff3e00?style=for-the-badge&logo=svelte&logoColor=white)
8
+
9
+ ![Shimmer From Structure Demo](https://github.com/darula-hpp/shimmer-from-structure/raw/main/example/preview.gif)
10
+
11
+ ## Why This Library?
12
+
13
+ Traditional shimmer libraries require you to:
14
+
15
+ - Manually create skeleton components that mirror your real components
16
+ - Maintain two versions of each component (real + skeleton)
17
+ - Update skeletons every time your layout changes
18
+
19
+ **Shimmer From Structure** eliminates all of that:
20
+
21
+ - ✅ **Works with React, Vue & Svelte** - Simple, framework-specific drivers
22
+ - ✅ Automatically measures your component's structure at runtime
23
+ - ✅ Generates shimmer effects that match actual dimensions
24
+ - ✅ Zero maintenance - works with any layout changes
25
+ - ✅ Works with complex nested structures
26
+ - ✅ Supports dynamic data with `templateProps`
27
+ - ✅ Preserves container backgrounds during loading
28
+ - ✅ Auto-detects border-radius from your CSS
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npm install shimmer-from-structure
34
+ # or
35
+ yarn add shimmer-from-structure
36
+ # or
37
+ pnpm add shimmer-from-structure
38
+ ```
39
+
40
+ ## 🎯 Framework Support
41
+
42
+ Shimmer From Structure provides dedicated packages for **React and Vue**.
43
+
44
+ ### React
45
+
46
+ React support is built-in to the main package for backward compatibility:
47
+
48
+ ```javascript
49
+ // React projects (or @shimmer-from-structure/react)
50
+ import { Shimmer } from 'shimmer-from-structure';
51
+ ```
52
+
53
+ ### Vue 3
54
+
55
+ Vue support requires importing from the specific adapter:
56
+
57
+ ```javascript
58
+ // Vue 3 projects
59
+ import { Shimmer } from '@shimmer-from-structure/vue';
60
+ ```
61
+
62
+ ### Svelte
63
+
64
+ Svelte support is provided via its own adapter:
65
+
66
+ ```javascript
67
+ // Svelte projects
68
+ import { Shimmer } from '@shimmer-from-structure/svelte';
69
+ ```
70
+
71
+ ---
72
+
73
+ # 📖 Basic Usage
74
+
75
+ ## React
76
+
77
+ ### Static Content
78
+
79
+ For components with hardcoded/static content:
80
+
81
+ ```tsx
82
+ import { Shimmer } from 'shimmer-from-structure';
83
+
84
+ function UserCard() {
85
+ return (
86
+ <Shimmer loading={isLoading}>
87
+ <div className="card">
88
+ <img src="avatar.jpg" className="avatar" />
89
+ <h2>John Doe</h2>
90
+ <p>Software Engineer</p>
91
+ </div>
92
+ </Shimmer>
93
+ );
94
+ }
95
+ ```
96
+
97
+ ## Vue
98
+
99
+ ### Static Content
100
+
101
+ ```vue
102
+ <script setup>
103
+ import { ref } from 'vue';
104
+ import { Shimmer } from '@shimmer-from-structure/vue';
105
+
106
+ const isLoading = ref(true);
107
+ </script>
108
+
109
+ <template>
110
+ <Shimmer :loading="isLoading">
111
+ <div class="card">
112
+ <img src="avatar.jpg" class="avatar" />
113
+ <h2>John Doe</h2>
114
+ <p>Software Engineer</p>
115
+ </div>
116
+ </Shimmer>
117
+ </template>
118
+ ```
119
+
120
+ ## Svelte
121
+
122
+ ### Static Content
123
+
124
+ ```svelte
125
+ <script>
126
+ import { Shimmer } from '@shimmer-from-structure/svelte';
127
+ let isLoading = true;
128
+ </script>
129
+
130
+ <Shimmer loading={isLoading}>
131
+ <div class="card">
132
+ <img src="avatar.jpg" class="avatar" />
133
+ <h2>John Doe</h2>
134
+ <p>Software Engineer</p>
135
+ </div>
136
+ </Shimmer>
137
+ ```
138
+
139
+ ---
140
+
141
+ ### Dynamic Content with `templateProps`
142
+
143
+ For components that receive dynamic data via props, use `templateProps` to provide mock data for skeleton generation:
144
+
145
+ **React**
146
+
147
+ ```tsx
148
+ import { Shimmer } from 'shimmer-from-structure';
149
+
150
+ // Your component that accepts props
151
+ const UserCard = ({ user }) => (
152
+ <div className="card">
153
+ <img src={user.avatar} className="avatar" />
154
+ <h2>{user.name}</h2>
155
+ <p>{user.role}</p>
156
+ </div>
157
+ );
158
+
159
+ // Template data for the skeleton
160
+ const userTemplate = {
161
+ name: 'Loading...',
162
+ role: 'Loading role...',
163
+ avatar: 'placeholder.jpg',
164
+ };
165
+
166
+ function App() {
167
+ const [loading, setLoading] = useState(true);
168
+ const [user, setUser] = useState(null);
169
+
170
+ return (
171
+ <Shimmer loading={loading} templateProps={{ user: userTemplate }}>
172
+ <UserCard user={user || userTemplate} />
173
+ </Shimmer>
174
+ );
175
+ }
176
+ ```
177
+
178
+ **Vue**
179
+
180
+ ```vue
181
+ <script setup>
182
+ import { ref } from 'vue';
183
+ import { Shimmer } from '@shimmer-from-structure/vue';
184
+ import UserCard from './UserCard.vue';
185
+
186
+ const loading = ref(true);
187
+ const userTemplate = {
188
+ name: 'Loading...',
189
+ role: 'Loading role...',
190
+ avatar: 'placeholder.jpg',
191
+ };
192
+ </script>
193
+
194
+ <template>
195
+ <Shimmer :loading="loading" :templateProps="{ user: userTemplate }">
196
+ <UserCard :user="user || userTemplate" />
197
+ </Shimmer>
198
+ </template>
199
+ ```
200
+
201
+ **Svelte**
202
+
203
+ ```svelte
204
+ <script>
205
+ import { Shimmer } from '@shimmer-from-structure/svelte';
206
+ import UserCard from './UserCard.svelte';
207
+
208
+ export let user;
209
+ let loading = true;
210
+ const userTemplate = {
211
+ name: 'Loading...',
212
+ role: 'Loading role...',
213
+ avatar: 'placeholder.jpg',
214
+ };
215
+ </script>
216
+
217
+ <Shimmer loading={loading} templateProps={{ user: userTemplate }}>
218
+ <UserCard user={user || userTemplate} />
219
+ </Shimmer>
220
+ ```
221
+
222
+ The `templateProps` object is spread onto the first child component when loading, allowing it to render with mock data for measurement.
223
+
224
+ ## 🎨 API Reference
225
+
226
+ ### `<Shimmer>` Props
227
+
228
+ | Prop | Type | Default | Description |
229
+ | ---------------------- | ------------------------- | -------------------------- | --------------------------------------------------------- |
230
+ | `loading` | `boolean` | `true` | Whether to show shimmer effect or actual content |
231
+ | `children` | `React.ReactNode` | required | The content to render/measure |
232
+ | `shimmerColor` | `string` | `'rgba(255,255,255,0.15)'` | Color of the shimmer wave |
233
+ | `backgroundColor` | `string` | `'rgba(255,255,255,0.08)'` | Background color of shimmer blocks |
234
+ | `duration` | `number` | `1.5` | Animation duration in seconds |
235
+ | `fallbackBorderRadius` | `number` | `4` | Border radius (px) for elements with no CSS border-radius |
236
+ | `templateProps` | `Record<string, unknown>` | - | Props to inject into first child for skeleton rendering |
237
+
238
+ ### Example with All Props
239
+
240
+ **React**
241
+
242
+ ```tsx
243
+ <Shimmer
244
+ loading={isLoading}
245
+ shimmerColor="rgba(255, 255, 255, 0.2)"
246
+ backgroundColor="rgba(255, 255, 255, 0.1)"
247
+ duration={2}
248
+ fallbackBorderRadius={8}
249
+ templateProps={{
250
+ user: userTemplate,
251
+ settings: settingsTemplate,
252
+ }}
253
+ >
254
+ <MyComponent user={user} settings={settings} />
255
+ </Shimmer>
256
+ ```
257
+
258
+ **Vue**
259
+
260
+ ```vue
261
+ <Shimmer
262
+ :loading="isLoading"
263
+ shimmerColor="rgba(255, 255, 255, 0.2)"
264
+ backgroundColor="rgba(255, 255, 255, 0.1)"
265
+ :duration="2"
266
+ :fallbackBorderRadius="8"
267
+ :templateProps="{
268
+ user: userTemplate,
269
+ settings: settingsTemplate,
270
+ }"
271
+ >
272
+ <MyComponent :user="user" :settings="settings" />
273
+ </Shimmer>
274
+ ```
275
+
276
+ **Svelte**
277
+
278
+ ```svelte
279
+ <Shimmer
280
+ loading={isLoading}
281
+ shimmerColor="rgba(255, 255, 255, 0.2)"
282
+ backgroundColor="rgba(255, 255, 255, 0.1)"
283
+ duration={2}
284
+ fallbackBorderRadius={8}
285
+ templateProps={{
286
+ user: userTemplate,
287
+ settings: settingsTemplate,
288
+ }}
289
+ >
290
+ <MyComponent {user} {settings} />
291
+ </Shimmer>
292
+ ```
293
+
294
+ ## 🔧 How It Works
295
+
296
+ 1. **Visible Container Rendering**: When `loading={true}`, your component renders with transparent text but **visible container backgrounds**
297
+ 2. **Template Props Injection**: If `templateProps` is provided, it's spread onto the first child so dynamic components can render
298
+ 3. **DOM Measurement**: Uses `useLayoutEffect` to synchronously measure all leaf elements via `getBoundingClientRect()`
299
+ 4. **Border Radius Detection**: Automatically captures each element's computed `border-radius` from CSS
300
+ 5. **Shimmer Generation**: Creates absolutely-positioned shimmer blocks matching measured dimensions
301
+ 6. **Animation**: Applies smooth gradient animation that sweeps across each block
302
+
303
+ ### Key Features
304
+
305
+ - **Container backgrounds visible**: Unlike `opacity: 0`, we use `color: transparent` so card backgrounds/borders show during loading
306
+ - **Auto border-radius**: Circular avatars get circular shimmer blocks automatically
307
+ - **Fallback radius**: Text elements (which have `border-radius: 0`) use `fallbackBorderRadius` to avoid sharp rectangles
308
+ - **Dark-mode friendly**: Default colors use semi-transparent whites that work on any background
309
+
310
+ ## Examples
311
+
312
+ ### Dashboard with Multiple Sections
313
+
314
+ Each section can have its own independent loading state:
315
+
316
+ **React**
317
+
318
+ ```tsx
319
+ function Dashboard() {
320
+ const [loadingUser, setLoadingUser] = useState(true);
321
+ const [loadingStats, setLoadingStats] = useState(true);
322
+
323
+ return (
324
+ <>
325
+ {/* User profile section */}
326
+ <Shimmer loading={loadingUser} templateProps={{ user: userTemplate }}>
327
+ <UserProfile user={user} />
328
+ </Shimmer>
329
+
330
+ {/* Stats section - with custom colors */}
331
+ <Shimmer
332
+ loading={loadingStats}
333
+ templateProps={{ stats: statsTemplate }}
334
+ shimmerColor="rgba(20, 184, 166, 0.2)"
335
+ >
336
+ <StatsGrid stats={stats} />
337
+ </Shimmer>
338
+ </>
339
+ );
340
+ }
341
+ ```
342
+
343
+ **Vue**
344
+
345
+ ```vue
346
+ <template>
347
+ <!-- User profile section -->
348
+ <Shimmer :loading="loadingUser" :templateProps="{ user: userTemplate }">
349
+ <UserProfile :user="user" />
350
+ </Shimmer>
351
+
352
+ <!-- Stats section - with custom colors -->
353
+ <Shimmer
354
+ :loading="loadingStats"
355
+ :templateProps="{ stats: statsTemplate }"
356
+ shimmerColor="rgba(20, 184, 166, 0.2)"
357
+ >
358
+ <StatsGrid :stats="stats" />
359
+ </Shimmer>
360
+ </template>
361
+ ```
362
+
363
+ **Svelte**
364
+
365
+ ```svelte
366
+ <Shimmer loading={loadingUser} templateProps={{ user: userTemplate }}>
367
+ <UserProfile {user} />
368
+ </Shimmer>
369
+
370
+ <Shimmer
371
+ loading={loadingStats}
372
+ templateProps={{ stats: statsTemplate }}
373
+ shimmerColor="rgba(20, 184, 166, 0.2)"
374
+ >
375
+ <StatsGrid {stats} />
376
+ </Shimmer>
377
+ ```
378
+
379
+ ### Transactions List
380
+
381
+ **React**
382
+
383
+ ```tsx
384
+ <Shimmer loading={loadingTransactions} templateProps={{ transactions: transactionsTemplate }}>
385
+ <TransactionsList transactions={transactions} />
386
+ </Shimmer>
387
+ ```
388
+
389
+ **Vue**
390
+
391
+ ```vue
392
+ <Shimmer :loading="loadingTransactions" :templateProps="{ transactions: transactionsTemplate }">
393
+ <TransactionsList :transactions="transactions" />
394
+ </Shimmer>
395
+ ```
396
+
397
+ **Svelte**
398
+
399
+ ```svelte
400
+ <Shimmer loading={loadingTransactions} templateProps={{ transactions: transactionsTemplate }}>
401
+ <TransactionsList {transactions} />
402
+ </Shimmer>
403
+ ```
404
+
405
+ ### Team Members Grid
406
+
407
+ **React**
408
+
409
+ ```tsx
410
+ <Shimmer loading={loadingTeam} templateProps={{ members: teamTemplate }}>
411
+ <TeamMembers members={team} />
412
+ </Shimmer>
413
+ ```
414
+
415
+ **Vue**
416
+
417
+ ```vue
418
+ <Shimmer :loading="loadingTeam" :templateProps="{ members: teamTemplate }">
419
+ <TeamMembers :members="team" />
420
+ </Shimmer>
421
+ ```
422
+
423
+ **Svelte**
424
+
425
+ ```svelte
426
+ <Shimmer loading={loadingTeam} templateProps={{ members: teamTemplate }}>
427
+ <TeamMembers members={team} />
428
+ </Shimmer>
429
+ ```
430
+
431
+ ## 🔄 Using with React Suspense
432
+
433
+ Shimmer works seamlessly as a Suspense fallback. When used this way, `loading` is always `true` because React automatically unmounts the fallback and replaces it with the resolved component.
434
+
435
+ ### Basic Suspense Pattern
436
+
437
+ ```tsx
438
+ import { Suspense, lazy } from 'react';
439
+ import { Shimmer } from 'shimmer-from-structure';
440
+
441
+ const UserProfile = lazy(() => import('./UserProfile'));
442
+
443
+ function App() {
444
+ return (
445
+ <Suspense
446
+ fallback={
447
+ <Shimmer loading={true} templateProps={{ user: userTemplate }}>
448
+ <UserProfile />
449
+ </Shimmer>
450
+ }
451
+ >
452
+ <UserProfile userId="123" />
453
+ </Suspense>
454
+ );
455
+ }
456
+ ```
457
+
458
+ ### Why `loading={true}` is Always Set
459
+
460
+ When using Shimmer as a Suspense fallback:
461
+
462
+ 1. **Suspend**: React renders the fallback → Shimmer shows with `loading={true}`
463
+ 2. **Resolve**: React **replaces** the entire fallback with the real component
464
+ 3. The Shimmer is **unmounted**, not updated — so you never need to toggle `loading`
465
+
466
+ ### Performance Tips for Suspense
467
+
468
+ **Memoize the fallback** to prevent re-renders:
469
+
470
+ ```tsx
471
+ const ShimmerFallback = React.memo(() => (
472
+ <Shimmer loading={true} templateProps={{ user: userTemplate }}>
473
+ <UserProfile />
474
+ </Shimmer>
475
+ ));
476
+
477
+ // Usage
478
+ <Suspense fallback={<ShimmerFallback />}>
479
+ <UserProfile userId="123" />
480
+ </Suspense>;
481
+ ```
482
+
483
+ **Keep templates lightweight** — the DOM is measured synchronously via `useLayoutEffect`, so avoid complex logic in your template.
484
+
485
+ ## Global Configuration
486
+
487
+ You can set default configuration for your entire app (or specific sections) using the context/provider pattern. This is perfect for maintaining consistent themes without repeating props.
488
+
489
+ ### React (Context API)
490
+
491
+ ```tsx
492
+ import { Shimmer, ShimmerProvider } from '@shimmer-from-structure/react';
493
+
494
+ function App() {
495
+ return (
496
+ // Set global defaults
497
+ <ShimmerProvider
498
+ config={{
499
+ shimmerColor: 'rgba(56, 189, 248, 0.4)', // Blue shimmer
500
+ backgroundColor: 'rgba(56, 189, 248, 0.1)', // Blue background
501
+ duration: 2.5,
502
+ fallbackBorderRadius: 8,
503
+ }}
504
+ >
505
+ <Dashboard />
506
+ </ShimmerProvider>
507
+ );
508
+ }
509
+ ```
510
+
511
+ ### Vue (Provide/Inject)
512
+
513
+ ```vue
514
+ <!-- App.vue -->
515
+ <script setup>
516
+ import { provideShimmerConfig } from '@shimmer-from-structure/vue';
517
+
518
+ provideShimmerConfig({
519
+ shimmerColor: 'rgba(56, 189, 248, 0.4)',
520
+ backgroundColor: 'rgba(56, 189, 248, 0.1)',
521
+ duration: 2.5,
522
+ fallbackBorderRadius: 8,
523
+ });
524
+ </script>
525
+
526
+ <template>
527
+ <router-view />
528
+ </template>
529
+ ```
530
+
531
+ ### Svelte (setShimmerConfig)
532
+
533
+ ```svelte
534
+ <!-- App.svelte or any parent component -->
535
+ <script>
536
+ import { setShimmerConfig } from '@shimmer-from-structure/svelte';
537
+
538
+ setShimmerConfig({
539
+ shimmerColor: 'rgba(56, 189, 248, 0.4)',
540
+ backgroundColor: 'rgba(56, 189, 248, 0.1)',
541
+ duration: 2.5,
542
+ fallbackBorderRadius: 8,
543
+ });
544
+ </script>
545
+
546
+ <Dashboard />
547
+ ```
548
+
549
+ ---
550
+
551
+ Components inside the provider automatically inherit values. You can still override them locally:
552
+
553
+ **React**
554
+
555
+ ```tsx
556
+ // Inherits blue theme from provider
557
+ <Shimmer loading={true}><UserCard /></Shimmer>
558
+
559
+ // Overrides provider settings
560
+ <Shimmer loading={true} duration={0.5}><FastCard /></Shimmer>
561
+ ```
562
+
563
+ **Vue**
564
+
565
+ ```vue
566
+ <!-- Inherits blue theme from provider -->
567
+ <Shimmer :loading="true"><UserCard /></Shimmer>
568
+
569
+ <!-- Overrides provider settings -->
570
+ <Shimmer :loading="true" :duration="0.5"><FastCard /></Shimmer>
571
+ ```
572
+
573
+ **Svelte**
574
+
575
+ ```svelte
576
+ <!-- Inherits blue theme from provider -->
577
+ <Shimmer loading={true}><UserCard /></Shimmer>
578
+
579
+ <!-- Overrides provider settings -->
580
+ <Shimmer loading={true} duration={0.5}><FastCard /></Shimmer>
581
+ ```
582
+
583
+ ### Accessing Config in Hooks/Composables
584
+
585
+ If you need to access the current configuration in your own components:
586
+
587
+ **React**
588
+
589
+ ```tsx
590
+ import { useShimmerConfig } from 'shimmer-from-structure';
591
+
592
+ function MyComponent() {
593
+ const config = useShimmerConfig();
594
+ return <div style={{ background: config.backgroundColor }}>...</div>;
595
+ }
596
+ ```
597
+
598
+ **Vue**
599
+
600
+ ```javascript
601
+ import { useShimmerConfig } from '@shimmer-from-structure/vue';
602
+
603
+ const config = useShimmerConfig();
604
+ console.log(config.value.backgroundColor);
605
+ ```
606
+
607
+ **Svelte**
608
+
609
+ ```javascript
610
+ import { getShimmerConfig } from '@shimmer-from-structure/svelte';
611
+
612
+ const config = getShimmerConfig();
613
+ console.log(config.backgroundColor);
614
+ ```
615
+
616
+ ## Best Practices
617
+
618
+ ### 1. Use `templateProps` for Dynamic Data
619
+
620
+ When your component receives data via props, always provide `templateProps` with mock data that matches the expected structure.
621
+
622
+ ### 2. Match Template Structure to Real Data
623
+
624
+ Ensure your template data has the same array length and property structure as real data for accurate shimmer layout.
625
+
626
+ ### 3. Use Individual Shimmer Components
627
+
628
+ Wrap each section in its own Shimmer for independent loading states:
629
+
630
+ ```tsx
631
+ // ✅ Good - independent loading
632
+ <Shimmer loading={loadingUsers}><UserList /></Shimmer>
633
+ <Shimmer loading={loadingPosts}><PostList /></Shimmer>
634
+
635
+ // ❌ Avoid - all-or-nothing loading
636
+ <Shimmer loading={loadingUsers || loadingPosts}>
637
+ <UserList />
638
+ <PostList />
639
+ </Shimmer>
640
+ ```
641
+
642
+ ### 4. Consider Element Widths
643
+
644
+ Block elements like `<h1>`, `<p>` take full container width. If you want shimmer to match text width:
645
+
646
+ ```css
647
+ .title {
648
+ width: fit-content;
649
+ }
650
+ ```
651
+
652
+ ### 5. Provide Container Dimensions
653
+
654
+ For async components (like charts), ensure containers have explicit dimensions so shimmer has something to measure.
655
+
656
+ ## ⚡ Performance Considerations
657
+
658
+ - Measurement happens only when `loading` changes to `true`
659
+ - Uses `useLayoutEffect` for synchronous measurement (no flicker)
660
+ - Minimal re-renders - only updates when loading state or children change
661
+ - Lightweight DOM measurements using native browser APIs
662
+
663
+ - Lightweight DOM measurements using native browser APIs
664
+
665
+ ## 🛠️ Development
666
+
667
+ This is a monorepo managed with npm workspaces. Each package can be built independently:
668
+
669
+ ```bash
670
+ # Install dependencies
671
+ npm install
672
+
673
+ # Build all packages
674
+ npm run build
675
+
676
+ # Build individual packages
677
+ npm run build:core
678
+ npm run build:react
679
+ npm run build:vue
680
+ npm run build:svelte
681
+ npm run build:main
682
+
683
+ # Run tests
684
+ npm test
685
+ ```
686
+
687
+ ## 📝 License
688
+
689
+ MIT
690
+
691
+ ## 🤝 Contributing
692
+
693
+ Contributions are welcome! Please feel free to submit a Pull Request.
694
+
695
+ ## 🐛 Known Limitations
696
+
697
+ - **Async components**: Components that render asynchronously (like charts using `ResponsiveContainer`) may need explicit container dimensions
698
+ - **Zero-dimension elements**: Elements with `display: none` or zero dimensions won't be captured
699
+ - **SVG internals**: Only the outer `<svg>` element is captured, not internal paths/shapes
700
+
701
+ ## 🏗️ Monorepo Structure
702
+
703
+ This library is organized as a monorepo with four packages:
704
+
705
+ | Package | Description | Size |
706
+ | -------------------------------- | ------------------------------------------- | -------- |
707
+ | `@shimmer-from-structure/core` | Framework-agnostic DOM utilities | 1.44 kB |
708
+ | `@shimmer-from-structure/react` | React adapter | 12.84 kB |
709
+ | `@shimmer-from-structure/vue` | Vue 3 adapter | 3.89 kB |
710
+ | `@shimmer-from-structure/svelte` | Svelte adapter | 4.60 kB |
711
+ | `shimmer-from-structure` | Main package (React backward compatibility) | 0.93 kB |
712
+
713
+ The core package contains all DOM measurement logic, while React and Vue packages are thin wrappers that provide framework-specific APIs.
714
+
715
+ ## 🚧 Roadmap
716
+
717
+ - [x] Dynamic data support via `templateProps`
718
+ - [x] Auto border-radius detection
719
+ - [x] Container background visibility
720
+ - [x] **Vue.js adapter**
721
+ - [x] **Svelte adapter**
722
+ - [ ] Better async component support
723
+ - [ ] Customizable shimmer direction (vertical, diagonal)
724
+ - [ ] React Native support
725
+
726
+ ---
727
+
728
+ Made with ❤️ for developers tired of maintaining skeleton screens