accessify-widget 0.3.52 → 0.3.54

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.
@@ -1,4 +1,4 @@
1
- import { d, i } from "./index-DMc-PMDI.js";
1
+ import { d, i } from "./index-Bz63wH7t.js";
2
2
  export {
3
3
  d as destroy,
4
4
  i as init
@@ -1,3 +1,25 @@
1
+ function isWidgetInternal(node) {
2
+ let n = node || null;
3
+ let hops = 0;
4
+ while (n && hops < 50) {
5
+ if (n instanceof Element) {
6
+ if (n.id === "accessify-root") return true;
7
+ if (n.tagName === "ACCESSIFY-WIDGET") return true;
8
+ const cls = n.getAttribute?.("class");
9
+ if (cls && /(?:^|\s)accessify-/.test(cls)) return true;
10
+ }
11
+ const parent = n.parentNode;
12
+ if (parent) {
13
+ n = parent;
14
+ } else if (n.host) {
15
+ n = n.host;
16
+ } else {
17
+ break;
18
+ }
19
+ hops++;
20
+ }
21
+ return false;
22
+ }
1
23
  function createAnimationStopModule() {
2
24
  let enabled = false;
3
25
  const STYLE_ID = "a11y-stop-animations";
@@ -9,6 +31,43 @@ function createAnimationStopModule() {
9
31
  let originalAnimate = null;
10
32
  let pausedVideos = [];
11
33
  let gifOriginals = /* @__PURE__ */ new Map();
34
+ let inPointerWindow = false;
35
+ let pointerWindowTimer = null;
36
+ let pointerListenerFn = null;
37
+ function openPointerWindow(e) {
38
+ const path = e.composedPath ? e.composedPath() : [];
39
+ const target = path && path[0] || e.target;
40
+ if (isWidgetInternal(target)) return;
41
+ inPointerWindow = true;
42
+ if (pointerWindowTimer) clearTimeout(pointerWindowTimer);
43
+ pointerWindowTimer = setTimeout(() => {
44
+ inPointerWindow = false;
45
+ pointerWindowTimer = null;
46
+ }, 120);
47
+ }
48
+ function startPointerTracking() {
49
+ if (pointerListenerFn) return;
50
+ pointerListenerFn = openPointerWindow;
51
+ const opts = { capture: true, passive: true };
52
+ window.addEventListener("pointerenter", pointerListenerFn, opts);
53
+ window.addEventListener("pointerover", pointerListenerFn, opts);
54
+ window.addEventListener("mouseenter", pointerListenerFn, opts);
55
+ window.addEventListener("mouseover", pointerListenerFn, opts);
56
+ }
57
+ function stopPointerTracking() {
58
+ if (!pointerListenerFn) return;
59
+ const opts = { capture: true };
60
+ window.removeEventListener("pointerenter", pointerListenerFn, opts);
61
+ window.removeEventListener("pointerover", pointerListenerFn, opts);
62
+ window.removeEventListener("mouseenter", pointerListenerFn, opts);
63
+ window.removeEventListener("mouseover", pointerListenerFn, opts);
64
+ pointerListenerFn = null;
65
+ if (pointerWindowTimer) {
66
+ clearTimeout(pointerWindowTimer);
67
+ pointerWindowTimer = null;
68
+ }
69
+ inPointerWindow = false;
70
+ }
12
71
  function patchAnimate() {
13
72
  if (originalAnimate) return;
14
73
  originalAnimate = Element.prototype.animate;
@@ -16,26 +75,18 @@ function createAnimationStopModule() {
16
75
  if (!enabled) {
17
76
  return originalAnimate.call(this, keyframes, options);
18
77
  }
19
- let opts;
20
- if (typeof options === "number") {
21
- opts = { duration: 1e-3, fill: "forwards" };
22
- } else {
23
- opts = {
24
- ...options || {},
25
- duration: 1e-3,
26
- delay: 0,
27
- iterations: 1,
28
- fill: "forwards"
29
- };
30
- delete opts.iterationStart;
31
- delete opts.endDelay;
78
+ if (isWidgetInternal(this)) {
79
+ return originalAnimate.call(this, keyframes, options);
32
80
  }
33
- const anim = originalAnimate.call(this, keyframes, opts);
34
- try {
35
- anim.finish();
36
- } catch {
81
+ if (inPointerWindow) {
82
+ const anim = originalAnimate.call(this, keyframes, options);
83
+ try {
84
+ anim.cancel();
85
+ } catch {
86
+ }
87
+ return anim;
37
88
  }
38
- return anim;
89
+ return originalAnimate.call(this, keyframes, options);
39
90
  };
40
91
  }
41
92
  function restoreAnimate() {
@@ -344,6 +395,7 @@ function createAnimationStopModule() {
344
395
  function activate() {
345
396
  if (enabled) return;
346
397
  enabled = true;
398
+ startPointerTracking();
347
399
  patchAnimate();
348
400
  injectStyles();
349
401
  finishAllAnimations();
@@ -374,6 +426,7 @@ function createAnimationStopModule() {
374
426
  stopPeriodicScan();
375
427
  teardownObserver();
376
428
  restoreAnimate();
429
+ stopPointerTracking();
377
430
  unfreezeScrollAnimations();
378
431
  restoreGSAP();
379
432
  restoreHiddenElements();
@@ -402,4 +455,4 @@ function createAnimationStopModule() {
402
455
  export {
403
456
  createAnimationStopModule as default
404
457
  };
405
- //# sourceMappingURL=animation-stop-_chC8bg1.js.map
458
+ //# sourceMappingURL=animation-stop-Bv6xRU2q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animation-stop-Bv6xRU2q.js","sources":["../src/features/animation-stop.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\n/**\n * Animation Stop – V4 (Clean 3-Layer Approach)\n *\n * Layer 1: CSS injection — kill all CSS animations/transitions via 0.001ms duration\n * Layer 2: Web Animations API — finish() all running WAAPI animations (Framer Motion etc.)\n * Layer 3: JS library detection — GSAP globalTimeline, inline-style freeze for rAF-based libs\n * Layer 4: Media — pause videos, freeze GIFs, stop SVG SMIL, stop marquees\n *\n * + MutationObserver for dynamic content\n * + Periodic re-scan (every 500ms) for frameworks that create new animations\n *\n * DOES NOT:\n * - Monkey-patch IntersectionObserver (breaks lazy loading)\n * - Override requestAnimationFrame (breaks entire page)\n * - Override Element.prototype.animate() (too invasive)\n * - Override matchMedia (too invasive)\n * - Set opacity:1 !important via CSS (breaks modals/hidden elements)\n * - Use animation:none or transition:none (breaks JS events)\n */\n/**\n * Walk up parentNode → shadowRoot.host chain to detect if a node\n * belongs to our own widget UI. Used to skip widget-internal animations\n * so the trigger/card hover effects inside Shadow DOM keep working.\n */\nfunction isWidgetInternal(node: Node | null | undefined): boolean {\n let n: Node | null = node || null;\n let hops = 0;\n while (n && hops < 50) {\n if (n instanceof Element) {\n if (n.id === 'accessify-root') return true;\n if (n.tagName === 'ACCESSIFY-WIDGET') return true;\n const cls = n.getAttribute?.('class');\n if (cls && /(?:^|\\s)accessify-/.test(cls)) return true;\n }\n const parent = (n as any).parentNode;\n if (parent) {\n n = parent;\n } else if ((n as any).host) {\n n = (n as any).host;\n } else {\n break;\n }\n hops++;\n }\n return false;\n}\n\nexport default function createAnimationStopModule(): FeatureModule {\n let enabled = false;\n const STYLE_ID = 'a11y-stop-animations';\n const STORAGE_KEY = 'accessify-animation-stop';\n\n // --- State ---\n let mutationObserver: MutationObserver | null = null;\n let scanInterval: ReturnType<typeof setInterval> | null = null;\n let scrollHandler: (() => void) | null = null;\n let originalVideoPlay: typeof HTMLVideoElement.prototype.play | null = null;\n let originalAnimate: typeof Element.prototype.animate | null = null;\n let pausedVideos: HTMLVideoElement[] = [];\n let gifOriginals = new Map<HTMLImageElement, string>();\n\n // --- Pointer-window state ---\n // Framer Motion's whileHover creates a WAAPI animation synchronously in\n // response to a pointerenter event. We set this flag for ~120ms whenever\n // a pointer enters a non-widget element, so patchAnimate() can tell\n // \"this animate() call is a hover gesture\" vs \"this is an entry/scroll\n // animation created at mount\". Hover animations are cancelled (revert to\n // baseline); everything else passes through untouched.\n let inPointerWindow = false;\n let pointerWindowTimer: ReturnType<typeof setTimeout> | null = null;\n let pointerListenerFn: ((e: Event) => void) | null = null;\n\n // =========================================================================\n // Layer 0a: Pointer-window tracking\n //\n // When a pointer enters a non-widget element we open a ~120ms window where\n // any new Element.animate() call is classified as a HOVER gesture and\n // cancelled (reverts to baseline). Outside this window animate() is a\n // pass-through, so entry/scroll/layout animations at mount still reach\n // their target state. This is how we distinguish Framer's whileHover from\n // its initial=\"hidden\" / animate=\"show\" entry transforms (both use y/scale).\n // =========================================================================\n\n function openPointerWindow(e: Event) {\n // Ignore pointer events over our own widget — our card/trigger hover FX\n // must keep working and are excluded from the cancel logic anyway.\n const path = (e as any).composedPath ? (e as any).composedPath() : [];\n const target = (path && path[0]) || (e.target as Node | null);\n if (isWidgetInternal(target)) return;\n\n inPointerWindow = true;\n if (pointerWindowTimer) clearTimeout(pointerWindowTimer);\n pointerWindowTimer = setTimeout(() => {\n inPointerWindow = false;\n pointerWindowTimer = null;\n }, 120);\n }\n\n function startPointerTracking() {\n if (pointerListenerFn) return;\n pointerListenerFn = openPointerWindow;\n // Capture phase on window so we fire BEFORE any React/Framer listener\n // registered on descendants. passive:true = no preventDefault side-effect.\n const opts: AddEventListenerOptions = { capture: true, passive: true };\n window.addEventListener('pointerenter', pointerListenerFn, opts);\n window.addEventListener('pointerover', pointerListenerFn, opts);\n window.addEventListener('mouseenter', pointerListenerFn, opts);\n window.addEventListener('mouseover', pointerListenerFn, opts);\n }\n\n function stopPointerTracking() {\n if (!pointerListenerFn) return;\n const opts: AddEventListenerOptions = { capture: true };\n window.removeEventListener('pointerenter', pointerListenerFn, opts);\n window.removeEventListener('pointerover', pointerListenerFn, opts);\n window.removeEventListener('mouseenter', pointerListenerFn, opts);\n window.removeEventListener('mouseover', pointerListenerFn, opts);\n pointerListenerFn = null;\n if (pointerWindowTimer) {\n clearTimeout(pointerWindowTimer);\n pointerWindowTimer = null;\n }\n inPointerWindow = false;\n }\n\n // =========================================================================\n // Layer 0b: Element.prototype.animate() intercept\n //\n // PASS-THROUGH by default so entry/scroll/layout animations land where\n // Framer intends them to land (important — `y: [20, 0]` must reach y:0\n // or the element stays stuck 20px off). ONLY inside the pointer-window\n // do we treat the animation as a hover gesture: create then cancel(),\n // which reverts the element to its baseline and kills visible hover\n // zoom/translate effects.\n // =========================================================================\n\n function patchAnimate() {\n if (originalAnimate) return;\n originalAnimate = Element.prototype.animate;\n\n Element.prototype.animate = function (\n this: Element,\n keyframes: Keyframe[] | PropertyIndexedKeyframes | null,\n options?: number | KeyframeAnimationOptions,\n ): Animation {\n if (!enabled) {\n return originalAnimate!.call(this, keyframes, options);\n }\n\n // Widget-internal (our own shadow DOM hover FX) — passthrough always\n if (isWidgetInternal(this)) {\n return originalAnimate!.call(this, keyframes, options);\n }\n\n // Hover gesture window: Framer just created this in response to a\n // pointerenter on a non-widget element. Run original then cancel()\n // to revert to baseline. No persisted zoom.\n if (inPointerWindow) {\n const anim = originalAnimate!.call(this, keyframes, options);\n try { anim.cancel(); } catch { /* ignore */ }\n return anim;\n }\n\n // Everything else (entry, scroll, layout, appear): passthrough.\n // CSS injection already caps CSS animation/transition duration so\n // intrinsic motion is effectively instantaneous; WAAPI entry\n // animations are allowed to run their natural course so elements\n // reach their final positioned state.\n return originalAnimate!.call(this, keyframes, options);\n };\n }\n\n function restoreAnimate() {\n if (originalAnimate) {\n Element.prototype.animate = originalAnimate;\n originalAnimate = null;\n }\n }\n\n // =========================================================================\n // Layer 1: CSS Injection\n // =========================================================================\n\n function injectStyles() {\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = `\n *, *::before, *::after {\n animation-duration: 0.001ms !important;\n animation-iteration-count: 1 !important;\n animation-delay: 0s !important;\n animation-fill-mode: forwards !important;\n transition-duration: 0.001ms !important;\n transition-delay: 0s !important;\n scroll-behavior: auto !important;\n animation-timeline: auto !important;\n scroll-timeline: none !important;\n }\n `;\n document.head.appendChild(style);\n }\n\n function removeStyles() {\n document.getElementById(STYLE_ID)?.remove();\n }\n\n // =========================================================================\n // Layer 2: Web Animations API — finish all WAAPI animations\n // =========================================================================\n\n function finishAllAnimations() {\n try {\n const animations = document.getAnimations?.();\n if (!animations) return;\n for (const anim of animations) {\n try {\n anim.finish();\n } catch {\n try { anim.cancel(); } catch { /* ignore */ }\n }\n }\n } catch { /* getAnimations not supported */ }\n }\n\n // =========================================================================\n // Layer 3: JS animation library detection + inline style freeze\n // =========================================================================\n\n function patchGSAP() {\n const g = (window as any).gsap;\n if (g?.globalTimeline) {\n try {\n g.globalTimeline.timeScale(0);\n (window as any)._a11yGsapPatched = true;\n } catch { /* ignore */ }\n }\n }\n\n function restoreGSAP() {\n const g = (window as any).gsap;\n if ((window as any)._a11yGsapPatched && g?.globalTimeline) {\n try {\n g.globalTimeline.timeScale(1);\n delete (window as any)._a11yGsapPatched;\n } catch { /* ignore */ }\n }\n }\n\n /**\n * Force hidden-by-animation elements visible.\n * Only touches INLINE styles — never sets CSS !important on opacity etc.\n * which would break modals and intentionally hidden elements.\n */\n function forceHiddenElementsVisible() {\n // Only process elements near the viewport to avoid layout chaos on\n // Framer/scroll-animated pages where dozens of off-screen elements\n // are hidden via opacity:0 / translateY as part of scroll reveals.\n const viewH = window.innerHeight;\n const margin = viewH * 1.5; // 1.5x viewport ahead\n\n const all = document.querySelectorAll<HTMLElement>('*');\n for (const el of all) {\n if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE' || el.tagName === 'NOSCRIPT' || el.tagName === 'META' || el.tagName === 'LINK') continue;\n if (el.closest('#accessify-root') || el.closest('accessify-widget')) continue;\n if (el.dataset.a11yOrigStyle) continue; // Already processed\n\n const computed = window.getComputedStyle(el);\n if (computed.display === 'none') continue; // Intentionally hidden\n\n // Skip elements far below the current scroll position —\n // they will be handled when the user scrolls to them (periodic re-scan).\n const rect = el.getBoundingClientRect();\n if (rect.top > viewH + margin) continue; // Too far below viewport\n // Also skip if entirely above viewport (already scrolled past)\n if (rect.bottom < -margin) continue;\n\n const inline = el.style;\n let needsFix = false;\n const originals: Record<string, string> = {};\n\n // Inline opacity 0 or near-0 (computed, not stylesheet)\n const opacityVal = parseFloat(computed.opacity);\n if (opacityVal < 0.1 && inline.opacity !== '') {\n originals.opacity = inline.opacity;\n el.style.opacity = '1';\n needsFix = true;\n } else if (opacityVal < 0.1 && el.hasAttribute('data-framer-appear-id')) {\n // Framer appear elements: opacity set via WAAPI or framework\n originals.opacity = inline.opacity || '';\n el.style.opacity = '1';\n needsFix = true;\n }\n\n // Inline transform with translate that moves element off-screen\n if (inline.transform && /translate[XY]\\([^0]/.test(inline.transform)) {\n originals.transform = inline.transform;\n el.style.transform = 'none';\n needsFix = true;\n }\n\n // Inline visibility hidden\n if (inline.visibility === 'hidden') {\n originals.visibility = inline.visibility;\n el.style.visibility = 'visible';\n needsFix = true;\n }\n\n // Inline clip-path that hides element\n if (inline.clipPath && inline.clipPath !== 'none') {\n originals.clipPath = inline.clipPath;\n el.style.clipPath = 'none';\n needsFix = true;\n }\n\n if (needsFix) {\n el.dataset.a11yOrigStyle = JSON.stringify(originals);\n }\n }\n }\n\n function restoreHiddenElements() {\n document.querySelectorAll<HTMLElement>('[data-a11y-orig-style]').forEach(el => {\n try {\n const orig: Record<string, string> = JSON.parse(el.dataset.a11yOrigStyle || '{}');\n for (const [prop, val] of Object.entries(orig)) {\n if (val === '') {\n el.style.removeProperty(prop);\n } else {\n (el.style as any)[prop] = val;\n }\n }\n } catch { /* malformed JSON */ }\n delete el.dataset.a11yOrigStyle;\n });\n }\n\n /**\n * Freeze inline-animated styles so scroll-handlers can't update them.\n * Takes a snapshot of current inline transforms and enforces them on scroll.\n */\n function freezeScrollAnimations() {\n document.querySelectorAll<HTMLElement>('[style]').forEach(el => {\n if (el.closest('#accessify-root') || el.closest('accessify-widget')) return;\n if (el.dataset.a11yFrozenStyle) return;\n const s = el.style;\n if (s.transform || s.opacity) {\n el.dataset.a11yFrozenStyle = JSON.stringify({\n transform: s.transform || '',\n opacity: s.opacity || '',\n });\n }\n });\n\n if (!scrollHandler) {\n scrollHandler = () => {\n if (!enabled) return;\n document.querySelectorAll<HTMLElement>('[data-a11y-frozen-style]').forEach(el => {\n try {\n const frozen = JSON.parse(el.dataset.a11yFrozenStyle || '{}');\n if (frozen.transform && el.style.transform !== frozen.transform) {\n el.style.transform = frozen.transform;\n }\n } catch { /* ignore */ }\n });\n };\n window.addEventListener('scroll', scrollHandler, { passive: true, capture: true });\n }\n }\n\n function unfreezeScrollAnimations() {\n if (scrollHandler) {\n window.removeEventListener('scroll', scrollHandler, true);\n scrollHandler = null;\n }\n document.querySelectorAll<HTMLElement>('[data-a11y-frozen-style]').forEach(el => {\n delete el.dataset.a11yFrozenStyle;\n });\n }\n\n // =========================================================================\n // Layer 4: Media\n // =========================================================================\n\n function pauseAllVideos() {\n document.querySelectorAll<HTMLVideoElement>('video').forEach(v => {\n if (!v.paused) {\n v.dataset.a11yPaused = 'true';\n v.pause();\n pausedVideos.push(v);\n }\n });\n }\n\n function interceptVideoPlay() {\n if (originalVideoPlay) return;\n originalVideoPlay = HTMLVideoElement.prototype.play;\n HTMLVideoElement.prototype.play = function () {\n if (enabled) return Promise.resolve();\n return originalVideoPlay!.call(this);\n };\n }\n\n function restoreVideoPlay() {\n if (originalVideoPlay) {\n HTMLVideoElement.prototype.play = originalVideoPlay;\n originalVideoPlay = null;\n }\n }\n\n function resumeAllVideos() {\n restoreVideoPlay();\n document.querySelectorAll<HTMLVideoElement>('video[data-a11y-paused]').forEach(v => {\n try { v.play(); } catch { /* gone */ }\n delete v.dataset.a11yPaused;\n });\n pausedVideos = [];\n }\n\n function freezeGif(img: HTMLImageElement) {\n const src = (img.currentSrc || img.src || '').toLowerCase();\n if (!src.match(/\\.gif(\\?|$)/i)) return;\n if (gifOriginals.has(img)) return;\n\n const doFreeze = () => {\n try {\n const w = img.naturalWidth || img.width;\n const h = img.naturalHeight || img.height;\n if (w === 0 || h === 0) return;\n const c = document.createElement('canvas');\n c.width = w; c.height = h;\n const ctx = c.getContext('2d');\n if (!ctx) return;\n ctx.drawImage(img, 0, 0);\n gifOriginals.set(img, img.src);\n img.src = c.toDataURL('image/png');\n } catch { /* CORS */ }\n };\n\n if (img.complete && img.naturalWidth > 0) doFreeze();\n else img.addEventListener('load', doFreeze, { once: true });\n }\n\n function freezeAllGifs() {\n document.querySelectorAll<HTMLImageElement>('img').forEach(freezeGif);\n }\n\n function restoreAllGifs() {\n for (const [img, src] of gifOriginals) {\n try { img.src = src; } catch { /* gone */ }\n }\n gifOriginals.clear();\n }\n\n function pauseSVG() {\n document.querySelectorAll('svg').forEach(s => {\n try { (s as any).pauseAnimations?.(); } catch { /* ignore */ }\n });\n }\n\n function resumeSVG() {\n document.querySelectorAll('svg').forEach(s => {\n try { (s as any).unpauseAnimations?.(); } catch { /* ignore */ }\n });\n }\n\n function stopMarquees() {\n document.querySelectorAll('marquee').forEach(m => {\n try { (m as any).stop?.(); } catch { /* ignore */ }\n });\n }\n\n function startMarquees() {\n document.querySelectorAll('marquee').forEach(m => {\n try { (m as any).start?.(); } catch { /* ignore */ }\n });\n }\n\n // =========================================================================\n // MutationObserver — dynamic content\n // =========================================================================\n\n function setupObserver() {\n if (mutationObserver) return;\n mutationObserver = new MutationObserver(mutations => {\n if (!enabled) return;\n let hasNewContent = false;\n for (const m of mutations) {\n if (m.addedNodes.length > 0) { hasNewContent = true; break; }\n }\n if (hasNewContent) {\n finishAllAnimations();\n forceHiddenElementsVisible();\n pauseAllVideos();\n freezeAllGifs();\n pauseSVG();\n }\n });\n mutationObserver.observe(document.body, { childList: true, subtree: true });\n }\n\n function teardownObserver() {\n mutationObserver?.disconnect();\n mutationObserver = null;\n }\n\n // =========================================================================\n // Periodic re-scan — frameworks create new animations constantly\n // =========================================================================\n\n function startPeriodicScan() {\n if (scanInterval) return;\n scanInterval = setInterval(() => {\n if (!enabled) return;\n finishAllAnimations();\n }, 500);\n }\n\n function stopPeriodicScan() {\n if (scanInterval) {\n clearInterval(scanInterval);\n scanInterval = null;\n }\n }\n\n // =========================================================================\n // Lifecycle\n // =========================================================================\n\n function activate() {\n if (enabled) return;\n enabled = true;\n\n // Layer 0a: Track pointer-window so patchAnimate() can classify hover vs entry\n startPointerTracking();\n\n // Layer 0b: Intercept Element.animate() — only cancels animations created\n // inside the pointer-window (= Framer whileHover). Entry animations pass through.\n patchAnimate();\n\n // Layer 1: CSS\n injectStyles();\n\n // Layer 2: Kill all currently running WAAPI animations\n finishAllAnimations();\n\n // Layer 3: JS libs + inline style fixes\n patchGSAP();\n forceHiddenElementsVisible();\n freezeScrollAnimations();\n\n // Layer 4: Media\n pauseAllVideos();\n interceptVideoPlay();\n freezeAllGifs();\n pauseSVG();\n stopMarquees();\n\n // Observers\n setupObserver();\n startPeriodicScan();\n\n // Delayed re-scans for async rendering frameworks\n for (const delay of [50, 200, 500, 1000, 2000, 4000]) {\n setTimeout(() => {\n if (!enabled) return;\n finishAllAnimations();\n forceHiddenElementsVisible();\n freezeScrollAnimations();\n pauseAllVideos();\n freezeAllGifs();\n }, delay);\n }\n\n localStorage.setItem(STORAGE_KEY, 'true');\n }\n\n function deactivate() {\n enabled = false;\n\n stopPeriodicScan();\n teardownObserver();\n restoreAnimate();\n stopPointerTracking();\n unfreezeScrollAnimations();\n restoreGSAP();\n restoreHiddenElements();\n startMarquees();\n resumeSVG();\n restoreAllGifs();\n resumeAllVideos();\n removeStyles();\n\n localStorage.removeItem(STORAGE_KEY);\n }\n\n return {\n id: 'animation-stop',\n name: () => 'Stop Animations',\n description: 'Pause all animations, transitions, and auto-playing videos (WCAG 2.3.1)',\n icon: 'animation-stop',\n category: 'visual',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'animation-stop', enabled }),\n setState: (state: { enabled: boolean }) => {\n if (state.enabled) activate();\n else deactivate();\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -6620,14 +6620,14 @@ function FeatureGrid($$anchor, $$props) {
6620
6620
  const FEATURE_LOADERS = {
6621
6621
  contrast: () => import("./contrast-CqsOs6Uo.js"),
6622
6622
  "text-size": () => import("./text-size-m_mHNPWo.js"),
6623
- "keyboard-nav": () => import("./keyboard-nav-qawWjwpG.js"),
6623
+ "keyboard-nav": () => import("./keyboard-nav-B57Xk_1n.js"),
6624
6624
  "link-highlight": () => import("./link-highlight-DBGm067Y.js"),
6625
6625
  "reading-guide": () => import("./reading-guide-VT8NciIL.js"),
6626
6626
  "reading-mask": () => import("./reading-mask-BABChuCz.js"),
6627
- "animation-stop": () => import("./animation-stop-_chC8bg1.js"),
6627
+ "animation-stop": () => import("./animation-stop-Bv6xRU2q.js"),
6628
6628
  "hide-images": () => import("./hide-images-B_LeCBcd.js"),
6629
6629
  "big-cursor": () => import("./big-cursor-B2UKu9dQ.js"),
6630
- "page-structure": () => import("./page-structure-BRbpzW6b.js"),
6630
+ "page-structure": () => import("./page-structure-Dnpn3tuh.js"),
6631
6631
  tts: () => import("./tts-CjszLRnb.js"),
6632
6632
  "text-simplify": () => import("./text-simplify-B0hIi6bW.js"),
6633
6633
  "alt-text": () => Promise.resolve().then(() => altText)
@@ -9030,4 +9030,4 @@ export {
9030
9030
  init as i,
9031
9031
  t
9032
9032
  };
9033
- //# sourceMappingURL=index-DMc-PMDI.js.map
9033
+ //# sourceMappingURL=index-Bz63wH7t.js.map