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;