From 6e7cc4dbc6d030704086e9ddecc7b20dddab779d Mon Sep 17 00:00:00 2001 From: moh Date: Sun, 20 Sep 2026 19:05:29 +0200 Subject: [PATCH] IMPROVED - Add custom macOS-style squircle app icons to the dock --- components/layout/dock-app-icon.tsx | 119 ++++++++++++++++++++++++++++ components/layout/site-dock.tsx | 53 +++++-------- components/ui/dock.tsx | 2 +- 3 files changed, 142 insertions(+), 32 deletions(-) create mode 100644 components/layout/dock-app-icon.tsx diff --git a/components/layout/dock-app-icon.tsx b/components/layout/dock-app-icon.tsx new file mode 100644 index 0000000..568558d --- /dev/null +++ b/components/layout/dock-app-icon.tsx @@ -0,0 +1,119 @@ +"use client"; + +import { useId } from "react"; + +export type DockSymbol = "home" | "about" | "portfolio" | "products" | "contact"; + +/** + * macOS Big Sur–style squircle app icon, drawn entirely in SVG so it stays + * crisp at every magnification step. Inactive icons use a neutral graphite + * gradient; the active icon lights up in the brand coral. + */ +export function DockAppIcon({ + symbol, + active = false, + disabled = false, +}: { + symbol: DockSymbol; + active?: boolean; + disabled?: boolean; +}) { + const uid = useId().replace(/[:]/g, ""); + const bg = `bg-${uid}`; + const gloss = `gloss-${uid}`; + + return ( + + + + {active ? ( + <> + + + + ) : ( + <> + + + + )} + + + + + + + + {/* tile */} + + + + + {/* symbol */} + + {symbol === "home" && ( + <> + + + + )} + {symbol === "about" && ( + <> + + + + )} + {symbol === "contact" && ( + <> + + + + )} + + + {/* filled symbols */} + {symbol === "portfolio" && ( + + + + + + + )} + {symbol === "products" && ( + + + + + + )} + + ); +} diff --git a/components/layout/site-dock.tsx b/components/layout/site-dock.tsx index b9f3fc6..5b0be07 100644 --- a/components/layout/site-dock.tsx +++ b/components/layout/site-dock.tsx @@ -1,13 +1,14 @@ "use client"; -import { FolderKanban, Home, Mail, Package, ShieldCheck, User } from "lucide-react"; +import { ShieldCheck } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { useTheme } from "next-themes"; import { useLocale, useTranslations } from "next-intl"; -import { useEffect, useState, type ComponentType } from "react"; +import { useEffect, useState } from "react"; +import { DockAppIcon, type DockSymbol } from "@/components/layout/dock-app-icon"; import { LocaleToggle } from "@/components/layout/locale-toggle"; import { SoundToggle } from "@/components/sound-toggle"; import { ThemeToggle } from "@/components/theme-toggle"; @@ -30,18 +31,17 @@ type SiteDockProps = { }; type NavItem = { - key: "home" | "about" | "portfolio" | "products" | "contact"; + key: DockSymbol; path: string; - Icon: ComponentType<{ className?: string }>; disabled?: boolean; }; const navItems: NavItem[] = [ - { key: "home", path: "", Icon: Home }, - { key: "about", path: "/about", Icon: User }, - { key: "portfolio", path: "/portfolio", Icon: FolderKanban }, - { key: "products", path: "/products", Icon: Package, disabled: true }, - { key: "contact", path: "/contact", Icon: Mail }, + { key: "home", path: "" }, + { key: "about", path: "/about" }, + { key: "portfolio", path: "/portfolio" }, + { key: "products", path: "/products", disabled: true }, + { key: "contact", path: "/contact" }, ]; function isNavItemActive(currentPath: string, itemPath: string) { @@ -192,7 +192,7 @@ export function SiteDock({ className="pointer-events-auto h-[68px] gap-2.5 border-border/60 bg-background/70 shadow-panel" > {/* Logo — first icon in the dock */} - + @@ -216,38 +216,29 @@ export function SiteDock({
- {navItems.map(({ key, path, Icon, disabled }) => { + {navItems.map(({ key, path, disabled }) => { const itemPath = path || "/"; const isActive = isNavItemActive(currentPath, itemPath); const label = t(key); - const inner = ( - - - - ); + const icon = ; return ( - {/* top gloss */} - {disabled ? ( - - {inner} + + {icon} ) : ( - {inner} + {icon} )} diff --git a/components/ui/dock.tsx b/components/ui/dock.tsx index 4457be2..0b5d53d 100644 --- a/components/ui/dock.tsx +++ b/components/ui/dock.tsx @@ -107,7 +107,7 @@ const DockIcon = ({ ...props }: DockIconProps) => { const ref = useRef(null); - const padding = Math.max(4, size * 0.12); + const padding = Math.max(2, size * 0.05); const defaultMouseX = useMotionValue(Infinity); const distanceCalc = useTransform(mouseX ?? defaultMouseX, (val: number) => {