rune-scroller 2.2.1 → 2.2.2

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.
Files changed (2) hide show
  1. package/README.md +61 -417
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,4 @@
1
- # ⚡ Rune Scroller - Full Reference
2
-
3
- **📚 Complete API Reference** — Detailed documentation for all features and options.
4
-
5
- ---
1
+ # ⚡ Rune Scroller
6
2
 
7
3
  <div align="center">
8
4
  <img src="./logo.png" alt="Rune Scroller Logo" width="200" />
@@ -26,177 +22,20 @@
26
22
 
27
23
  ## ✨ Features
28
24
 
29
- - **14KB gzipped** (47KB uncompressed) - Minimal overhead, optimized for production
25
+ - **5.7KB gzipped** (JS+CSS) - Minimal overhead
30
26
  - **Zero dependencies** - Pure Svelte 5 + IntersectionObserver
31
- - **14 animations** - Fade, Zoom, Flip, Slide, Bounce variants
32
- - **Full TypeScript support** - Type definitions generated from JSDoc
27
+ - **14 animations** - Fade, Zoom, Flip, Slide, Bounce
28
+ - **TypeScript support** - Full type definitions
33
29
  - **SSR-ready** - SvelteKit compatible
34
30
  - **GPU-accelerated** - Pure CSS transforms
35
31
  - **Accessible** - Respects `prefers-reduced-motion`
36
- - **v2.0.0 New** - `onVisible` callback, ResizeObserver support, animation validation, sentinel customization
37
- - **✨ Latest** - `useIntersection` migrated to Svelte 5 `$effect` rune for better lifecycle management
38
- - **🚀 Bundle optimized** - CSS with custom properties, production build minification
39
- - **🚀 v2.2.0** - Cache CSS check to eliminate 99% of reflows
40
32
 
41
33
  ---
42
-
43
- ## 🚀 Performance
44
-
45
- ### Cache CSS Validation (v2.2.0)
46
-
47
- **Problem:**
48
- - `checkAndWarnIfCSSNotLoaded()` was called for EVERY element
49
- - Each call did:
50
- - `document.createElement('div')`
51
- - `document.body.appendChild(test)`
52
- - `getComputedStyle(test)` ⚠️ **Expensive!** Forces full page reflow
53
- - `test.remove()`
54
- - For 100 animated elements = **100 reflows + 100 DOM operations**
55
-
56
- **Solution:**
57
- ```javascript
58
- // Cache to check only once per page load
59
- let cssCheckResult = null;
60
-
61
- export function checkAndWarnIfCSSNotLoaded() {
62
- if (cssCheckResult !== null) return cssCheckResult;
63
- // ... expensive check ...
64
- cssCheckResult = hasAnimation;
65
- return hasAnimation;
66
- }
67
- ```
68
-
69
- **Impact:**
70
- - Validation runs ONLY ONCE per page load
71
- - Eliminates layout thrashing from repeated `getComputedStyle()` calls
72
- - For 100 elements: **99 fewer reflows** (100 → 1)
73
- - Zero memory overhead (single boolean)
74
-
75
- ### Current Performance Metrics
76
-
77
- | Metric | Value |
78
- |---------|--------|
79
- | **Bundle size** | 12.4KB (compressed), 40.3KB (unpacked) |
80
- | **Initialization** | ~1-2ms per element |
81
- | **Observer callback** | <0.5ms per frame |
82
- | **CSS validation** | ~0.1ms total (v2.2.0, with cache) |
83
- | **Memory per observer** | ~1.2KB |
84
- | **Animation performance** | 60fps maintained |
85
- | **Memory leaks** | 0 detected |
86
-
87
- ### Why Performance Matters
88
-
89
- **Layout Thrashing:**
90
- - Synchronous reflows block the main thread
91
- - Each reflow can take 10-20ms
92
- - For 100 elements = **1-2 seconds blocked**
93
- - User sees stuttering/jank while scrolling
94
-
95
- **Solution:**
96
- - Cache = 1 reflow instead of N
97
- - 99% improvement on pages with many animations
98
- - Smoother scrolling, better UX
99
-
100
- ### Optimized Code Patterns
101
-
102
- **IntersectionObserver:**
103
- - Native API (no scroll listeners)
104
- - Fast callback (<0.5ms per frame)
105
- - No debounce needed (browser handles this efficiently)
106
-
107
- **CSS Animations:**
108
- - Transforms only (GPU-accelerated)
109
- - No layout/repaint during animation
110
- - `will-change` on visible elements only
111
-
112
- **DOM Operations:**
113
- - `insertAdjacentElement('beforebegin')` instead of `insertBefore`
114
- - `offsetHeight` instead of `getBoundingClientRect()` (avoids transform issues)
115
- - Complete cleanup on destroy
116
-
117
- **Memory Management:**
118
- - All observers disconnected
119
- - Sentinel and wrapper removed
120
- - State prevents double-disconnects
121
- - 0 memory leaks detected (121/121 tests)
122
-
123
- ### Future Considerations
124
-
125
- **1. `will-change` Timing**
126
- - Currently: `.is-visible { will-change: transform, opacity; }`
127
- - Trade-off: Stays active after animation (consumes GPU memory)
128
- - Consideration: Use `transitionend` event to remove `will-change`
129
- - Recommendation: Keep current (GPU memory is cheap)
130
-
131
- **2. Threshold Tuning**
132
- - Current: `threshold: 0` (triggers as soon as 1px is visible)
133
- - Alternative: `threshold: 0.1` or `threshold: 0.25`
134
- - Trade-off: Higher threshold = later trigger = smoother stagger
135
- - Recommendation: Keep `threshold: 0` for immediate feedback
136
-
137
- **3. requestIdleCallback**
138
- - Potential: Defer non-critical setup to browser idle time
139
- - Trade-off: Complex to implement, marginal benefit
140
- - Recommendation: Not needed (current performance is excellent)
141
-
142
- **4. Testing on Low-End Devices**
143
- - Test on mobile phones, older browsers
144
- - Use DevTools CPU throttling
145
- - Consider Lighthouse/Puppeteer for automated testing
146
- - Ensure 60fps maintained on real devices
147
-
148
- ### What NOT to Optimize
149
-
150
- **Anti-patterns to avoid:**
151
-
152
- 1. ❌ **Premature optimization**
153
- - Don't optimize without measurements
154
- - Profile first, optimize later
155
- - "Premature optimization is the root of all evil"
156
-
157
- 2. ❌ **Over-engineering**
158
- - Complex solutions for small gains
159
- - Keep it simple when possible
160
- - Don't sacrifice readability for micro-optimizations
161
-
162
- 3. ❌ **Breaking performance for size**
163
- - Bundle size matters (12.4KB is excellent)
164
- - Don't add huge dependencies for minor improvements
165
-
166
- 4. ❌ **Optimizing unused paths**
167
- - Focus on hot paths (element creation, scroll, intersection)
168
- - Cold paths (initialization, destroy) less critical
169
-
170
- 5. ❌ **Sacrificing maintainability**
171
- - Don't sacrifice code clarity for micro-optimizations
172
- - Comments should explain WHY, not just WHAT
173
- - Keep code simple and understandable
174
-
175
- ### Performance Testing
176
-
177
- **Recommended approach:**
178
- 1. Create a benchmark with 100-1000 animated elements
179
- 2. Measure: initialization, first animation, scroll performance, cleanup
180
- 3. Profile with DevTools Performance tab
181
- 4. Test on real pages (not just benchmarks)
182
- 5. Verify 60fps is maintained during scroll
183
-
184
- **Tools:**
185
- - Chrome DevTools Performance tab
186
- - Firefox Performance Profiler
187
- - Web Inspector (Safari)
188
- - Lighthouse (PageSpeed, accessibility, best practices)
189
34
 
190
- ---
191
-
192
35
  ## 📦 Installation
193
36
 
194
37
  ```bash
195
38
  npm install rune-scroller
196
- # or
197
- pnpm add rune-scroller
198
- # or
199
- yarn add rune-scroller
200
39
  ```
201
40
 
202
41
  ---
@@ -224,51 +63,12 @@ yarn add rune-scroller
224
63
  </div>
225
64
  ```
226
65
 
227
- That's it! The CSS animations are included automatically when you import rune-scroller.
228
-
229
- ### Option 2: Manual CSS Import
230
-
231
- For fine-grained control, import CSS manually:
232
-
233
- **Step 1: Import CSS in your root layout (recommended for SvelteKit):**
234
-
235
- ```svelte
236
- <!-- src/routes/+layout.svelte -->
237
- <script>
238
- import 'rune-scroller/animations.css';
239
- let { children } = $props();
240
- </script>
241
-
242
- {@render children()}
243
- ```
244
-
245
- **Or import in each component:**
246
-
247
- ```svelte
248
- <script>
249
- import runeScroller from 'rune-scroller';
250
- import 'rune-scroller/animations.css';
251
- </script>
252
- ```
253
-
254
- **Step 2: Use the animations**
255
-
256
- ```svelte
257
- <script>
258
- import runeScroller from 'rune-scroller';
259
- // CSS already imported in layout or above
260
- </script>
261
-
262
- <div use:runeScroller={{ animation: 'fade-in' }}>
263
- Animated content
264
- </div>
265
- ```
266
-
267
66
  ---
268
67
 
269
68
  ## 🎨 Available Animations
270
69
 
271
70
  ### Fade (5)
71
+
272
72
  - `fade-in` - Simple opacity fade
273
73
  - `fade-in-up` - Fade + move up 300px
274
74
  - `fade-in-down` - Fade + move down 300px
@@ -276,6 +76,7 @@ For fine-grained control, import CSS manually:
276
76
  - `fade-in-right` - Fade + move from left 300px
277
77
 
278
78
  ### Zoom (5)
79
+
279
80
  - `zoom-in` - Scale from 0.3 to 1
280
81
  - `zoom-out` - Scale from 2 to 1
281
82
  - `zoom-in-up` - Zoom (0.5→1) + move up 300px
@@ -283,6 +84,7 @@ For fine-grained control, import CSS manually:
283
84
  - `zoom-in-right` - Zoom (0.5→1) + move from left 300px
284
85
 
285
86
  ### Others (4)
87
+
286
88
  - `flip` - 3D flip on Y-axis
287
89
  - `flip-x` - 3D flip on X-axis
288
90
  - `slide-rotate` - Slide + rotate 10°
@@ -294,114 +96,45 @@ For fine-grained control, import CSS manually:
294
96
 
295
97
  ```typescript
296
98
  interface RuneScrollerOptions {
297
- animation?: AnimationType; // Animation name (default: 'fade-in')
298
- duration?: number; // Duration in ms (default: 800)
299
- repeat?: boolean; // Repeat on scroll (default: false)
300
- debug?: boolean; // Show sentinel as visible line (default: false)
301
- offset?: number; // Sentinel offset in px (default: 0, negative = above)
302
- onVisible?: (element: HTMLElement) => void; // Callback when animation triggers (v2.0.0+)
303
- sentinelColor?: string; // Sentinel debug color, e.g. '#ff6b6b' (v2.0.0+)
304
- sentinelId?: string; // Custom ID for sentinel identification (v2.0.0+)
99
+ animation?: AnimationType // Animation name (default: 'fade-in')
100
+ duration?: number // Duration in ms (default: 800)
101
+ repeat?: boolean // Repeat on scroll (default: false)
102
+ debug?: boolean // Show sentinel as visible line (default: false)
103
+ offset?: number // Sentinel offset in px (default: 0, negative = earlier)
104
+ onVisible?: (element: HTMLElement) => void // Callback when visible
105
+ sentinelColor?: string // Debug sentinel color (e.g. '#ff6b6b')
106
+ sentinelId?: string // Custom sentinel ID
305
107
  }
306
108
  ```
307
109
 
308
- ### Option Details
309
-
310
- - **`animation`** - Type of animation to play. Choose from 14 built-in animations listed above. Invalid animations automatically fallback to 'fade-in' with a console warning.
311
- - **`duration`** - How long the animation lasts in milliseconds (default: 800ms).
312
- - **`repeat`** - If `true`, animation plays every time sentinel enters viewport. If `false`, plays only once.
313
- - **`debug`** - If `true`, displays the sentinel element as a visible line below your element. Useful for seeing exactly when animations trigger. Default color is cyan (#00e0ff), customize with `sentinelColor`.
314
- - **`offset`** - Offset of the sentinel in pixels. Positive values move sentinel down (delays animation), negative values move it up (triggers earlier). Useful for large elements where you want animation to trigger before the entire element is visible.
315
- - **`onVisible`** *(v2.0.0+)* - Callback function triggered when the animation becomes visible. Receives the animated element as parameter. Useful for analytics, lazy loading, or triggering custom effects.
316
- - **`sentinelColor`** *(v2.0.0+)* - Customize the debug sentinel color (e.g., '#ff6b6b' for red). Only visible when `debug: true`. Useful for distinguishing multiple sentinels on the same page.
317
- - **`sentinelId`** *(v2.0.0+)* - Set a custom ID for the sentinel element. If not provided, an auto-ID is generated (`sentinel-1`, `sentinel-2`, etc.). Useful for identifying sentinels in DevTools and tracking which element owns which sentinel.
318
-
319
110
  ### Examples
320
111
 
321
112
  ```svelte
322
113
  <!-- Basic -->
323
- <div use:runeScroller={{ animation: 'zoom-in' }}>
324
- Content
325
- </div>
114
+ <div use:runeScroller={{ animation: 'zoom-in' }}>Content</div>
326
115
 
327
116
  <!-- Custom duration -->
328
- <div use:runeScroller={{ animation: 'fade-in-up', duration: 1000 }}>
329
- Fast animation
330
- </div>
117
+ <div use:runeScroller={{ animation: 'fade-in-up', duration: 1000 }}>Fast</div>
331
118
 
332
119
  <!-- Repeat mode -->
333
- <div use:runeScroller={{ animation: 'bounce-in', repeat: true }}>
334
- Repeats every time you scroll
335
- </div>
120
+ <div use:runeScroller={{ animation: 'bounce-in', repeat: true }}>Repeats</div>
336
121
 
337
- <!-- Debug mode - shows cyan line marking sentinel position -->
338
- <div use:runeScroller={{ animation: 'fade-in', debug: true }}>
339
- The cyan line below this shows when animation will trigger
340
- </div>
122
+ <!-- Debug mode -->
123
+ <div use:runeScroller={{ animation: 'fade-in', debug: true }}>Debug</div>
341
124
 
342
- <!-- Multiple options -->
343
- <div use:runeScroller={{
344
- animation: 'fade-in-up',
345
- duration: 1200,
346
- repeat: true,
347
- debug: true
348
- }}>
349
- Full featured example
125
+ <!-- Trigger earlier with negative offset -->
126
+ <div use:runeScroller={{ animation: 'fade-in-up', offset: -200 }}>
127
+ Triggers 200px before element bottom
350
128
  </div>
351
129
 
352
- <!-- Large element - trigger animation earlier with negative offset -->
353
- <div use:runeScroller={{
354
- animation: 'fade-in-up',
355
- offset: -200 // Trigger 200px before element bottom
356
- }}>
357
- Large content that needs early triggering
358
- </div>
359
-
360
- <!-- Delay animation by moving sentinel down -->
361
- <div use:runeScroller={{
362
- animation: 'zoom-in',
363
- offset: 300 // Trigger 300px after element bottom
364
- }}>
365
- Content with delayed animation
366
- </div>
367
-
368
- <!-- v2.0.0: onVisible callback for analytics tracking -->
130
+ <!-- onVisible callback for analytics -->
369
131
  <div use:runeScroller={{
370
132
  animation: 'fade-in-up',
371
133
  onVisible: (el) => {
372
- console.log('Animation visible!', el);
373
- // Track analytics, load images, trigger API calls, etc.
374
- window.gtag?.('event', 'animation_visible', { element: el.id });
134
+ window.gtag?.('event', 'section_viewed', { id: el.id });
375
135
  }
376
136
  }}>
377
- Tracked animation
378
- </div>
379
-
380
- <!-- v2.0.0: Custom sentinel color for debugging -->
381
- <div use:runeScroller={{
382
- animation: 'fade-in',
383
- debug: true,
384
- sentinelColor: '#ff6b6b' // Red instead of default cyan
385
- }}>
386
- Red debug sentinel
387
- </div>
388
-
389
- <!-- v2.0.0: Custom sentinel ID for identification -->
390
- <div use:runeScroller={{
391
- animation: 'zoom-in',
392
- sentinelId: 'hero-zoom',
393
- debug: true
394
- }}>
395
- Identified sentinel (shows "hero-zoom" in debug mode)
396
- </div>
397
-
398
- <!-- v2.0.0: Auto-ID (sentinel-1, sentinel-2, etc) -->
399
- <div use:runeScroller={{
400
- animation: 'fade-in-up',
401
- debug: true
402
- // sentinelId omitted → auto generates "sentinel-1", "sentinel-2", etc
403
- }}>
404
- Auto-identified sentinel
137
+ Tracked section
405
138
  </div>
406
139
  ```
407
140
 
@@ -409,31 +142,25 @@ interface RuneScrollerOptions {
409
142
 
410
143
  ## 🎯 How It Works
411
144
 
412
- Rune Scroller uses **sentinel-based triggering**:
145
+ **Sentinel-based triggering:**
413
146
 
414
- 1. An invisible 1px sentinel element is created below your element
415
- 2. When the sentinel enters the viewport, animation triggers
416
- 3. This ensures precise timing regardless of element size
417
- 4. Uses native IntersectionObserver for performance
418
- 5. Pure CSS animations (GPU-accelerated)
419
- 6. *(v2.0.0)* Sentinel automatically repositions on element resize via ResizeObserver
147
+ 1. Invisible 1px sentinel created below your element
148
+ 2. When sentinel enters viewport, animation triggers
149
+ 3. Uses native IntersectionObserver for performance
150
+ 4. Pure CSS animations (GPU-accelerated)
151
+ 5. ResizeObserver auto-repositions sentinel
420
152
 
421
153
  **Why sentinels?**
154
+
422
155
  - Accurate timing across all screen sizes
423
156
  - No complex offset calculations
424
- - Handles staggered animations naturally
425
- - Sentinel stays fixed while element animates (no observer confusion with transforms)
426
-
427
- **Automatic ResizeObserver** *(v2.0.0+)*
428
- - Sentinel repositions automatically when element resizes
429
- - Works with responsive layouts and dynamic content
430
- - No configuration needed—it just works
157
+ - Works with animated elements (transforms don't affect observer)
431
158
 
432
159
  ---
433
160
 
434
161
  ## 🌐 SSR Compatibility
435
162
 
436
- Works seamlessly with SvelteKit. Simply import rune-scroller in your root layout:
163
+ Works seamlessly with SvelteKit:
437
164
 
438
165
  ```svelte
439
166
  <!-- src/routes/+layout.svelte -->
@@ -445,22 +172,6 @@ Works seamlessly with SvelteKit. Simply import rune-scroller in your root layout
445
172
  {@render children()}
446
173
  ```
447
174
 
448
- Then use animations anywhere in your app:
449
-
450
- ```svelte
451
- <!-- src/routes/+page.svelte -->
452
- <script>
453
- import runeScroller from 'rune-scroller';
454
- </script>
455
-
456
- <!-- No special handling needed -->
457
- <div use:runeScroller={{ animation: 'fade-in-up' }}>
458
- Works in SvelteKit SSR!
459
- </div>
460
- ```
461
-
462
- The library checks for browser environment and gracefully handles server-side rendering.
463
-
464
175
  ---
465
176
 
466
177
  ## ♿ Accessibility
@@ -468,73 +179,32 @@ The library checks for browser environment and gracefully handles server-side re
468
179
  Respects `prefers-reduced-motion`:
469
180
 
470
181
  ```css
471
- /* In animations.css */
472
182
  @media (prefers-reduced-motion: reduce) {
473
- .scroll-animate {
474
- animation: none !important;
475
- opacity: 1 !important;
476
- transform: none !important;
477
- }
183
+ .scroll-animate {
184
+ animation: none !important;
185
+ opacity: 1 !important;
186
+ transform: none !important;
187
+ }
478
188
  }
479
189
  ```
480
190
 
481
- Users who prefer reduced motion will see content without animations.
482
-
483
191
  ---
484
192
 
485
193
  ## 📚 API Reference
486
- ### Public API
487
-
488
- Rune Scroller exports a **single action-based API** (no components):
489
-
490
- 1. **`runeScroller`** (default) - Sentinel-based, simple, powerful
491
-
492
- **Why actions instead of components?**
493
- - Actions are lightweight directives
494
- - No DOM wrapper overhead
495
- - Better performance
496
- - More flexible
497
-
498
- ### Main Export
499
194
 
500
195
  ```typescript
501
- // CSS is automatically included
502
- import runeScroller from 'rune-scroller';
196
+ // Default export
197
+ import runeScroller from "rune-scroller"
503
198
 
504
199
  // Named exports
505
200
  import {
506
- useIntersection, // Composable
507
- useIntersectionOnce, // Composable
508
- calculateRootMargin // Utility
509
- } from 'rune-scroller';
201
+ useIntersection, // Composable
202
+ useIntersectionOnce, // Composable
203
+ calculateRootMargin, // Utility
204
+ } from "rune-scroller"
510
205
 
511
206
  // Types
512
- import type {
513
- AnimationType,
514
- RuneScrollerOptions,
515
- IntersectionOptions,
516
- UseIntersectionReturn
517
- } from 'rune-scroller';
518
- ```
519
-
520
- ### TypeScript Types
521
-
522
- ```typescript
523
- type AnimationType =
524
- | 'fade-in' | 'fade-in-up' | 'fade-in-down' | 'fade-in-left' | 'fade-in-right'
525
- | 'zoom-in' | 'zoom-out' | 'zoom-in-up' | 'zoom-in-left' | 'zoom-in-right'
526
- | 'flip' | 'flip-x' | 'slide-rotate' | 'bounce-in';
527
-
528
- interface RuneScrollerOptions {
529
- animation?: AnimationType;
530
- duration?: number;
531
- repeat?: boolean;
532
- debug?: boolean;
533
- offset?: number;
534
- onVisible?: (element: HTMLElement) => void; // v2.0.0+
535
- sentinelColor?: string; // v2.0.0+
536
- sentinelId?: string; // v2.0.0+
537
- }
207
+ import type { AnimationType, RuneScrollerOptions } from "rune-scroller"
538
208
  ```
539
209
 
540
210
  ---
@@ -546,45 +216,33 @@ interface RuneScrollerOptions {
546
216
  ```svelte
547
217
  <script>
548
218
  import runeScroller from 'rune-scroller';
549
-
550
- const items = [
551
- { title: 'Feature 1', description: 'Description 1' },
552
- { title: 'Feature 2', description: 'Description 2' },
553
- { title: 'Feature 3', description: 'Description 3' }
554
- ];
219
+ const items = ['Item 1', 'Item 2', 'Item 3'];
555
220
  </script>
556
221
 
557
- <div class="grid">
558
- {#each items as item}
559
- <div use:runeScroller={{ animation: 'fade-in-up', duration: 800 }}>
560
- <h3>{item.title}</h3>
561
- <p>{item.description}</p>
562
- </div>
563
- {/each}
564
- </div>
222
+ {#each items as item, i}
223
+ <div use:runeScroller={{
224
+ animation: 'fade-in-up',
225
+ duration: 800,
226
+ style: `--delay: ${i * 100}ms`
227
+ }}>
228
+ {item}
229
+ </div>
230
+ {/each}
565
231
  ```
566
232
 
567
233
  ### Hero Section
568
234
 
569
235
  ```svelte
570
- <div use:runeScroller={{ animation: 'fade-in-down', duration: 1000 }}>
571
- <h1>Welcome</h1>
572
- </div>
573
-
574
- <div use:runeScroller={{ animation: 'fade-in-up', duration: 1200 }}>
575
- <p>Engaging content</p>
576
- </div>
577
-
578
- <div use:runeScroller={{ animation: 'zoom-in', duration: 1000 }}>
579
- <button>Get Started</button>
580
- </div>
236
+ <h1 use:runeScroller={{ animation: 'fade-in-down', duration: 1000 }}>Welcome</h1>
237
+ <p use:runeScroller={{ animation: 'fade-in-up', duration: 1200 }}>Subtitle</p>
238
+ <button use:runeScroller={{ animation: 'zoom-in', duration: 800 }}>Get Started</button>
581
239
  ```
582
240
 
583
241
  ---
584
242
 
585
243
  ## 🔗 Links
586
244
 
587
- - **npm Package**: [rune-scroller](https://www.npmjs.com/package/rune-scroller)
245
+ - **npm**: [rune-scroller](https://www.npmjs.com/package/rune-scroller)
588
246
  - **GitHub**: [lelabdev/rune-scroller](https://github.com/lelabdev/rune-scroller)
589
247
  - **Changelog**: [CHANGELOG.md](https://github.com/lelabdev/rune-scroller/blob/main/lib/CHANGELOG.md)
590
248
 
@@ -596,18 +254,4 @@ MIT © [ludoloops](https://github.com/ludoloops)
596
254
 
597
255
  ---
598
256
 
599
- ## 🤝 Contributing
600
-
601
- Contributions welcome! Please open an issue or PR on GitHub.
602
-
603
- ```bash
604
- # Development
605
- bun install
606
- bun run dev
607
- bun test
608
- bun run build
609
- ```
610
-
611
- ---
612
-
613
257
  Made with ❤️ by [LeLab.dev](https://lelab.dev)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rune-scroller",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "Lightweight, high-performance scroll animations for Svelte 5. 14KB gzipped, zero dependencies.",
5
5
  "type": "module",
6
6
  "sideEffects": false,