File size: 2,141 Bytes
03677d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import {createContext, useContext, useEffect, useState} from 'react';
import type {ReactNode} from 'react';

const MotionContext = createContext({enabled:true, reduced:false, paused:false, toggle:()=>{}});

/** A single preference governs ambient movement, tours and archived playback. */
export function MotionProvider({children}:{children:ReactNode}) {
 const [hidden,setHidden]=useState(document.hidden);
 const [reduced,setReduced]=useState(()=>window.matchMedia('(prefers-reduced-motion: reduce)').matches);
 const [paused,setPaused]=useState(()=>{try{return sessionStorage.getItem('ouroboros-motion-paused')==='true';}catch{return false;}});
 useEffect(()=>{
  const visibility=()=>setHidden(document.hidden);
  document.addEventListener('visibilitychange',visibility);
  return()=>document.removeEventListener('visibilitychange',visibility);
 },[]);
 useEffect(()=>{
  const query=window.matchMedia('(prefers-reduced-motion: reduce)');
  const update=()=>setReduced(query.matches);
  query.addEventListener('change',update);
  return()=>query.removeEventListener('change',update);
 },[]);
 useEffect(()=>{
  document.documentElement.dataset.motion=paused||reduced||hidden?'paused':'running';
  try{sessionStorage.setItem('ouroboros-motion-paused',String(paused));}catch{/* Preferences still work when storage is unavailable. */}
 },[paused,reduced,hidden]);
 return <MotionContext.Provider value={{enabled:!paused&&!reduced&&!hidden,reduced,paused,toggle:()=>setPaused(value=>!value)}}>{children}</MotionContext.Provider>;
}
export function useMotion(){return useContext(MotionContext);}
export function MotionControl(){
 const {enabled,reduced,toggle}=useMotion();
 return <button className="motion-control" type="button" onClick={toggle} disabled={reduced} aria-label={reduced?'Animations off: reduced motion preference':enabled?'Pause page animations':'Resume page animations'} aria-pressed={!enabled} title={reduced?'Following your reduced motion preference':enabled?'Pause page animations':'Resume page animations'}><span aria-hidden="true">{enabled?'Ⅱ':'▷'}</span><span>{enabled?'Motion on':'Motion off'}</span></button>;
}