{"id":36499815,"url":"https://github.com/lelabdev/rune-scroller","last_synced_at":"2026-01-16T18:29:47.611Z","repository":{"id":321612410,"uuid":"1086411950","full_name":"lelabdev/rune-scroller","owner":"lelabdev","description":"Lightweight, high-performance scroll animations for Svelte 5","archived":false,"fork":false,"pushed_at":"2026-01-06T18:10:33.000Z","size":814,"stargazers_count":26,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-12T04:50:54.245Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lelabdev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-30T11:37:46.000Z","updated_at":"2026-01-06T18:10:36.000Z","dependencies_parsed_at":null,"dependency_job_id":"9a114c27-ef0d-41ba-a278-6cc7d190c079","html_url":"https://github.com/lelabdev/rune-scroller","commit_stats":null,"previous_names":["lelabdev/rune-scroller"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/lelabdev/rune-scroller","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Frune-scroller","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Frune-scroller/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Frune-scroller/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Frune-scroller/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lelabdev","download_url":"https://codeload.github.com/lelabdev/rune-scroller/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Frune-scroller/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28480830,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-12T02:18:50.757Z","updated_at":"2026-01-16T18:29:47.584Z","avatar_url":"https://github.com/lelabdev.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ⚡ Rune Scroller - Full Reference\n\n**📚 Complete API Reference** — Detailed documentation for all features and options.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\t\u003cimg src=\"./logo.png\" alt=\"Rune Scroller Logo\" width=\"200\" /\u003e\n\u003c/div\u003e\n\n**Lightweight scroll animations for Svelte 5** — Built with Svelte 5 Runes and IntersectionObserver API.\n\n\u003e 🚀 **Open Source** by [ludoloops](https://github.com/ludoloops) at [LeLab.dev](https://lelab.dev)\n\u003e 📜 Licensed under **MIT**\n\n\u003cdiv align=\"center\"\u003e\n\t\u003ca href=\"https://bundlephobia.com/package/rune-scroller\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/bundlephobia/minzip/rune-scroller\" alt=\"minzipped size\" /\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://bundlephobia.com/package/rune-scroller\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/bundlephobia/min/rune-scroller\" alt=\"minified size\" /\u003e\n\t\u003c/a\u003e\n\u003c/div\u003e\n\n---\n\n## ✨ Features\n\n- **14KB gzipped** (47KB uncompressed) - Minimal overhead, optimized for production\n- **Zero dependencies** - Pure Svelte 5 + IntersectionObserver\n- **14 animations** - Fade, Zoom, Flip, Slide, Bounce variants\n- **Full TypeScript support** - Type definitions generated from JSDoc\n- **SSR-ready** - SvelteKit compatible\n- **GPU-accelerated** - Pure CSS transforms\n- **Accessible** - Respects `prefers-reduced-motion`\n- **v2.0.0 New** - `onVisible` callback, ResizeObserver support, animation validation, sentinel customization\n- **✨ Latest** - `useIntersection` migrated to Svelte 5 `$effect` rune for better lifecycle management\n- **🚀 Bundle optimized** - CSS with custom properties, production build minification\n- **🚀 v2.2.0** - Cache CSS check to eliminate 99% of reflows\n\n---\n \n## 🚀 Performance\n\n### Cache CSS Validation (v2.2.0)\n\n**Problem:**\n- `checkAndWarnIfCSSNotLoaded()` was called for EVERY element\n- Each call did:\n  - `document.createElement('div')`\n  - `document.body.appendChild(test)`\n  - `getComputedStyle(test)` ⚠️ **Expensive!** Forces full page reflow\n  - `test.remove()`\n- For 100 animated elements = **100 reflows + 100 DOM operations**\n\n**Solution:**\n```javascript\n// Cache to check only once per page load\nlet cssCheckResult = null;\n\nexport function checkAndWarnIfCSSNotLoaded() {\n  if (cssCheckResult !== null) return cssCheckResult;\n  // ... expensive check ...\n  cssCheckResult = hasAnimation;\n  return hasAnimation;\n}\n```\n\n**Impact:**\n- Validation runs ONLY ONCE per page load\n- Eliminates layout thrashing from repeated `getComputedStyle()` calls\n- For 100 elements: **99 fewer reflows** (100 → 1)\n- Zero memory overhead (single boolean)\n\n### Current Performance Metrics\n\n| Metric | Value |\n|---------|--------|\n| **Bundle size** | 12.4KB (compressed), 40.3KB (unpacked) |\n| **Initialization** | ~1-2ms per element |\n| **Observer callback** | \u003c0.5ms per frame |\n| **CSS validation** | ~0.1ms total (v2.2.0, with cache) |\n| **Memory per observer** | ~1.2KB |\n| **Animation performance** | 60fps maintained |\n| **Memory leaks** | 0 detected |\n\n### Why Performance Matters\n\n**Layout Thrashing:**\n- Synchronous reflows block the main thread\n- Each reflow can take 10-20ms\n- For 100 elements = **1-2 seconds blocked**\n- User sees stuttering/jank while scrolling\n\n**Solution:**\n- Cache = 1 reflow instead of N\n- 99% improvement on pages with many animations\n- Smoother scrolling, better UX\n\n### Optimized Code Patterns\n\n**IntersectionObserver:**\n- Native API (no scroll listeners)\n- Fast callback (\u003c0.5ms per frame)\n- No debounce needed (browser handles this efficiently)\n\n**CSS Animations:**\n- Transforms only (GPU-accelerated)\n- No layout/repaint during animation\n- `will-change` on visible elements only\n\n**DOM Operations:**\n- `insertAdjacentElement('beforebegin')` instead of `insertBefore`\n- `offsetHeight` instead of `getBoundingClientRect()` (avoids transform issues)\n- Complete cleanup on destroy\n\n**Memory Management:**\n- All observers disconnected\n- Sentinel and wrapper removed\n- State prevents double-disconnects\n- 0 memory leaks detected (121/121 tests)\n\n### Future Considerations\n\n**1. `will-change` Timing**\n- Currently: `.is-visible { will-change: transform, opacity; }`\n- Trade-off: Stays active after animation (consumes GPU memory)\n- Consideration: Use `transitionend` event to remove `will-change`\n- Recommendation: Keep current (GPU memory is cheap)\n\n**2. Threshold Tuning**\n- Current: `threshold: 0` (triggers as soon as 1px is visible)\n- Alternative: `threshold: 0.1` or `threshold: 0.25`\n- Trade-off: Higher threshold = later trigger = smoother stagger\n- Recommendation: Keep `threshold: 0` for immediate feedback\n\n**3. requestIdleCallback**\n- Potential: Defer non-critical setup to browser idle time\n- Trade-off: Complex to implement, marginal benefit\n- Recommendation: Not needed (current performance is excellent)\n\n**4. Testing on Low-End Devices**\n- Test on mobile phones, older browsers\n- Use DevTools CPU throttling\n- Consider Lighthouse/Puppeteer for automated testing\n- Ensure 60fps maintained on real devices\n\n### What NOT to Optimize\n\n**Anti-patterns to avoid:**\n\n1. ❌ **Premature optimization**\n   - Don't optimize without measurements\n   - Profile first, optimize later\n   - \"Premature optimization is the root of all evil\"\n\n2. ❌ **Over-engineering**\n   - Complex solutions for small gains\n   - Keep it simple when possible\n   - Don't sacrifice readability for micro-optimizations\n\n3. ❌ **Breaking performance for size**\n   - Bundle size matters (12.4KB is excellent)\n   - Don't add huge dependencies for minor improvements\n\n4. ❌ **Optimizing unused paths**\n   - Focus on hot paths (element creation, scroll, intersection)\n   - Cold paths (initialization, destroy) less critical\n\n5. ❌ **Sacrificing maintainability**\n   - Don't sacrifice code clarity for micro-optimizations\n   - Comments should explain WHY, not just WHAT\n   - Keep code simple and understandable\n\n### Performance Testing\n\n**Recommended approach:**\n1. Create a benchmark with 100-1000 animated elements\n2. Measure: initialization, first animation, scroll performance, cleanup\n3. Profile with DevTools Performance tab\n4. Test on real pages (not just benchmarks)\n5. Verify 60fps is maintained during scroll\n\n**Tools:**\n- Chrome DevTools Performance tab\n- Firefox Performance Profiler\n- Web Inspector (Safari)\n- Lighthouse (PageSpeed, accessibility, best practices)\n\n---\n \n## 📦 Installation\n\n```bash\nnpm install rune-scroller\n# or\npnpm add rune-scroller\n# or\nyarn add rune-scroller\n```\n\n---\n\n## 🚀 Quick Start\n\n```svelte\n\u003cscript\u003e\n\timport runeScroller from 'rune-scroller';\n\u003c/script\u003e\n\n\u003c!-- Simple animation --\u003e\n\u003cdiv use:runeScroller={{ animation: 'fade-in' }}\u003e\n\t\u003ch2\u003eAnimated Heading\u003c/h2\u003e\n\u003c/div\u003e\n\n\u003c!-- With custom duration --\u003e\n\u003cdiv use:runeScroller={{ animation: 'fade-in-up', duration: 1500 }}\u003e\n\t\u003cdiv class=\"card\"\u003eSmooth fade and slide\u003c/div\u003e\n\u003c/div\u003e\n\n\u003c!-- Repeat on every scroll --\u003e\n\u003cdiv use:runeScroller={{ animation: 'bounce-in', repeat: true }}\u003e\n\t\u003cbutton\u003eBounces on every scroll\u003c/button\u003e\n\u003c/div\u003e\n```\n\nThat's it! The CSS animations are included automatically when you import rune-scroller.\n\n### Option 2: Manual CSS Import\n\nFor fine-grained control, import CSS manually:\n\n**Step 1: Import CSS in your root layout (recommended for SvelteKit):**\n\n```svelte\n\u003c!-- src/routes/+layout.svelte --\u003e\n\u003cscript\u003e\n\timport 'rune-scroller/animations.css';\n\tlet { children } = $props();\n\u003c/script\u003e\n\n{@render children()}\n```\n\n**Or import in each component:**\n\n```svelte\n\u003cscript\u003e\n\timport runeScroller from 'rune-scroller';\n\timport 'rune-scroller/animations.css';\n\u003c/script\u003e\n```\n\n**Step 2: Use the animations**\n\n```svelte\n\u003cscript\u003e\n\timport runeScroller from 'rune-scroller';\n\t// CSS already imported in layout or above\n\u003c/script\u003e\n\n\u003cdiv use:runeScroller={{ animation: 'fade-in' }}\u003e\n\tAnimated content\n\u003c/div\u003e\n```\n\n---\n\n## 🎨 Available Animations\n\n### Fade (5)\n- `fade-in` - Simple opacity fade\n- `fade-in-up` - Fade + move up 300px\n- `fade-in-down` - Fade + move down 300px\n- `fade-in-left` - Fade + move from right 300px\n- `fade-in-right` - Fade + move from left 300px\n\n### Zoom (5)\n- `zoom-in` - Scale from 0.3 to 1\n- `zoom-out` - Scale from 2 to 1\n- `zoom-in-up` - Zoom (0.5→1) + move up 300px\n- `zoom-in-left` - Zoom (0.5→1) + move from right 300px\n- `zoom-in-right` - Zoom (0.5→1) + move from left 300px\n\n### Others (4)\n- `flip` - 3D flip on Y-axis\n- `flip-x` - 3D flip on X-axis\n- `slide-rotate` - Slide + rotate 10°\n- `bounce-in` - Bouncy entrance (spring effect)\n\n---\n\n## ⚙️ Options\n\n```typescript\ninterface RuneScrollerOptions {\n\tanimation?: AnimationType;  // Animation name (default: 'fade-in')\n\tduration?: number;          // Duration in ms (default: 800)\n\trepeat?: boolean;           // Repeat on scroll (default: false)\n\tdebug?: boolean;            // Show sentinel as visible line (default: false)\n\toffset?: number;            // Sentinel offset in px (default: 0, negative = above)\n\tonVisible?: (element: HTMLElement) =\u003e void;  // Callback when animation triggers (v2.0.0+)\n\tsentinelColor?: string;     // Sentinel debug color, e.g. '#ff6b6b' (v2.0.0+)\n\tsentinelId?: string;        // Custom ID for sentinel identification (v2.0.0+)\n}\n```\n\n### Option Details\n\n- **`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.\n- **`duration`** - How long the animation lasts in milliseconds (default: 800ms).\n- **`repeat`** - If `true`, animation plays every time sentinel enters viewport. If `false`, plays only once.\n- **`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`.\n- **`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.\n- **`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.\n- **`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.\n- **`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.\n\n### Examples\n\n```svelte\n\u003c!-- Basic --\u003e\n\u003cdiv use:runeScroller={{ animation: 'zoom-in' }}\u003e\n\tContent\n\u003c/div\u003e\n\n\u003c!-- Custom duration --\u003e\n\u003cdiv use:runeScroller={{ animation: 'fade-in-up', duration: 1000 }}\u003e\n\tFast animation\n\u003c/div\u003e\n\n\u003c!-- Repeat mode --\u003e\n\u003cdiv use:runeScroller={{ animation: 'bounce-in', repeat: true }}\u003e\n\tRepeats every time you scroll\n\u003c/div\u003e\n\n\u003c!-- Debug mode - shows cyan line marking sentinel position --\u003e\n\u003cdiv use:runeScroller={{ animation: 'fade-in', debug: true }}\u003e\n\tThe cyan line below this shows when animation will trigger\n\u003c/div\u003e\n\n\u003c!-- Multiple options --\u003e\n\u003cdiv use:runeScroller={{\n\tanimation: 'fade-in-up',\n\tduration: 1200,\n\trepeat: true,\n\tdebug: true\n}}\u003e\n\tFull featured example\n\u003c/div\u003e\n\n\u003c!-- Large element - trigger animation earlier with negative offset --\u003e\n\u003cdiv use:runeScroller={{\n\tanimation: 'fade-in-up',\n\toffset: -200  // Trigger 200px before element bottom\n}}\u003e\n\tLarge content that needs early triggering\n\u003c/div\u003e\n\n\u003c!-- Delay animation by moving sentinel down --\u003e\n\u003cdiv use:runeScroller={{\n\tanimation: 'zoom-in',\n\toffset: 300  // Trigger 300px after element bottom\n}}\u003e\n\tContent with delayed animation\n\u003c/div\u003e\n\n\u003c!-- v2.0.0: onVisible callback for analytics tracking --\u003e\n\u003cdiv use:runeScroller={{\n\tanimation: 'fade-in-up',\n\tonVisible: (el) =\u003e {\n\t\tconsole.log('Animation visible!', el);\n\t\t// Track analytics, load images, trigger API calls, etc.\n\t\twindow.gtag?.('event', 'animation_visible', { element: el.id });\n\t}\n}}\u003e\n\tTracked animation\n\u003c/div\u003e\n\n\u003c!-- v2.0.0: Custom sentinel color for debugging --\u003e\n\u003cdiv use:runeScroller={{\n\tanimation: 'fade-in',\n\tdebug: true,\n\tsentinelColor: '#ff6b6b'  // Red instead of default cyan\n}}\u003e\n\tRed debug sentinel\n\u003c/div\u003e\n\n\u003c!-- v2.0.0: Custom sentinel ID for identification --\u003e\n\u003cdiv use:runeScroller={{\n\tanimation: 'zoom-in',\n\tsentinelId: 'hero-zoom',\n\tdebug: true\n}}\u003e\n\tIdentified sentinel (shows \"hero-zoom\" in debug mode)\n\u003c/div\u003e\n\n\u003c!-- v2.0.0: Auto-ID (sentinel-1, sentinel-2, etc) --\u003e\n\u003cdiv use:runeScroller={{\n\tanimation: 'fade-in-up',\n\tdebug: true\n\t// sentinelId omitted → auto generates \"sentinel-1\", \"sentinel-2\", etc\n}}\u003e\n\tAuto-identified sentinel\n\u003c/div\u003e\n```\n\n---\n\n## 🎯 How It Works\n\nRune Scroller uses **sentinel-based triggering**:\n\n1. An invisible 1px sentinel element is created below your element\n2. When the sentinel enters the viewport, animation triggers\n3. This ensures precise timing regardless of element size\n4. Uses native IntersectionObserver for performance\n5. Pure CSS animations (GPU-accelerated)\n6. *(v2.0.0)* Sentinel automatically repositions on element resize via ResizeObserver\n\n**Why sentinels?**\n- Accurate timing across all screen sizes\n- No complex offset calculations\n- Handles staggered animations naturally\n- Sentinel stays fixed while element animates (no observer confusion with transforms)\n\n**Automatic ResizeObserver** *(v2.0.0+)*\n- Sentinel repositions automatically when element resizes\n- Works with responsive layouts and dynamic content\n- No configuration needed—it just works\n\n---\n\n## 🌐 SSR Compatibility\n\nWorks seamlessly with SvelteKit. Simply import rune-scroller in your root layout:\n\n```svelte\n\u003c!-- src/routes/+layout.svelte --\u003e\n\u003cscript\u003e\n\timport runeScroller from 'rune-scroller';\n\tlet { children } = $props();\n\u003c/script\u003e\n\n{@render children()}\n```\n\nThen use animations anywhere in your app:\n\n```svelte\n\u003c!-- src/routes/+page.svelte --\u003e\n\u003cscript\u003e\n\timport runeScroller from 'rune-scroller';\n\u003c/script\u003e\n\n\u003c!-- No special handling needed --\u003e\n\u003cdiv use:runeScroller={{ animation: 'fade-in-up' }}\u003e\n\tWorks in SvelteKit SSR!\n\u003c/div\u003e\n```\n\nThe library checks for browser environment and gracefully handles server-side rendering.\n\n---\n\n## ♿ Accessibility\n\nRespects `prefers-reduced-motion`:\n\n```css\n/* In animations.css */\n@media (prefers-reduced-motion: reduce) {\n\t.scroll-animate {\n\t\tanimation: none !important;\n\t\topacity: 1 !important;\n\t\ttransform: none !important;\n\t}\n}\n```\n\nUsers who prefer reduced motion will see content without animations.\n\n---\n\n## 📚 API Reference\n### Public API\n\nRune Scroller exports a **single action-based API** (no components):\n\n1. **`runeScroller`** (default) - Sentinel-based, simple, powerful\n\n**Why actions instead of components?**\n- Actions are lightweight directives\n- No DOM wrapper overhead\n- Better performance\n- More flexible\n\n### Main Export\n\n```typescript\n// CSS is automatically included\nimport runeScroller from 'rune-scroller';\n\n// Named exports\nimport {\n\tuseIntersection,            // Composable\n\tuseIntersectionOnce,        // Composable\n\tcalculateRootMargin         // Utility\n} from 'rune-scroller';\n\n// Types\nimport type {\n\tAnimationType,\n\tRuneScrollerOptions,\n\tIntersectionOptions,\n\tUseIntersectionReturn\n} from 'rune-scroller';\n```\n\n### TypeScript Types\n\n```typescript\ntype AnimationType =\n\t| 'fade-in' | 'fade-in-up' | 'fade-in-down' | 'fade-in-left' | 'fade-in-right'\n\t| 'zoom-in' | 'zoom-out' | 'zoom-in-up' | 'zoom-in-left' | 'zoom-in-right'\n\t| 'flip' | 'flip-x' | 'slide-rotate' | 'bounce-in';\n\ninterface RuneScrollerOptions {\n\tanimation?: AnimationType;\n\tduration?: number;\n\trepeat?: boolean;\n\tdebug?: boolean;\n\toffset?: number;\n\tonVisible?: (element: HTMLElement) =\u003e void;      // v2.0.0+\n\tsentinelColor?: string;                          // v2.0.0+\n\tsentinelId?: string;                             // v2.0.0+\n}\n```\n\n---\n\n## 📖 Examples\n\n### Staggered Animations\n\n```svelte\n\u003cscript\u003e\n\timport runeScroller from 'rune-scroller';\n\n\tconst items = [\n\t\t{ title: 'Feature 1', description: 'Description 1' },\n\t\t{ title: 'Feature 2', description: 'Description 2' },\n\t\t{ title: 'Feature 3', description: 'Description 3' }\n\t];\n\u003c/script\u003e\n\n\u003cdiv class=\"grid\"\u003e\n\t{#each items as item}\n\t\t\u003cdiv use:runeScroller={{ animation: 'fade-in-up', duration: 800 }}\u003e\n\t\t\t\u003ch3\u003e{item.title}\u003c/h3\u003e\n\t\t\t\u003cp\u003e{item.description}\u003c/p\u003e\n\t\t\u003c/div\u003e\n\t{/each}\n\u003c/div\u003e\n```\n\n### Hero Section\n\n```svelte\n\u003cdiv use:runeScroller={{ animation: 'fade-in-down', duration: 1000 }}\u003e\n\t\u003ch1\u003eWelcome\u003c/h1\u003e\n\u003c/div\u003e\n\n\u003cdiv use:runeScroller={{ animation: 'fade-in-up', duration: 1200 }}\u003e\n\t\u003cp\u003eEngaging content\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv use:runeScroller={{ animation: 'zoom-in', duration: 1000 }}\u003e\n\t\u003cbutton\u003eGet Started\u003c/button\u003e\n\u003c/div\u003e\n```\n\n---\n\n## 🔗 Links\n\n- **npm Package**: [rune-scroller](https://www.npmjs.com/package/rune-scroller)\n- **GitHub**: [lelabdev/rune-scroller](https://github.com/lelabdev/rune-scroller)\n- **Changelog**: [CHANGELOG.md](https://github.com/lelabdev/rune-scroller/blob/main/lib/CHANGELOG.md)\n\n---\n\n## 📄 License\n\nMIT © [ludoloops](https://github.com/ludoloops)\n\n---\n\n## 🤝 Contributing\n\nContributions welcome! Please open an issue or PR on GitHub.\n\n```bash\n# Development\nbun install\nbun run dev\nbun test\nbun run build\n```\n\n---\n\nMade with ❤️ by [LeLab.dev](https://lelab.dev)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flelabdev%2Frune-scroller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flelabdev%2Frune-scroller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flelabdev%2Frune-scroller/lists"}