rune-scroller 2.2.1 → 3.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rune-scroller",
3
- "version": "2.2.1",
4
- "description": "Lightweight, high-performance scroll animations for Svelte 5. 14KB gzipped, zero dependencies.",
3
+ "version": "3.0.0",
4
+ "description": "Lightweight scroll animations for Svelte 5. Drop-in AOS replacement. 30 animations, zero dependencies.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "license": "MIT",
@@ -29,6 +29,9 @@
29
29
  "svelte": "./dist/index.js",
30
30
  "default": "./dist/index.js"
31
31
  },
32
+ "./aos": {
33
+ "default": "./dist/aos.js"
34
+ },
32
35
  "./animations.css": "./dist/animations.css"
33
36
  },
34
37
  "files": [
@@ -59,7 +62,7 @@
59
62
  "@eslint/compat": "^1.4.0",
60
63
  "@eslint/js": "^9.36.0",
61
64
  "@sveltejs/kit": "^2.43.2",
62
- "@sveltejs/package": "^2.5.4",
65
+ "@sveltejs/package": "^2.5.7",
63
66
  "@sveltejs/vite-plugin-svelte": "^6.2.0",
64
67
  "@types/node": "^22",
65
68
  "eslint": "^9.36.0",
@@ -1,83 +0,0 @@
1
- /**
2
- * Reusable scroll animation styles - Optimized with CSS custom properties
3
- * Reduces bundle size while maintaining all 14 animations
4
- */
5
-
6
- /* Base animation container with optimized transitions */
7
- .scroll-animate,
8
- .animated-element {
9
- opacity: 0;
10
- transition: opacity var(--duration, 2500ms) linear var(--delay, 0ms),
11
- transform var(--duration, 2500ms) cubic-bezier(0.34, 1.56, 0.64, 1) var(--delay, 0ms);
12
- transform: perspective(1000px) translate(var(--tx, 0), var(--ty, 0)) scale(var(--scale, 1)) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) rotate(var(--rotate, 0deg));
13
- }
14
-
15
- .scroll-animate.is-visible,
16
- .animated-element.is-visible {
17
- opacity: 1 !important;
18
- will-change: transform, opacity;
19
- transform: perspective(1000px) translate(var(--tx, 0), var(--ty, 0)) scale(var(--scale, 1)) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) rotate(var(--rotate, 0deg));
20
- }
21
-
22
- /* Initial state - transform values before animation */
23
- [data-animation='fade-in'] { --tx: 0; --ty: 0; }
24
- [data-animation='fade-in-up'] { --ty: var(--translate-distance, 300px); }
25
- [data-animation='fade-in-down'] { --ty: calc(-1 * var(--translate-distance, 300px)); }
26
- [data-animation='fade-in-left'] { --tx: calc(-1 * var(--translate-distance, 300px)); }
27
- [data-animation='fade-in-right'] { --tx: var(--translate-distance, 300px); }
28
-
29
- [data-animation='zoom-in'] { --scale: 0.3; }
30
- [data-animation='zoom-out'] { --scale: 2; }
31
- [data-animation='zoom-in-up'] { --scale: 0.5; --ty: var(--translate-distance, 300px); }
32
- [data-animation='zoom-in-left'] { --scale: 0.5; --tx: calc(-1 * var(--translate-distance, 300px)); }
33
- [data-animation='zoom-in-right'] { --scale: 0.5; --tx: var(--translate-distance, 300px); }
34
-
35
- [data-animation='flip'] { --ry: 90deg; }
36
- [data-animation='flip-x'] { --rx: 90deg; }
37
-
38
- [data-animation='slide-rotate'] { --tx: calc(-1 * var(--translate-distance, 300px)); --rotate: -45deg; }
39
- [data-animation='bounce-in'] { --scale: 0; }
40
-
41
- /* Visible state - reset transform values to final position */
42
- [data-animation='fade-in-up'].is-visible { --ty: 0; }
43
- [data-animation='fade-in-down'].is-visible { --ty: 0; }
44
- [data-animation='fade-in-left'].is-visible { --tx: 0; }
45
- [data-animation='fade-in-right'].is-visible { --tx: 0; }
46
-
47
- [data-animation='zoom-in'].is-visible { --scale: 1; }
48
- [data-animation='zoom-out'].is-visible { --scale: 1; }
49
- [data-animation='zoom-in-up'].is-visible { --scale: 1; --ty: 0; }
50
- [data-animation='zoom-in-left'].is-visible { --scale: 1; --tx: 0; }
51
- [data-animation='zoom-in-right'].is-visible { --scale: 1; --tx: 0; }
52
-
53
- [data-animation='flip'].is-visible { --ry: 0deg; }
54
- [data-animation='flip-x'].is-visible { --rx: 0deg; }
55
-
56
- [data-animation='slide-rotate'].is-visible { --tx: 0; --rotate: 0deg; }
57
-
58
- @keyframes bounce {
59
- 0% { transform: scale(0); }
60
- 50% { transform: scale(1.1); }
61
- 100% { transform: scale(1); }
62
- }
63
-
64
- /* Bounce animation - special case with keyframes */
65
- [data-animation='bounce-in'].is-visible {
66
- animation: bounce var(--duration, 1500ms) cubic-bezier(0.68, -0.55, 0.265, 1.55);
67
- animation-delay: var(--delay, 0ms);
68
- }
69
-
70
- /* Accessibility: Respect user's motion preferences */
71
- @media (prefers-reduced-motion: reduce) {
72
- .scroll-animate,
73
- .animated-element {
74
- transition: none;
75
- animation: none !important;
76
- }
77
-
78
- .scroll-animate.is-visible,
79
- .animated-element.is-visible {
80
- opacity: 1;
81
- transform: none !important;
82
- }
83
- }
@@ -1,18 +0,0 @@
1
- /**
2
- * Calculate rootMargin for IntersectionObserver from offset or custom rootMargin
3
- *
4
- * @param {number} [offset] - Viewport offset (0-100). 0 = bottom of viewport touches top of element, 100 = top of viewport touches top of element
5
- * @param {string} [rootMargin] - Custom rootMargin string (takes precedence over offset)
6
- * @returns {string} rootMargin string for IntersectionObserver
7
- */
8
- export function calculateRootMargin(offset?: number, rootMargin?: string): string;
9
- /**
10
- * Animation utilities
11
- * Type definitions have been moved to types.js for single source of truth
12
- */
13
- /**
14
- * All available animation types in the library
15
- * Useful for programmatic access and validation
16
- * @type {readonly string[]}
17
- */
18
- export const ANIMATION_TYPES: readonly string[];
@@ -1,38 +0,0 @@
1
- /**
2
- * Animation utilities
3
- * Type definitions have been moved to types.js for single source of truth
4
- */
5
-
6
- /**
7
- * All available animation types in the library
8
- * Useful for programmatic access and validation
9
- * @type {readonly string[]}
10
- */
11
- export const ANIMATION_TYPES = [
12
- 'fade-in',
13
- 'fade-in-up',
14
- 'fade-in-down',
15
- 'fade-in-left',
16
- 'fade-in-right',
17
- 'zoom-in',
18
- 'zoom-out',
19
- 'zoom-in-up',
20
- 'zoom-in-left',
21
- 'zoom-in-right',
22
- 'flip',
23
- 'flip-x',
24
- 'slide-rotate',
25
- 'bounce-in'
26
- ];
27
-
28
- /**
29
- * Calculate rootMargin for IntersectionObserver from offset or custom rootMargin
30
- *
31
- * @param {number} [offset] - Viewport offset (0-100). 0 = bottom of viewport touches top of element, 100 = top of viewport touches top of element
32
- * @param {string} [rootMargin] - Custom rootMargin string (takes precedence over offset)
33
- * @returns {string} rootMargin string for IntersectionObserver
34
- */
35
- export function calculateRootMargin(offset, rootMargin) {
36
- return rootMargin ??
37
- (offset !== undefined ? `-${100 - offset}% 0px -${offset}% 0px` : '-10% 0px -10% 0px');
38
- }
@@ -1,30 +0,0 @@
1
- /**
2
- * @param {HTMLElement} element
3
- * @param {number} [duration]
4
- * @param {number} [delay=0]
5
- */
6
- export function setCSSVariables(element: HTMLElement, duration?: number, delay?: number): void;
7
- /**
8
- * @param {HTMLElement} element
9
- * @param {import('./types.js').AnimationType} animation
10
- */
11
- export function setupAnimationElement(element: HTMLElement, animation: import("./types.js").AnimationType): void;
12
- /**
13
- * @param {HTMLElement} element
14
- * @param {boolean} [debug=false]
15
- * @param {number} [offset=0]
16
- * @param {string} [sentinelColor='#00e0ff']
17
- * @param {string} [debugLabel]
18
- * @param {string} [sentinelId]
19
- * @returns {{ element: HTMLElement, id: string }}
20
- */
21
- export function createSentinel(element: HTMLElement, debug?: boolean, offset?: number, sentinelColor?: string, debugLabel?: string, sentinelId?: string): {
22
- element: HTMLElement;
23
- id: string;
24
- };
25
- /**
26
- * Check if CSS animations are loaded and warn if not (dev only)
27
- * Uses cache to avoid expensive getComputedStyle() on every element creation
28
- * @returns {boolean} True if CSS appears to be loaded
29
- */
30
- export function checkAndWarnIfCSSNotLoaded(): boolean;
package/dist/dom-utils.js DELETED
@@ -1,112 +0,0 @@
1
- /**
2
- * Global counter for auto-generating sentinel IDs
3
- * @type {number}
4
- */
5
- let sentinelCounter = 0;
6
-
7
- /**
8
- * Cache to check CSS only once per page load
9
- * Avoids expensive getComputedStyle() calls
10
- * @type {boolean | null}
11
- */
12
- let cssCheckResult = null;
13
-
14
- /**
15
- * @param {HTMLElement} element
16
- * @param {number} [duration]
17
- * @param {number} [delay=0]
18
- */
19
- export function setCSSVariables(element, duration, delay = 0) {
20
- if (duration !== undefined) {
21
- element.style.setProperty('--duration', `${duration}ms`);
22
- }
23
- element.style.setProperty('--delay', `${delay}ms`);
24
- }
25
-
26
- /**
27
- * @param {HTMLElement} element
28
- * @param {import('./types.js').AnimationType} animation
29
- */
30
- export function setupAnimationElement(element, animation) {
31
- element.classList.add('scroll-animate');
32
- element.setAttribute('data-animation', animation);
33
- }
34
-
35
- /**
36
- * @param {HTMLElement} element
37
- * @param {boolean} [debug=false]
38
- * @param {number} [offset=0]
39
- * @param {string} [sentinelColor='#00e0ff']
40
- * @param {string} [debugLabel]
41
- * @param {string} [sentinelId]
42
- * @returns {{ element: HTMLElement, id: string }}
43
- */
44
- export function createSentinel(element, debug = false, offset = 0, sentinelColor = '#00e0ff', debugLabel = '', sentinelId) {
45
- const sentinel = document.createElement('div');
46
- // Use offsetHeight instead of getBoundingClientRect for accurate dimensions
47
- // getBoundingClientRect returns transformed dimensions (affected by scale, etc)
48
- // offsetHeight returns actual element height independent of CSS transforms
49
- const elementHeight = element.offsetHeight;
50
- const sentinelTop = elementHeight + offset;
51
-
52
- // Generate auto-ID if not provided
53
- if (!sentinelId) {
54
- sentinelCounter++;
55
- sentinelId = `sentinel-${sentinelCounter}`;
56
- }
57
-
58
- // Always set to data-sentinel-id attribute
59
- sentinel.setAttribute('data-sentinel-id', sentinelId);
60
-
61
- if (debug) {
62
- sentinel.style.cssText =
63
- `position:absolute;top:${sentinelTop}px;left:0;width:100%;height:3px;background:${sentinelColor};margin:0;padding:2px 4px;box-sizing:border-box;z-index:999;pointer-events:none;display:flex;align-items:center;font-size:10px;color:#000;font-weight:bold;white-space:nowrap;overflow:hidden;text-overflow:ellipsis`;
64
- sentinel.setAttribute('data-sentinel-debug', 'true');
65
- // Show ID in debug mode (or debugLabel if provided)
66
- if (debugLabel) {
67
- sentinel.textContent = debugLabel;
68
- } else {
69
- sentinel.textContent = sentinelId;
70
- }
71
- } else {
72
- sentinel.style.cssText =
73
- `position:absolute;top:${sentinelTop}px;left:0;width:100%;height:1px;visibility:hidden;margin:0;padding:0;box-sizing:border-box;pointer-events:none`;
74
- }
75
-
76
- return { element: sentinel, id: sentinelId };
77
- }
78
-
79
- /**
80
- * Check if CSS animations are loaded and warn if not (dev only)
81
- * Uses cache to avoid expensive getComputedStyle() on every element creation
82
- * @returns {boolean} True if CSS appears to be loaded
83
- */
84
- export function checkAndWarnIfCSSNotLoaded() {
85
- if (typeof document === 'undefined') return false;
86
- if (process.env.NODE_ENV === 'production') return true;
87
-
88
- // Return cached result if already checked (avoids expensive reflows)
89
- if (cssCheckResult !== null) return cssCheckResult;
90
-
91
- // Try to detect if animations.css is loaded by checking for animation classes
92
- const test = document.createElement('div');
93
- test.className = 'scroll-animate is-visible';
94
- test.style.position = 'absolute';
95
- test.style.opacity = '0';
96
- document.body.appendChild(test);
97
- const computed = getComputedStyle(test);
98
- const hasAnimation = computed.animation !== 'none' && computed.animation !== '';
99
- test.remove();
100
-
101
- if (!hasAnimation) {
102
- console.warn(
103
- '[rune-scroller] CSS animations not found. Make sure to import the animations:\n' +
104
- ' import "rune-scroller/animations.css";\n' +
105
- 'Documentation: https://github.com/lelabdev/rune-scroller#installation'
106
- );
107
- }
108
-
109
- // Cache the result for future calls
110
- cssCheckResult = hasAnimation;
111
- return hasAnimation;
112
- }
package/dist/index.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export default runeScroller;
2
- export { runeScroller };
3
- export { calculateRootMargin } from "./animations.js";
4
- import { runeScroller } from './runeScroller.js';
5
- export { useIntersection, useIntersectionOnce } from "./useIntersection.svelte.js";
package/dist/index.js DELETED
@@ -1,23 +0,0 @@
1
- /**
2
- * Rune Scroller - Lightweight scroll animations for Svelte 5
3
- *
4
- * Main entry point exporting all public APIs
5
- *
6
- * @module rune-scroller
7
- */
8
-
9
- // Import CSS animations automatically
10
- import './animations.css';
11
-
12
- // Main action (default export - recommended)
13
- import { runeScroller } from './runeScroller.js';
14
- export default runeScroller;
15
- export { runeScroller };
16
-
17
-
18
-
19
- // Composables
20
- export { useIntersection, useIntersectionOnce } from './useIntersection.svelte.js';
21
-
22
- // Utilities
23
- export { calculateRootMargin } from './animations.js';
@@ -1,21 +0,0 @@
1
- /**
2
- * Shared IntersectionObserver utility functions
3
- * Reduces code duplication between action implementations
4
- */
5
- /**
6
- * @param {HTMLElement} target
7
- * @param {IntersectionObserverCallback} callback
8
- * @param {IntersectionObserverInit} options
9
- * @returns {{ observer: IntersectionObserver, isConnected: boolean }}
10
- */
11
- export function createManagedObserver(target: HTMLElement, callback: IntersectionObserverCallback, options: IntersectionObserverInit): {
12
- observer: IntersectionObserver;
13
- isConnected: boolean;
14
- };
15
- /**
16
- * @param {IntersectionObserver} observer
17
- * @param {{ isConnected: boolean }} state
18
- */
19
- export function disconnectObserver(observer: IntersectionObserver, state: {
20
- isConnected: boolean;
21
- }): void;
@@ -1,31 +0,0 @@
1
- /**
2
- * Shared IntersectionObserver utility functions
3
- * Reduces code duplication between action implementations
4
- */
5
-
6
- /**
7
- * @param {HTMLElement} target
8
- * @param {IntersectionObserverCallback} callback
9
- * @param {IntersectionObserverInit} options
10
- * @returns {{ observer: IntersectionObserver, isConnected: boolean }}
11
- */
12
- export function createManagedObserver(target, callback, options) {
13
- const observer = new IntersectionObserver(callback, options);
14
- observer.observe(target);
15
-
16
- return {
17
- observer,
18
- isConnected: true
19
- };
20
- }
21
-
22
- /**
23
- * @param {IntersectionObserver} observer
24
- * @param {{ isConnected: boolean }} state
25
- */
26
- export function disconnectObserver(observer, state) {
27
- if (state.isConnected && observer) {
28
- observer.disconnect();
29
- state.isConnected = false;
30
- }
31
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * @param {HTMLElement} element
3
- * @param {import('./types.js').RuneScrollerOptions} [options]
4
- * @returns {{ update: (newOptions?: import('./types.js').RuneScrollerOptions) => void, destroy: () => void }}
5
- */
6
- export function runeScroller(element: HTMLElement, options?: import("./types.js").RuneScrollerOptions): {
7
- update: (newOptions?: import("./types.js").RuneScrollerOptions) => void;
8
- destroy: () => void;
9
- };
@@ -1,166 +0,0 @@
1
- import { setCSSVariables, setupAnimationElement, createSentinel, checkAndWarnIfCSSNotLoaded } from './dom-utils.js';
2
- import { createManagedObserver, disconnectObserver } from './observer-utils.js';
3
- import { ANIMATION_TYPES } from './animations.js';
4
-
5
- /**
6
- * @param {HTMLElement} element
7
- * @param {import('./types.js').RuneScrollerOptions} [options]
8
- * @returns {{ update: (newOptions?: import('./types.js').RuneScrollerOptions) => void, destroy: () => void }}
9
- */
10
- export function runeScroller(element, options) {
11
- // SSR Guard: Return no-op action when running on server
12
- if (typeof window === 'undefined') {
13
- return {
14
- update: () => {},
15
- destroy: () => {}
16
- };
17
- }
18
-
19
- // Warn if CSS is not loaded (first time only)
20
- if (typeof document !== 'undefined') {
21
- checkAndWarnIfCSSNotLoaded();
22
- }
23
-
24
- // Validate animation type
25
- let animation = options?.animation ?? 'fade-in';
26
- if (animation && !ANIMATION_TYPES.includes(animation)) {
27
- if (process.env.NODE_ENV !== 'production') {
28
- console.warn(
29
- `[rune-scroller] Invalid animation "${animation}". Using "fade-in" instead. ` +
30
- `Valid options: ${ANIMATION_TYPES.join(', ')}`
31
- );
32
- }
33
- animation = 'fade-in';
34
- }
35
-
36
- // Initialize opacity: 0 BEFORE adding .scroll-animate class
37
- // This ensures the transition applies correctly when .is-visible is added later
38
- element.style.opacity = '0';
39
-
40
- // Setup animation classes and CSS variables
41
- if (animation) {
42
- setupAnimationElement(element, animation);
43
- }
44
-
45
- // Set CSS variables for duration
46
- if (options?.duration !== undefined) {
47
- setCSSVariables(element, options.duration);
48
- }
49
-
50
- // Force reflow to ensure transitions are ready
51
- void element.offsetHeight;
52
-
53
- // Create a wrapper div around the element to position the sentinel
54
- const wrapper = document.createElement('div');
55
- wrapper.style.cssText = 'position:relative;display:block;width:100%;margin:0;padding:0;box-sizing:border-box';
56
- element.insertAdjacentElement('beforebegin', wrapper);
57
- wrapper.appendChild(element);
58
-
59
- // Create the invisible sentinel (or visible if debug=true)
60
- // Positioned absolutely relative to the wrapper
61
- const sentinelResult = createSentinel(
62
- element,
63
- options?.debug,
64
- options?.offset,
65
- options?.sentinelColor,
66
- options?.debugLabel,
67
- options?.sentinelId
68
- );
69
- const sentinel = sentinelResult.element;
70
- const sentinelId = sentinelResult.id;
71
-
72
- // Add sentinel ID to element (either provided or auto-generated)
73
- element.setAttribute('data-sentinel-id', sentinelId);
74
-
75
- wrapper.appendChild(sentinel);
76
-
77
- // Observe the sentinel with cleanup tracking
78
- const state = { isConnected: true };
79
- let currentSentinel = sentinel;
80
- let resizeObserver;
81
- let intersectionObserver;
82
-
83
- const { observer } = createManagedObserver(
84
- sentinel,
85
- (entries) => {
86
- const isIntersecting = entries[0].isIntersecting;
87
- if (isIntersecting) {
88
- // Add the is-visible class to trigger animation
89
- element.classList.add('is-visible');
90
- // Call onVisible callback if provided
91
- options?.onVisible?.(element);
92
- // Disconnect if not in repeat mode
93
- if (!options?.repeat) {
94
- disconnectObserver(intersectionObserver, state);
95
- }
96
- } else if (options?.repeat) {
97
- // In repeat mode, remove the class when the sentinel exits
98
- element.classList.remove('is-visible');
99
- }
100
- },
101
- { threshold: 0 }
102
- );
103
-
104
- intersectionObserver = observer;
105
-
106
- // Function to recreate sentinel when element is resized
107
- const recreateSentinel = () => {
108
- const newSentinelResult = createSentinel(
109
- element,
110
- options?.debug,
111
- options?.offset,
112
- options?.sentinelColor,
113
- options?.debugLabel,
114
- sentinelId
115
- );
116
- const newSentinel = newSentinelResult.element;
117
- currentSentinel.replaceWith(newSentinel);
118
- currentSentinel = newSentinel;
119
- // Update observer to watch the new sentinel
120
- intersectionObserver.disconnect();
121
- intersectionObserver.observe(newSentinel);
122
- };
123
-
124
- // Setup ResizeObserver to handle element resizing
125
- if (typeof ResizeObserver !== 'undefined') {
126
- resizeObserver = new ResizeObserver(() => {
127
- recreateSentinel();
128
- });
129
- resizeObserver.observe(element);
130
- }
131
-
132
- return {
133
- update(newOptions) {
134
- if (newOptions?.animation) {
135
- element.setAttribute('data-animation', newOptions.animation);
136
- }
137
- if (newOptions?.duration) {
138
- setCSSVariables(element, newOptions.duration);
139
- }
140
- // Update repeat option
141
- if (newOptions?.repeat !== undefined && newOptions.repeat !== options?.repeat) {
142
- options = { ...options, repeat: newOptions.repeat };
143
- }
144
- // Update offset and debug if changed
145
- if ((newOptions?.offset !== undefined && newOptions.offset !== options?.offset) ||
146
- (newOptions?.debug !== undefined && newOptions.debug !== options?.debug)) {
147
- options = { ...options, ...newOptions };
148
- recreateSentinel();
149
- }
150
- },
151
- destroy() {
152
- disconnectObserver(intersectionObserver, state);
153
- // Cleanup ResizeObserver
154
- if (resizeObserver) {
155
- resizeObserver.disconnect();
156
- }
157
- currentSentinel.remove();
158
- // Unwrap element (move it out of wrapper)
159
- const parent = wrapper.parentElement;
160
- if (parent) {
161
- wrapper.insertAdjacentElement('beforebegin', element);
162
- }
163
- wrapper.remove();
164
- }
165
- };
166
- }
package/dist/types.d.ts DELETED
@@ -1,78 +0,0 @@
1
- /**
2
- * Animation type names available in Rune Scroller
3
- */
4
- export type AnimationType = "fade-in" | "fade-in-up" | "fade-in-down" | "fade-in-left" | "fade-in-right" | "zoom-in" | "zoom-out" | "zoom-in-up" | "zoom-in-left" | "zoom-in-right" | "flip" | "flip-x" | "slide-rotate" | "bounce-in";
5
- /**
6
- * Options for the runeScroller action
7
- * Sentinel-based scroll animation triggering
8
- */
9
- export type RuneScrollerOptions = {
10
- /**
11
- * - Animation type to apply
12
- */
13
- animation?: AnimationType | undefined;
14
- /**
15
- * - Animation duration in milliseconds
16
- */
17
- duration?: number | undefined;
18
- /**
19
- * - Repeat animation on every scroll
20
- */
21
- repeat?: boolean | undefined;
22
- /**
23
- * - Show sentinel as visible line for debugging
24
- */
25
- debug?: boolean | undefined;
26
- /**
27
- * - Sentinel color for debug mode (hex or CSS color)
28
- */
29
- sentinelColor?: string | undefined;
30
- /**
31
- * - Unique identifier for sentinel (auto-generated if not provided)
32
- */
33
- sentinelId?: string | undefined;
34
- /**
35
- * - Debug label to show on sentinel (e.g., animation name)
36
- */
37
- debugLabel?: string | undefined;
38
- /**
39
- * - Offset of sentinel in pixels (negative = above element)
40
- */
41
- offset?: number | undefined;
42
- /**
43
- * - Callback fired when animation becomes visible
44
- */
45
- onVisible?: ((element: HTMLElement) => void) | undefined;
46
- };
47
- /**
48
- * Configuration options for IntersectionObserver
49
- * Used by useIntersection and useIntersectionOnce composables
50
- */
51
- export type IntersectionOptions = {
52
- /**
53
- * - IntersectionObserver threshold
54
- */
55
- threshold?: number | number[] | undefined;
56
- /**
57
- * - Custom margin around root element
58
- */
59
- rootMargin?: string | undefined;
60
- /**
61
- * - Root element for intersection observation
62
- */
63
- root?: Element | null | undefined;
64
- };
65
- /**
66
- * Return type for useIntersection and useIntersectionOnce composables
67
- * Provides reactive element reference and visibility state
68
- */
69
- export type UseIntersectionReturn = {
70
- /**
71
- * - Reference to the DOM element being observed
72
- */
73
- element: HTMLElement | null;
74
- /**
75
- * - Whether the element is currently visible in viewport
76
- */
77
- isVisible: boolean;
78
- };