{"$schema":"https://ui.shadcn.com/schema/registry.json","author":"Kaiyu Hsu <uicapsule@kyh.io>","categories":["controls","minimal"],"dependencies":["motion"],"description":"Sun/moon light-dark theme toggle icon — the moon's crescent mask slides away as it shrinks into a sun, the icon rotates, and six rays pop in one by one.","devDependencies":[],"files":[{"content":"\"use client\";\n\nimport { useId } from \"react\";\nimport { motion } from \"motion/react\";\n\n/**\n * The six sun rays, in the order they pop in when switching to light.\n * Positions are the vertices of a hexagon inscribed at r=8 around (9, 9);\n * `delay` is written out literally rather than derived from the index so the\n * stagger ramp stays free of floating-point drift.\n */\nconst SUN_RAYS = [\n  { cx: 17, cy: 9, delay: 0 },\n  { cx: 13, cy: 15.928203230275509, delay: 0.05 },\n  { cx: 5.000000000000002, cy: 15.92820323027551, delay: 0.1 },\n  { cx: 1, cy: 9.000000000000002, delay: 0.15 },\n  { cx: 4.9999999999999964, cy: 2.071796769724492, delay: 0.2 },\n  { cx: 13, cy: 2.0717967697244912, delay: 0.25 },\n] as const;\n\nconst RAY_RADIUS = 1.5;\n\ninterface LightDarkToggleProps {\n  isLight: boolean;\n}\n\nexport const LightDarkToggle = ({ isLight }: LightDarkToggleProps) => {\n  // Scoped so several toggles can coexist on one page without their masks colliding.\n  const maskId = `moon-mask-${useId().replaceAll(/[^a-zA-Z0-9]/gu, \"\")}`;\n\n  return (\n    <svg\n      viewBox=\"0 0 18 18\"\n      style={{ transform: isLight ? \"rotate(90deg)\" : \"rotate(40deg)\" }}\n      className=\"size-6 overflow-visible transition-transform duration-500\"\n    >\n      <mask id={maskId}>\n        <rect x=\"0\" y=\"0\" width=\"18\" height=\"18\" fill=\"#FFF\" />\n        <motion.circle animate={{ cx: isLight ? 25 : 10 }} cy=\"2\" r=\"8\" fill=\"black\" />\n      </mask>\n      <motion.circle\n        cx=\"9\"\n        cy=\"9\"\n        fill=\"currentColor\"\n        mask={`url(#${maskId})`}\n        animate={{ r: isLight ? 5 : 8 }}\n      />\n      <g>\n        {SUN_RAYS.map((ray) => (\n          <motion.circle\n            key={`${ray.cx}-${ray.cy}`}\n            cx={ray.cx}\n            cy={ray.cy}\n            r={RAY_RADIUS}\n            fill=\"currentColor\"\n            animate={{ scale: isLight ? 1 : 0 }}\n            transition={{ delay: isLight ? ray.delay : 0 }}\n          />\n        ))}\n      </g>\n    </svg>\n  );\n};\n","path":"/light-dark-toggle.tsx","target":"uicapsule/light-dark-toggle/light-dark-toggle.tsx","type":"registry:file"},{"content":"\"use client\";\n\nimport { useState } from \"react\";\n\nimport { LightDarkToggle } from \"./light-dark-toggle\";\n\nconst Preview = () => {\n  const [isLight, setIsLight] = useState(true);\n\n  return (\n    <div\n      className={`main flex h-dvh items-center justify-center ${isLight ? \"bg-stone-300\" : \"bg-stone-900\"}`}\n    >\n      <button\n        type=\"button\"\n        aria-label={isLight ? \"Switch to dark mode\" : \"Switch to light mode\"}\n        className={`grid size-12 place-items-center rounded-xl bg-gradient-to-t shadow-lg ${isLight ? \"from-[#f8fafc] to-[#f1f5f9] text-stone-950\" : \"from-[#020617] to-[#0F172A] text-stone-50\"}`}\n        onClick={() => setIsLight(!isLight)}\n      >\n        <LightDarkToggle isLight={isLight} />\n      </button>\n    </div>\n  );\n};\n\nexport default Preview;\n","path":"/preview.tsx","target":"uicapsule/light-dark-toggle/preview.tsx","type":"registry:file"}],"homepage":"https://www.uicapsule.com/ui/light-dark-toggle","name":"light-dark-toggle","registryDependencies":[],"title":"Light/Dark Toggle","type":"registry:block"}