diff --git a/apps/www/config/docs.ts b/apps/www/config/docs.ts index 19e1ea203..a91746c27 100644 --- a/apps/www/config/docs.ts +++ b/apps/www/config/docs.ts @@ -495,6 +495,12 @@ export const docsConfig: DocsConfig = { items: [], label: "", }, + { + title: "Nebula Toggle", + href: "/docs/components/nebula-toggle", + items: [], + label: "New", + }, ], }, { diff --git a/apps/www/content/docs/components/nebula-toggle.mdx b/apps/www/content/docs/components/nebula-toggle.mdx new file mode 100644 index 000000000..3ed9d4514 --- /dev/null +++ b/apps/www/content/docs/components/nebula-toggle.mdx @@ -0,0 +1,49 @@ +--- +title: Nebula Toggle +date: 2026-07-11 +description: A cosmic, ethereal theme toggle switch with light and dark mode animations. +author: magicui +published: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn@latest add @magicui/nebula-toggle +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Props + +| Prop | Type | Default | Description | +| ----------- | ---------------------------- | ----------- | ------------------------------------------------------- | +| `checked` | `boolean` | `undefined` | Controlled checked state. If omitted, uses local state. | +| `onChange` | `(checked: boolean) => void` | `undefined` | Callback fired when the state of the toggle changes. | +| `className` | `string` | `undefined` | Custom CSS class styles to apply to the container. | diff --git a/apps/www/public/llms-full.txt b/apps/www/public/llms-full.txt index c84bf080e..4759ffd36 100644 --- a/apps/www/public/llms-full.txt +++ b/apps/www/public/llms-full.txt @@ -13097,6 +13097,293 @@ export default function MorphingTextDemo() { +===== COMPONENT: nebula-toggle ===== +Title: Nebula Toggle +Description: A cosmic, ethereal theme toggle button with light and dark mode animations. + +--- file: magicui/nebula-toggle.tsx --- +"use client" + +import React, { useEffect, useState } from "react" + +import { cn } from "@/lib/utils" + +export interface NebulaToggleProps { + checked?: boolean + onChange?: (checked: boolean) => void + className?: string +} + +export function NebulaToggle({ + checked: controlledChecked, + onChange, + className, +}: NebulaToggleProps) { + const [isMounted, setIsMounted] = useState(false) + const [checked, setChecked] = useState(false) + + useEffect(() => { + setIsMounted(true) + const saved = localStorage.getItem("theme-preference") + if (saved === "dark") { + setChecked(true) + } else if (saved === "light") { + setChecked(false) + } else { + const prefersDark = window.matchMedia( + "(prefers-color-scheme: dark)" + ).matches + setChecked(prefersDark) + } + }, []) + + const isDark = controlledChecked !== undefined ? controlledChecked : checked + + useEffect(() => { + if (isMounted) { + const root = window.document.documentElement + if (isDark) { + root.classList.add("dark") + } else { + root.classList.remove("dark") + } + } + }, [isDark, isMounted]) + + const handleToggle = () => { + const nextValue = !isDark + if (controlledChecked === undefined) { + setChecked(nextValue) + localStorage.setItem("theme-preference", nextValue ? "dark" : "light") + } + onChange?.(nextValue) + } + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === " " || e.key === "Enter") { + e.preventDefault() + handleToggle() + } + } + + return ( + <> + +
+ {/* Outer nebula glow ring */} +
+ + {/* Ambient glow around toggle */} +
+ + + + {/* Track - cosmic nebula with ethereal glow */} +
+ {/* Swirling Nebula clouds */} +
+ + {/* Track inner glow line */} +
+
+ + {/* Thumb */} +
+ {/* Glass reflection on thumb */} +
+ + {/* Cosmic glow ring around thumb */} +
+ + {/* Icons */} + + ☀️ + + + 🌙 + +
+ + {/* Floating cosmic dust */} +
+ + ) +} + + +===== EXAMPLE: nebula-toggle-demo ===== +Title: Nebula Toggle Demo + +--- file: example/nebula-toggle-demo.tsx --- +import { NebulaToggle } from "@/registry/magicui/nebula-toggle" + +export default function NebulaToggleDemo() { + return ( +
+ +
+ ) +} + + +===== EXAMPLE: nebula-toggle-demo ===== +Title: Nebula Toggle Demo + +--- file: example/nebula-toggle-demo.tsx --- +import { NebulaToggle } from "@/registry/magicui/nebula-toggle" + +export default function NebulaToggleDemo() { + return ( +
+ +
+ ) +} + + + ===== COMPONENT: neon-gradient-card ===== Title: Neon Gradient Card Description: A beautiful neon card effect diff --git a/apps/www/public/llms.txt b/apps/www/public/llms.txt index a375dac2d..2d7f0150c 100644 --- a/apps/www/public/llms.txt +++ b/apps/www/public/llms.txt @@ -51,6 +51,7 @@ This file provides LLM-friendly entry points to documentation and examples. - [Marquee](https://magicui.design/docs/components/marquee): An infinite scrolling component that can be used to display text, images, or videos. - [Meteors](https://magicui.design/docs/components/meteors): A meteor shower effect. - [Morphing Text](https://magicui.design/docs/components/morphing-text): A dynamic text morphing component for Magic UI. +- [Nebula Toggle](https://magicui.design/docs/components/nebula-toggle): A cosmic, ethereal theme toggle button with light and dark mode animations. - [Neon Gradient Card](https://magicui.design/docs/components/neon-gradient-card): A beautiful neon card effect - [Noise Texture](https://magicui.design/docs/components/noise-texture): An SVG fractal noise layer using feTurbulence, desaturation, and contrast controls for subtle texture overlays. - [Number Ticker](https://magicui.design/docs/components/number-ticker): Animate numbers to count up or down to a target number @@ -254,6 +255,7 @@ This file provides LLM-friendly entry points to documentation and examples. - [kinetic-text-demo](https://github.com/magicuidesign/magicui/blob/main/example/kinetic-text-demo.tsx): Example usage - [Text 3D Flip Demo](https://github.com/magicuidesign/magicui/blob/main/example/text-3d-flip-demo.tsx): Example usage - [Text 3D Flip Demo 2](https://github.com/magicuidesign/magicui/blob/main/example/text-3d-flip-demo-2.tsx): Example usage +- [Nebula Toggle Demo](https://github.com/magicuidesign/magicui/blob/main/example/nebula-toggle-demo.tsx): Example usage ## Optional diff --git a/apps/www/public/r/nebula-toggle-demo.json b/apps/www/public/r/nebula-toggle-demo.json new file mode 100644 index 000000000..8c4af2dfb --- /dev/null +++ b/apps/www/public/r/nebula-toggle-demo.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "nebula-toggle-demo", + "type": "registry:example", + "title": "Nebula Toggle Demo", + "description": "Example showing a cosmic theme toggle with light/dark transition animations.", + "registryDependencies": [ + "nebula-toggle", + "@magicui/nebula-toggle" + ], + "files": [ + { + "path": "registry/example/nebula-toggle-demo.tsx", + "content": "import { NebulaToggle } from \"@/registry/magicui/nebula-toggle\"\n\nexport default function NebulaToggleDemo() {\n return (\n
\n \n
\n )\n}\n", + "type": "registry:example" + } + ] +} \ No newline at end of file diff --git a/apps/www/public/r/nebula-toggle.json b/apps/www/public/r/nebula-toggle.json new file mode 100644 index 000000000..2322414a0 --- /dev/null +++ b/apps/www/public/r/nebula-toggle.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "nebula-toggle", + "type": "registry:ui", + "title": "Nebula Toggle", + "description": "A cosmic, ethereal theme toggle button with light and dark mode animations.", + "dependencies": [], + "files": [ + { + "path": "registry/magicui/nebula-toggle.tsx", + "content": "\"use client\"\n\nimport React, { useEffect, useState } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface NebulaToggleProps {\n checked?: boolean\n onChange?: (checked: boolean) => void\n className?: string\n}\n\nexport function NebulaToggle({\n checked: controlledChecked,\n onChange,\n className,\n}: NebulaToggleProps) {\n const [isMounted, setIsMounted] = useState(false)\n const [checked, setChecked] = useState(false)\n\n useEffect(() => {\n setIsMounted(true)\n const saved = localStorage.getItem(\"theme-preference\")\n if (saved === \"dark\") {\n setChecked(true)\n } else if (saved === \"light\") {\n setChecked(false)\n } else {\n const prefersDark = window.matchMedia(\n \"(prefers-color-scheme: dark)\"\n ).matches\n setChecked(prefersDark)\n }\n }, [])\n\n const isDark = controlledChecked !== undefined ? controlledChecked : checked\n\n useEffect(() => {\n if (isMounted) {\n const root = window.document.documentElement\n if (isDark) {\n root.classList.add(\"dark\")\n } else {\n root.classList.remove(\"dark\")\n }\n }\n }, [isDark, isMounted])\n\n const handleToggle = () => {\n const nextValue = !isDark\n if (controlledChecked === undefined) {\n setChecked(nextValue)\n localStorage.setItem(\"theme-preference\", nextValue ? \"dark\" : \"light\")\n }\n onChange?.(nextValue)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n e.preventDefault()\n handleToggle()\n }\n }\n\n return (\n <>\n \n \n {/* Outer nebula glow ring */}\n \n\n {/* Ambient glow around toggle */}\n \n\n \n\n {/* Track - cosmic nebula with ethereal glow */}\n \n {/* Swirling Nebula clouds */}\n \n\n {/* Track inner glow line */}\n \n
\n\n {/* Thumb */}\n \n {/* Glass reflection on thumb */}\n \n\n {/* Cosmic glow ring around thumb */}\n \n\n {/* Icons */}\n \n ☀️\n \n \n 🌙\n \n
\n\n {/* Floating cosmic dust */}\n \n \n \n \n
\n \n )\n}\n", + "type": "registry:ui" + } + ] +} \ No newline at end of file diff --git a/apps/www/public/r/registry.json b/apps/www/public/r/registry.json index b624cc4c0..21eb68643 100644 --- a/apps/www/public/r/registry.json +++ b/apps/www/public/r/registry.json @@ -1360,6 +1360,19 @@ } ] }, + { + "name": "nebula-toggle", + "type": "registry:ui", + "title": "Nebula Toggle", + "description": "A cosmic, ethereal theme toggle button with light and dark mode animations.", + "dependencies": [], + "files": [ + { + "path": "registry/magicui/nebula-toggle.tsx", + "type": "registry:ui" + } + ] + }, { "name": "magic-card-demo", "type": "registry:example", @@ -3950,6 +3963,22 @@ } ] }, + { + "name": "nebula-toggle-demo", + "type": "registry:example", + "title": "Nebula Toggle Demo", + "description": "Example showing a cosmic theme toggle with light/dark transition animations.", + "registryDependencies": [ + "nebula-toggle", + "@magicui/nebula-toggle" + ], + "files": [ + { + "path": "registry/example/nebula-toggle-demo.tsx", + "type": "registry:example" + } + ] + }, { "name": "utils", "type": "registry:lib", diff --git a/apps/www/public/registry.json b/apps/www/public/registry.json index b624cc4c0..21eb68643 100644 --- a/apps/www/public/registry.json +++ b/apps/www/public/registry.json @@ -1360,6 +1360,19 @@ } ] }, + { + "name": "nebula-toggle", + "type": "registry:ui", + "title": "Nebula Toggle", + "description": "A cosmic, ethereal theme toggle button with light and dark mode animations.", + "dependencies": [], + "files": [ + { + "path": "registry/magicui/nebula-toggle.tsx", + "type": "registry:ui" + } + ] + }, { "name": "magic-card-demo", "type": "registry:example", @@ -3950,6 +3963,22 @@ } ] }, + { + "name": "nebula-toggle-demo", + "type": "registry:example", + "title": "Nebula Toggle Demo", + "description": "Example showing a cosmic theme toggle with light/dark transition animations.", + "registryDependencies": [ + "nebula-toggle", + "@magicui/nebula-toggle" + ], + "files": [ + { + "path": "registry/example/nebula-toggle-demo.tsx", + "type": "registry:example" + } + ] + }, { "name": "utils", "type": "registry:lib", diff --git a/apps/www/registry.json b/apps/www/registry.json index b624cc4c0..21eb68643 100644 --- a/apps/www/registry.json +++ b/apps/www/registry.json @@ -1360,6 +1360,19 @@ } ] }, + { + "name": "nebula-toggle", + "type": "registry:ui", + "title": "Nebula Toggle", + "description": "A cosmic, ethereal theme toggle button with light and dark mode animations.", + "dependencies": [], + "files": [ + { + "path": "registry/magicui/nebula-toggle.tsx", + "type": "registry:ui" + } + ] + }, { "name": "magic-card-demo", "type": "registry:example", @@ -3950,6 +3963,22 @@ } ] }, + { + "name": "nebula-toggle-demo", + "type": "registry:example", + "title": "Nebula Toggle Demo", + "description": "Example showing a cosmic theme toggle with light/dark transition animations.", + "registryDependencies": [ + "nebula-toggle", + "@magicui/nebula-toggle" + ], + "files": [ + { + "path": "registry/example/nebula-toggle-demo.tsx", + "type": "registry:example" + } + ] + }, { "name": "utils", "type": "registry:lib", diff --git a/apps/www/registry/__index__.tsx b/apps/www/registry/__index__.tsx index 35aa0bc8a..1efa5597e 100644 --- a/apps/www/registry/__index__.tsx +++ b/apps/www/registry/__index__.tsx @@ -1324,6 +1324,23 @@ export const Index: Record = { }), meta: undefined, }, + "nebula-toggle": { + name: "nebula-toggle", + description: "A cosmic, ethereal theme toggle button with light and dark mode animations.", + type: "registry:ui", + registryDependencies: undefined, + files: [{ + path: "registry/magicui/nebula-toggle.tsx", + type: "registry:ui", + target: "" + }], + component: React.lazy(async () => { + const mod = await import("@/registry/magicui/nebula-toggle.tsx") + const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') ?? item.name + return { default: mod.default ?? mod[exportName] } + }), + meta: undefined, + }, "magic-card-demo": { name: "magic-card-demo", description: "Example showing a spotlight effect that follows your mouse cursor and highlights borders on hover.", @@ -4184,6 +4201,23 @@ export const Index: Record = { }), meta: undefined, }, + "nebula-toggle-demo": { + name: "nebula-toggle-demo", + description: "Example showing a cosmic theme toggle with light/dark transition animations.", + type: "registry:example", + registryDependencies: ["nebula-toggle","@magicui/nebula-toggle"], + files: [{ + path: "registry/example/nebula-toggle-demo.tsx", + type: "registry:example", + target: "" + }], + component: React.lazy(async () => { + const mod = await import("@/registry/example/nebula-toggle-demo.tsx") + const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') ?? item.name + return { default: mod.default ?? mod[exportName] } + }), + meta: undefined, + }, "utils": { name: "utils", description: "", diff --git a/apps/www/registry/example/nebula-toggle-demo.tsx b/apps/www/registry/example/nebula-toggle-demo.tsx new file mode 100644 index 000000000..190651054 --- /dev/null +++ b/apps/www/registry/example/nebula-toggle-demo.tsx @@ -0,0 +1,9 @@ +import { NebulaToggle } from "@/registry/magicui/nebula-toggle" + +export default function NebulaToggleDemo() { + return ( +
+ +
+ ) +} diff --git a/apps/www/registry/magicui/nebula-toggle.tsx b/apps/www/registry/magicui/nebula-toggle.tsx new file mode 100644 index 000000000..52f16adfa --- /dev/null +++ b/apps/www/registry/magicui/nebula-toggle.tsx @@ -0,0 +1,249 @@ +"use client" + +import React, { useEffect, useState } from "react" + +import { cn } from "@/lib/utils" + +export interface NebulaToggleProps { + checked?: boolean + onChange?: (checked: boolean) => void + className?: string +} + +export function NebulaToggle({ + checked: controlledChecked, + onChange, + className, +}: NebulaToggleProps) { + const [isMounted, setIsMounted] = useState(false) + const [checked, setChecked] = useState(false) + + useEffect(() => { + setIsMounted(true) + const saved = localStorage.getItem("theme-preference") + if (saved === "dark") { + setChecked(true) + } else if (saved === "light") { + setChecked(false) + } else { + const prefersDark = window.matchMedia( + "(prefers-color-scheme: dark)" + ).matches + setChecked(prefersDark) + } + }, []) + + const isDark = controlledChecked !== undefined ? controlledChecked : checked + + useEffect(() => { + if (isMounted) { + const root = window.document.documentElement + if (isDark) { + root.classList.add("dark") + } else { + root.classList.remove("dark") + } + } + }, [isDark, isMounted]) + + const handleToggle = () => { + const nextValue = !isDark + if (controlledChecked === undefined) { + setChecked(nextValue) + localStorage.setItem("theme-preference", nextValue ? "dark" : "light") + } + onChange?.(nextValue) + } + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === " " || e.key === "Enter") { + e.preventDefault() + handleToggle() + } + } + + return ( + <> + +
+ {/* Outer nebula glow ring */} +
+ + {/* Ambient glow around toggle */} +
+ + + + {/* Track - cosmic nebula with ethereal glow */} +
+ {/* Swirling Nebula clouds */} +
+ + {/* Track inner glow line */} +
+
+ + {/* Thumb */} +
+ {/* Glass reflection on thumb */} +
+ + {/* Cosmic glow ring around thumb */} +
+ + {/* Icons */} + + ☀️ + + + 🌙 + +
+ + {/* Floating cosmic dust */} +
+ + ) +} diff --git a/apps/www/registry/registry-examples.ts b/apps/www/registry/registry-examples.ts index eb8c8ff10..8c26c0a18 100644 --- a/apps/www/registry/registry-examples.ts +++ b/apps/www/registry/registry-examples.ts @@ -2245,4 +2245,18 @@ export const examples: Registry["items"] = [ }, ], }, + { + name: "nebula-toggle-demo", + type: "registry:example", + title: "Nebula Toggle Demo", + description: + "Example showing a cosmic theme toggle with light/dark transition animations.", + registryDependencies: ["nebula-toggle", "@magicui/nebula-toggle"], + files: [ + { + path: "example/nebula-toggle-demo.tsx", + type: "registry:example", + }, + ], + }, ] diff --git a/apps/www/registry/registry-ui.ts b/apps/www/registry/registry-ui.ts index 81f626874..cdb8c9386 100644 --- a/apps/www/registry/registry-ui.ts +++ b/apps/www/registry/registry-ui.ts @@ -1312,4 +1312,18 @@ export const ui: Registry["items"] = [ }, ], }, + { + name: "nebula-toggle", + type: "registry:ui", + title: "Nebula Toggle", + description: + "A cosmic, ethereal theme toggle button with light and dark mode animations.", + dependencies: [], + files: [ + { + path: "magicui/nebula-toggle.tsx", + type: "registry:ui", + }, + ], + }, ]