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}