A React component library for rendering common AT Protocol records for applications such as Bluesky and Leaflet.
1import { useEffect, useState } from 'react'; 2 3/** 4 * Possible user-facing color scheme preferences. 5 */ 6export type ColorSchemePreference = 'light' | 'dark' | 'system'; 7 8const MEDIA_QUERY = '(prefers-color-scheme: dark)'; 9 10/** 11 * Resolves a persisted preference into an explicit light/dark value. 12 * 13 * @param pref - Stored preference value (`light`, `dark`, or `system`). 14 * @returns Explicit light/dark scheme suitable for rendering. 15 */ 16function resolveScheme(pref: ColorSchemePreference): 'light' | 'dark' { 17 if (pref === 'light' || pref === 'dark') return pref; 18 if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { 19 return 'light'; 20 } 21 return window.matchMedia(MEDIA_QUERY).matches ? 'dark' : 'light'; 22} 23 24/** 25 * React hook that returns the effective light/dark scheme, respecting system preferences. 26 * 27 * @param preference - User preference; defaults to following the OS setting. 28 * @returns {'light' | 'dark'} Explicit scheme that should be used for rendering. 29 */ 30export function useColorScheme(preference: ColorSchemePreference = 'system'): 'light' | 'dark' { 31 const [scheme, setScheme] = useState<'light' | 'dark'>(() => resolveScheme(preference)); 32 33 useEffect(() => { 34 if (preference === 'light' || preference === 'dark') { 35 setScheme(preference); 36 return; 37 } 38 if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { 39 setScheme('light'); 40 return; 41 } 42 const media = window.matchMedia(MEDIA_QUERY); 43 const update = (event: MediaQueryListEvent | MediaQueryList) => { 44 setScheme(event.matches ? 'dark' : 'light'); 45 }; 46 update(media); 47 if (typeof media.addEventListener === 'function') { 48 media.addEventListener('change', update); 49 return () => media.removeEventListener('change', update); 50 } 51 media.addListener(update); 52 return () => media.removeListener(update); 53 }, [preference]); 54 55 return scheme; 56}