Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | 12x 12x 16x 16x 16x 8x 8x 8x 8x 1x 8x 8x 8x 16x 14x 14x 3x 11x 16x 2x 16x 16x 16x 12x 17x 17x 2x 15x | /* eslint-disable react-refresh/only-export-components */
/**
* AccessibilityProvider - Context provider for accessibility settings
*
* Provides high contrast mode, reduced motion support, and other accessibility features
* Respects user preferences from prefers-reduced-motion and prefers-contrast media queries
*
* @module components/base
*/
import React, {
createContext,
useContext,
useState,
useEffect,
useMemo,
useCallback,
} from "react";
/**
* Accessibility context value interface
*/
export interface AccessibilityContextValue {
readonly highContrast: boolean;
readonly reducedMotion: boolean;
readonly setHighContrast: (enabled: boolean) => void;
readonly toggleHighContrast: () => void;
}
/**
* Accessibility context
*/
const AccessibilityContext = createContext<AccessibilityContextValue | null>(
null,
);
/**
* Props for AccessibilityProvider
*/
export interface AccessibilityProviderProps {
readonly children: React.ReactNode;
}
/**
* AccessibilityProvider Component
*
* Provides accessibility settings throughout the component tree
* Automatically detects user preferences for reduced motion
*
* @example
* ```tsx
* <AccessibilityProvider>
* <App />
* </AccessibilityProvider>
* ```
*/
export const AccessibilityProvider: React.FC<AccessibilityProviderProps> = ({
children,
}) => {
const [highContrast, setHighContrast] = useState(false);
const [reducedMotion, setReducedMotion] = useState(false);
useEffect(() => {
Iif (typeof window === "undefined") return;
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
setReducedMotion(mediaQuery.matches);
const handleChange = (e: MediaQueryListEvent) => {
setReducedMotion(e.matches);
};
mediaQuery.addEventListener("change", handleChange);
return () => {
mediaQuery.removeEventListener("change", handleChange);
};
}, []);
useEffect(() => {
Iif (typeof document === "undefined") return;
if (highContrast) {
document.body.classList.add("high-contrast");
} else {
document.body.classList.remove("high-contrast");
}
}, [highContrast]);
const toggleHighContrast = useCallback(() => {
setHighContrast((prev) => !prev);
}, []);
const value = useMemo<AccessibilityContextValue>(
() => ({
highContrast,
reducedMotion,
setHighContrast,
toggleHighContrast,
}),
[highContrast, reducedMotion, toggleHighContrast],
);
return (
<AccessibilityContext.Provider value={value}>
{children}
</AccessibilityContext.Provider>
);
};
AccessibilityProvider.displayName = "AccessibilityProvider";
/**
* Custom hook to access accessibility context
*
* @throws Error if used outside of AccessibilityProvider
* @returns Accessibility context value
*
* @example
* ```tsx
* const { highContrast, reducedMotion, toggleHighContrast } = useAccessibility();
* ```
*/
export function useAccessibility(): AccessibilityContextValue {
const context = useContext(AccessibilityContext);
if (!context) {
throw new Error(
"useAccessibility must be used within AccessibilityProvider",
);
}
return context;
}
|