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 136 137 138 139 140 | 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);
// Detect user preference for reduced motion
useEffect(() => {
Iif (typeof window === "undefined") return;
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
setReducedMotion(mediaQuery.matches);
const handleChange = (e: MediaQueryListEvent) => {
setReducedMotion(e.matches);
};
// Modern browsers
mediaQuery.addEventListener("change", handleChange);
return () => {
mediaQuery.removeEventListener("change", handleChange);
};
}, []);
// Apply high contrast theme globally
useEffect(() => {
Iif (typeof document === "undefined") return;
if (highContrast) {
document.body.classList.add("high-contrast");
} else {
document.body.classList.remove("high-contrast");
}
}, [highContrast]);
// Toggle high contrast mode
const toggleHighContrast = useCallback(() => {
setHighContrast((prev) => !prev);
}, []);
// Memoize context value to prevent unnecessary re-renders
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;
}
|