Organize/components/theme/use-dark-theme.ts

48 lines
1.6 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { useTheme, resolveTheme } from "./theme-provider";
// The themes whose *surfaces* are dark (see app/globals.css): "dark" and
// "ocean". Anything that used to compare `resolvedTheme === "dark"` to pick
// a color variant (group card fills/strokes, markdown widget color modes,
// ...) uses this instead, so Ocean is treated exactly like Dark.
const DARK_SURFACES = new Set(["dark", "ocean"]);
function useMounted(): boolean {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return mounted;
}
/**
* True when the active theme has dark surfaces (Dark or Ocean).
*
* Reactive to UI theme switches: the value is derived from the provider's
* theme state (the single source of truth), so the moment `setTheme` fires,
* every consumer re-renders with the new variant in the same commit -- no
* stale card colors, no manual refresh needed.
*
* Hydration-safe: until mounted we always report "light", matching the
* server render (the no-FOUC script in app/layout.tsx covers the CSS side
* of that first frame).
*/
export function isDarkTheme(): boolean {
const mounted = useMounted();
const { theme } = useTheme();
if (!mounted) return false;
return DARK_SURFACES.has(resolveTheme(theme));
}
/**
* The `data-color-mode` value the markdown editor/preview widgets expect.
* They only understand light/dark, so the light "sunset" look reports as
* light and the dark "ocean" look reports as dark.
*/
export function colorModeFromTheme(): "light" | "dark" {
return isDarkTheme() ? "dark" : "light";
}