State

useTernaryDarkMode

Three-state dark mode — system, dark or light — persisted.

Demo

Resolved: light

“system” follows your OS setting — the other two force a mode.

ternaryDarkMode
system
isDarkMode
false

The choice persists — reload and the panel keeps its mode.

Usage

use-ternary-dark-mode.tsxtsx
import { useTernaryDarkMode } from "hookli";

export function Demo() {
  const { isDarkMode, ternaryDarkMode, setTernaryDarkMode } =
    useTernaryDarkMode();

  return (
    <div className={isDarkMode ? "panel-dark" : "panel-light"}>
      <select
        value={ternaryDarkMode}
        onChange={(e) => setTernaryDarkMode(e.target.value)}
      >
        <option value="light">Light</option>
        <option value="system">System</option>
        <option value="dark">Dark</option>
      </select>
    </div>
  );
}

API

useTernaryDarkMode.d.tsts
useTernaryDarkMode(options?: UseTernaryDarkModeOptions): UseTernaryDarkModeReturn

Parameters

NameTypeDefaultDescription
optionsUseTernaryDarkModeOptions{}Optional starting mode and localStorage key.

Returns

NameTypeDescription
{ … }UseTernaryDarkModeReturnThe resolved isDarkMode boolean, the stored ternary preference, and its setters.

TernaryDarkMode

The three possible preferences.

NameTypeDescription
value"system" | "dark" | "light""system" resolves against the OS; "dark"/"light" force a mode.

UseTernaryDarkModeOptions

Optional starting mode and persistence key.

NameTypeDescription
defaultValueTernaryDarkModeThe mode before anything is stored: "system", "dark" or "light".
localStorageKeystringThe localStorage key the choice is persisted under.

UseTernaryDarkModeReturn

The resolved mode plus setters.

NameTypeDescription
isDarkModebooleanTrue when the mode is "dark", or "system" while the OS prefers dark.
ternaryDarkModeTernaryDarkModeThe stored preference: "system" | "dark" | "light".
setTernaryDarkMode(value: TernaryDarkMode | ((prev: TernaryDarkMode) => TernaryDarkMode)) => voidSet the preference directly; accepts a value or an updater.
toggleTernaryDarkMode() => voidCycle the preference: light → system → dark → light.

Hook

The implementation, straight from hookli. Copy it, or install the package.

src/hooks/use-ternary-dark-mode/use-ternary-dark-mode.tstsx
import { useCallback } from "react";
import { useLocalStorage } from "../use-local-storage/use-local-storage";
import { useMediaQuery } from "../use-media-query/use-media-query";

type TernaryDarkMode = "system" | "dark" | "light";

interface UseTernaryDarkModeOptions {
  defaultValue?: TernaryDarkMode;
  localStorageKey?: string;
}

interface UseTernaryDarkModeReturn {
  isDarkMode: boolean;
  ternaryDarkMode: TernaryDarkMode;
  setTernaryDarkMode: (
    value: TernaryDarkMode | ((prev: TernaryDarkMode) => TernaryDarkMode),
  ) => void;
  toggleTernaryDarkMode: () => void;
}

const COLOR_SCHEME_QUERY = "(prefers-color-scheme: dark)";
const DEFAULT_STORAGE_KEY = "hookli-ternary-dark-mode";

export function useTernaryDarkMode(
  options: UseTernaryDarkModeOptions = {},
): UseTernaryDarkModeReturn {
  const { defaultValue = "system", localStorageKey = DEFAULT_STORAGE_KEY } =
    options;
  const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY);
  const { value: ternaryDarkMode, setStoredValue: setTernaryDarkMode } =
    useLocalStorage<TernaryDarkMode>(localStorageKey, defaultValue);

  const isDarkMode =
    ternaryDarkMode === "dark" || (ternaryDarkMode === "system" && isDarkOS);

  const toggleTernaryDarkMode = useCallback(() => {
    const cycle: TernaryDarkMode[] = ["light", "system", "dark"];
    setTernaryDarkMode((prev) => {
      const nextIndex = (cycle.indexOf(prev) + 1) % cycle.length;
      return cycle[nextIndex];
    });
  }, [setTernaryDarkMode]);

  return {
    isDarkMode,
    ternaryDarkMode,
    setTernaryDarkMode,
    toggleTernaryDarkMode,
  };
}

View source on GitHub

Related hooks