diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d0ac45ee..c2e0b68f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,6 +12,7 @@ "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-icons": "^1.3.1", "@radix-ui/react-navigation-menu": "^1.1.4", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", @@ -21,7 +22,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", - "framer-motion": "^11.11.10", + "framer-motion": "^11.11.11", "fuse.js": "^7.0.0", "lucide-react": "^0.453.0", "mini-svg-data-uri": "^1.4.4", @@ -1335,6 +1336,14 @@ } } }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.1.tgz", + "integrity": "sha512-QvYompk0X+8Yjlo/Fv4McrzxohDdM5GgLHyQcPpcsPvlOSXCGFjdbuyGL5dzRbg0GpknAjQJJZzdiRK7iWVuFQ==", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x || ^19.x" + } + }, "node_modules/@radix-ui/react-id": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index be0c307b..6bd130d2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,7 @@ "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-icons": "^1.3.1", "@radix-ui/react-navigation-menu": "^1.1.4", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", @@ -31,7 +32,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", - "framer-motion": "^11.11.10", + "framer-motion": "^11.11.11", "fuse.js": "^7.0.0", "lucide-react": "^0.453.0", "mini-svg-data-uri": "^1.4.4", diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 68f65f7f..2b5ac059 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -68,7 +68,12 @@ export default function RootLayout({ - +
diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index b40eb78a..d70ab639 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -6,11 +6,9 @@ import { useEffect, useState } from "react"; import { navbarLinks } from "@/config/siteConfig"; -import { cn } from "@/lib/utils"; -import { MoonIcon, SunIcon } from "lucide-react"; -import { useTheme } from "next-themes"; import CommandMenu from "./CommandMenu"; import StarOnGithubButton from "./ui/star-on-github-button"; +import { ModeToggle } from "./ui/theme-toggle"; import { Tooltip, TooltipContent, @@ -22,7 +20,6 @@ export const dynamic = "force-dynamic"; function Navbar() { const [isScrolled, setIsScrolled] = useState(false); - const { theme, setTheme } = useTheme(); useEffect(() => { const handleScroll = () => { @@ -56,7 +53,6 @@ function Navbar() { /> Proxmox VE Helper-Scripts - {/* */}
@@ -81,28 +77,7 @@ function Navbar() { ))} - - - - - - - Theme Toggle - - - +
diff --git a/frontend/src/components/ui/theme-toggle.tsx b/frontend/src/components/ui/theme-toggle.tsx new file mode 100644 index 00000000..e8e59036 --- /dev/null +++ b/frontend/src/components/ui/theme-toggle.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { useTheme } from "next-themes"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip"; +import { Button } from "./button"; +import { MoonIcon, SunIcon } from "@radix-ui/react-icons"; + +export function ModeToggle() { + const { setTheme, theme: currentTheme } = useTheme(); + + const handleChangeTheme = (theme: "light" | "dark") => { + if (theme === currentTheme) return; + + if (!document.startViewTransition) return setTheme(theme); + document.startViewTransition(() => setTheme(theme)); + }; + + return ( + + + + + + + Theme Toggle + + + + ); +} diff --git a/frontend/src/styles/globals.css b/frontend/src/styles/globals.css index e9820c8a..ced8b3be 100644 --- a/frontend/src/styles/globals.css +++ b/frontend/src/styles/globals.css @@ -30,6 +30,29 @@ --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; + --expo-out: linear( + 0 0%, + 0.1684 2.66%, + 0.3165 5.49%, + 0.446 8.52%, + 0.5581 11.78%, + 0.6535 15.29%, + 0.7341 19.11%, + 0.8011 23.3%, + 0.8557 27.93%, + 0.8962 32.68%, + 0.9283 38.01%, + 0.9529 44.08%, + 0.9711 51.14%, + 0.9833 59.06%, + 0.9915 68.74%, + 1 100% + ); + } + + ::selection { + background-color: hsl(var(--accent)); + color: hsl(var(--foreground)); } .dark { @@ -58,8 +81,48 @@ --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; } + + + + ::view-transition-group(root) { + animation-duration: 0.7s; + animation-timing-function: var(--expo-out); + } + + ::view-transition-new(root) { + animation-name: reveal-light; + } + + ::view-transition-old(root), + .dark::view-transition-old(root) { + animation: none; + z-index: -1; + } + .dark::view-transition-new(root) { + animation-name: reveal-dark; + } + + @keyframes reveal-dark { + from { + clip-path: polygon(50% -71%, -50% 71%, -50% 71%, 50% -71%); + } + to { + clip-path: polygon(50% -71%, -50% 71%, 50% 171%, 171% 50%); + } + } + + @keyframes reveal-light { + from { + clip-path: polygon(171% 50%, 50% 171%, 50% 171%, 171% 50%); + } + to { + clip-path: polygon(171% 50%, 50% 171%, -50% 71%, 50% -71%); + } + } } + + @layer base { * { @apply border-border;