The Coline design system: accessible React components for Coline Apps, themed live by the host workspace, and for Coline Sites in light and dark. Tailwind v4 stylesheet included. MIT.
| 5 | /** |
| 6 | * The provider for Coline Sites (SDK 2.1). A site has no Coline host to |
| 7 | * hand it a theme, so this follows the visitor's light or dark preference |
| 8 | * (or a fixed `theme`) by toggling `.dark` on the root element, which is |
| 9 | * how every component's tokens switch. Wrap the site's page in it once, |
| 10 | * instead of `ColineAppProvider`, which only works inside Coline. |
| 11 | */ |
| 12 |
| 13 | export type ColineSiteTheme = "system" | "light" | "dark"; |
| 14 |
| 15 | const SiteColorSchemeContext = React.createContext<"light" | "dark">("light"); |
| 16 |
| 17 | function systemScheme(): "light" | "dark" { |
| 18 | return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; |
| 19 | } |
| 20 |
| 21 | export function ColineSiteProvider({ |
| 22 | theme = "system", |
| 23 | children, |
| 24 | }: { |
| 25 | /** `system` (the default) follows the visitor's device and changes with it. */ |
| 26 | theme?: ColineSiteTheme; |
| 27 | children: React.ReactNode; |
| 28 | }) { |
| 29 | const [system, setSystem] = React.useState<"light" | "dark">(systemScheme); |
| 30 | React.useEffect(() => { |
| 31 | if (theme !== "system") return; |
| 32 | const media = window.matchMedia("(prefers-color-scheme: dark)"); |
| 33 | const update = () => setSystem(media.matches ? "dark" : "light"); |
| 34 | update(); |
| 35 | media.addEventListener("change", update); |
| 36 | return () => media.removeEventListener("change", update); |
| 37 | }, [theme]); |
| 38 | const scheme = theme === "system" ? system : theme; |
| 39 | React.useLayoutEffect(() => { |
| 40 | const root = document.documentElement; |
| 41 | root.classList.toggle("dark", scheme === "dark"); |
| 42 | root.style.colorScheme = scheme; |
| 43 | }, [scheme]); |
| 44 | return ( |
| 45 | <SiteColorSchemeContext.Provider value={scheme}> |
| 46 | <MotionConfig reducedMotion="user">{children}</MotionConfig> |
| 47 | </SiteColorSchemeContext.Provider> |
| 48 | ); |
| 49 | } |
| 50 |
| 51 | /** The light or dark scheme a site is showing. */ |
| 52 | export function useSiteColorScheme(): "light" | "dark" { |
| 53 | return React.useContext(SiteColorSchemeContext); |
| 54 | } |
| 55 |