/** * JavaScript-side representation of the same color tokens NativeWind * exposes as CSS variables in `styles/globals.css`. Components that need * a palette object (RN-Web inline styles, SVG `stroke`, etc.) consume * this so the values stay in lockstep with the className-based theming. * * Always read via `usePalette()` (or `getPalette(scheme)` for non-hook * call sites) — the table mirrors `:root` for light and `.dark` for * dark. */ import { useMemo } from "react"; import { useColorScheme } from "nativewind"; export interface Palette { background: string; foreground: string; card: string; cardForeground: string; popover: string; popoverForeground: string; primary: string; primaryForeground: string; secondary: string; secondaryForeground: string; muted: string; mutedForeground: string; accent: string; accentForeground: string; destructive: string; destructiveForeground: string; border: string; input: string; inputBorder: string; ring: string; placeholder: string; } export const lightPalette: Palette = { background: "rgb(255, 255, 255)", foreground: "rgb(0, 0, 0)", card: "rgb(255, 255, 255)", cardForeground: "rgb(0, 0, 0)", popover: "rgb(255, 255, 255)", popoverForeground: "rgb(0, 0, 0)", primary: "rgb(0, 0, 0)", primaryForeground: "rgb(255, 255, 255)", secondary: "rgb(245, 245, 245)", secondaryForeground: "rgb(0, 0, 0)", muted: "rgb(245, 245, 245)", mutedForeground: "rgb(115, 115, 115)", accent: "rgb(245, 245, 245)", accentForeground: "rgb(0, 0, 0)", destructive: "rgb(239, 68, 68)", destructiveForeground: "rgb(255, 255, 255)", border: "rgb(229, 229, 229)", input: "rgb(255, 255, 255)", inputBorder: "rgb(229, 229, 229)", ring: "rgb(0, 0, 0)", placeholder: "rgb(163, 163, 163)", }; export const darkPalette: Palette = { background: "rgb(0, 0, 0)", foreground: "rgb(255, 255, 255)", card: "rgb(0, 0, 0)", cardForeground: "rgb(255, 255, 255)", popover: "rgb(0, 0, 0)", popoverForeground: "rgb(255, 255, 255)", primary: "rgb(255, 255, 255)", primaryForeground: "rgb(0, 0, 0)", secondary: "rgb(38, 38, 38)", secondaryForeground: "rgb(255, 255, 255)", muted: "rgb(38, 38, 38)", mutedForeground: "rgb(163, 163, 163)", accent: "rgb(38, 38, 38)", accentForeground: "rgb(255, 255, 255)", destructive: "rgb(127, 29, 29)", destructiveForeground: "rgb(255, 255, 255)", border: "rgb(38, 38, 38)", input: "rgb(0, 0, 0)", inputBorder: "rgb(38, 38, 38)", ring: "rgb(255, 255, 255)", placeholder: "rgb(115, 115, 115)", }; export type ColorScheme = "light" | "dark"; export function getPalette(scheme: ColorScheme): Palette { return scheme === "dark" ? darkPalette : lightPalette; } /** * Returns the active palette + scheme. Re-renders the caller when the * OS / class-toggled theme flips. */ export function usePalette(): { scheme: ColorScheme; palette: Palette } { const { colorScheme } = useColorScheme(); const scheme: ColorScheme = colorScheme === "dark" ? "dark" : "light"; // Memoize the wrapper so callers using the result as a useMemo / // useCallback dep see a stable reference until the scheme actually // changes. The contained `palette` is already a module-level cached // singleton; only the wrapper object was being re-created. return useMemo(() => ({ scheme, palette: getPalette(scheme) }), [scheme]); }