2026-07-05 15:50:37 -05:00
|
|
|
import { createContext, useContext, useState, type ReactNode } from 'react';
|
|
|
|
|
import { MotionConfig } from 'framer-motion';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* "Reduce motion" device preference, persisted to localStorage under
|
|
|
|
|
* 'bt-reduce-motion'. When ON, framer-motion animations are forced off
|
|
|
|
|
* (`reducedMotion="always"`); when OFF we pass "user" so the OS
|
|
|
|
|
* `prefers-reduced-motion` setting is still honored.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const STORAGE_KEY = 'bt-reduce-motion';
|
|
|
|
|
|
|
|
|
|
function loadPref(): boolean {
|
|
|
|
|
try {
|
|
|
|
|
return localStorage.getItem(STORAGE_KEY) === 'true';
|
|
|
|
|
} catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface MotionPreferenceValue {
|
|
|
|
|
reduceMotion: boolean;
|
|
|
|
|
setReduceMotion: (v: boolean) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MotionPreferenceContext = createContext<MotionPreferenceValue | null>(null);
|
|
|
|
|
|
|
|
|
|
export function MotionPreferenceProvider({ children }: { children: ReactNode }) {
|
|
|
|
|
const [reduceMotion, setReduceMotionState] = useState<boolean>(loadPref);
|
|
|
|
|
|
|
|
|
|
const setReduceMotion = (v: boolean) => {
|
|
|
|
|
setReduceMotionState(v);
|
|
|
|
|
try {
|
|
|
|
|
localStorage.setItem(STORAGE_KEY, String(v));
|
|
|
|
|
} catch {
|
|
|
|
|
// localStorage unavailable
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<MotionPreferenceContext.Provider value={{ reduceMotion, setReduceMotion }}>
|
2026-07-06 14:23:53 -05:00
|
|
|
<MotionConfig reducedMotion={reduceMotion ? 'always' : 'user'}>{children}</MotionConfig>
|
2026-07-05 15:50:37 -05:00
|
|
|
</MotionPreferenceContext.Provider>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useMotionPreference() {
|
|
|
|
|
const ctx = useContext(MotionPreferenceContext);
|
|
|
|
|
if (!ctx) {
|
|
|
|
|
throw new Error('useMotionPreference must be used within a MotionPreferenceProvider');
|
|
|
|
|
}
|
|
|
|
|
return ctx;
|
|
|
|
|
}
|