{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "theme-adapt",
  "title": "Adaptive Theme Switch",
  "description": "Light/dark switch that keeps brand canvases and lifts type that would vanish in the other mode.",
  "dependencies": [
    "next-themes"
  ],
  "registryDependencies": [
    "@atroui/utils",
    "@atroui/adaptive-theme"
  ],
  "files": [
    {
      "path": "registry/default/ui/theme-adapt.tsx",
      "content": "\"use client\"\n\nimport { useTheme } from \"next-themes\"\nimport { useLayoutEffect, useState } from \"react\"\n\nimport {\n  applyCompanionPalette,\n  clampContrast,\n  clearCompanionPalette,\n  companionDark,\n  sampleLightSwatch,\n} from \"@/lib/adaptive-theme\"\nimport { cn } from \"@/lib/utils\"\n\nfunction syncCompanion(adapt: boolean, dark: boolean, floor: number) {\n  const root = document.documentElement\n  if (!adapt || !dark) {\n    clearCompanionPalette(root)\n    return false\n  }\n  const light = sampleLightSwatch()\n  if (!light) return false\n  applyCompanionPalette(root, companionDark(light, \"adaptive\", floor))\n  return true\n}\n\n/**\n * Adaptive light/dark switch.\n * DAY and NIGHT are separate choices. Night applies an OKLCH companion\n * from light :root tokens before paint.\n */\nexport function ThemeAdapt({\n  className,\n  adapt = true,\n  minContrast = 4.5,\n}: {\n  className?: string\n  /** When true, night mode uses a generated companion instead of only .dark. */\n  adapt?: boolean\n  minContrast?: number\n}) {\n  const { resolvedTheme, setTheme } = useTheme()\n  const [mounted, setMounted] = useState(false)\n  const [companionOn, setCompanionOn] = useState(false)\n  const floor = clampContrast(minContrast)\n  const isDark = mounted && resolvedTheme === \"dark\"\n\n  useLayoutEffect(() => {\n    setMounted(true)\n  }, [])\n\n  useLayoutEffect(() => {\n    if (!mounted) return\n    const dark = document.documentElement.classList.contains(\"dark\")\n    setCompanionOn(syncCompanion(adapt, dark, floor))\n    const obs = new MutationObserver(() => {\n      const nowDark = document.documentElement.classList.contains(\"dark\")\n      setCompanionOn(syncCompanion(adapt, nowDark, floor))\n    })\n    obs.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [\"class\"],\n    })\n    return () => obs.disconnect()\n  }, [adapt, floor, mounted, resolvedTheme])\n\n  const choose = (mode: \"light\" | \"dark\") => {\n    if (mode === \"dark\") {\n      const light = sampleLightSwatch()\n      setTheme(\"dark\")\n      if (adapt && light) {\n        applyCompanionPalette(\n          document.documentElement,\n          companionDark(light, \"adaptive\", floor)\n        )\n        setCompanionOn(true)\n      }\n      return\n    }\n    clearCompanionPalette(document.documentElement)\n    setCompanionOn(false)\n    setTheme(\"light\")\n  }\n\n  return (\n    <div className={cn(\"inline-flex flex-col items-stretch gap-1\", className)}>\n      <div\n        role=\"radiogroup\"\n        aria-label=\"Appearance\"\n        className=\"relative isolate flex h-9 w-40 overflow-hidden rounded-lg border border-border-subtle\"\n      >\n        <button\n          type=\"button\"\n          role=\"radio\"\n          aria-checked={!isDark}\n          aria-label=\"Day mode\"\n          onClick={() => choose(\"light\")}\n          className={cn(\n            \"flex w-1/2 items-center justify-center bg-[#efeae1] text-[#161412]\",\n            \"outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand/50\"\n          )}\n        >\n          <span className=\"font-mono text-[10px] font-medium tracking-[0.1em]\">\n            DAY\n          </span>\n        </button>\n        <button\n          type=\"button\"\n          role=\"radio\"\n          aria-checked={isDark}\n          aria-label=\"Night mode, companion palette from light tokens\"\n          onClick={() => choose(\"dark\")}\n          className={cn(\n            \"flex w-1/2 items-center justify-center bg-[#0e0e0f] text-[#eceae4]\",\n            \"outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand/50\"\n          )}\n        >\n          <span className=\"font-mono text-[10px] font-medium tracking-[0.1em]\">\n            NIGHT\n          </span>\n        </button>\n        {mounted ? (\n          <span\n            aria-hidden\n            className={cn(\n              \"pointer-events-none absolute top-px bottom-px w-[calc(50%-2px)] rounded-md\",\n              \"ring-1 ring-brand/80\",\n              \"transition-[left] duration-300 ease-[cubic-bezier(0.32,0.72,0,1)]\",\n              \"motion-reduce:transition-none\"\n            )}\n            style={{ left: isDark ? \"calc(50% + 1px)\" : \"1px\" }}\n          />\n        ) : null}\n      </div>\n      {companionOn ? (\n        <p className=\"max-w-40 text-[11px] leading-snug text-muted-foreground\">\n          Companion from light tokens.\n        </p>\n      ) : null}\n    </div>\n  )\n}\n",
      "type": "registry:ui",
      "target": "components/ui/theme-adapt.tsx"
    }
  ],
  "type": "registry:ui"
}