IMPROVED - Add custom macOS-style squircle app icons to the dock
This commit is contained in:
@@ -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 */}
|
||||
<DockIcon className="group relative overflow-visible rounded-[28%] border border-border/60 bg-white shadow-[inset_0_1px_0_rgba(255,255,255,0.5)]">
|
||||
<DockIcon className="group relative overflow-visible rounded-[26%] bg-white shadow-[0_6px_14px_-6px_rgba(0,0,0,0.5)] ring-1 ring-black/10">
|
||||
<Link
|
||||
href={homeHref}
|
||||
aria-label="mohfarawati — Home"
|
||||
@@ -206,7 +206,7 @@ export function SiteDock({
|
||||
sizes="96px"
|
||||
priority
|
||||
className={cn(
|
||||
"h-full w-full rounded-full object-contain transition-opacity duration-300",
|
||||
"h-full w-full rounded-[inherit] object-contain transition-opacity duration-300",
|
||||
mounted ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
@@ -216,38 +216,29 @@ export function SiteDock({
|
||||
|
||||
<div className="mx-0.5 h-9 w-px self-center bg-border/70" aria-hidden />
|
||||
|
||||
{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 = (
|
||||
<span className="flex h-full w-full items-center justify-center rounded-[inherit]">
|
||||
<Icon className="h-[72%] w-[72%]" strokeWidth={1.9} />
|
||||
</span>
|
||||
);
|
||||
const icon = <DockAppIcon symbol={key} active={isActive} disabled={disabled} />;
|
||||
|
||||
return (
|
||||
<DockIcon
|
||||
key={key}
|
||||
className={cn(
|
||||
"group relative overflow-visible rounded-[28%] border transition-all duration-200",
|
||||
"shadow-[inset_0_1px_0_rgba(255,255,255,0.12)]",
|
||||
"group relative overflow-visible rounded-[26%] transition-shadow duration-200",
|
||||
isActive
|
||||
? "border-primary/50 bg-primary/15 text-primary shadow-[inset_0_1px_0_rgba(255,255,255,0.15),0_0_0_1px_hsl(var(--primary)/0.35),0_10px_24px_-8px_hsl(var(--primary)/0.6)]"
|
||||
: disabled
|
||||
? "border-border/50 bg-foreground/[0.03] text-foreground/35"
|
||||
: "border-border/60 bg-foreground/[0.05] text-foreground/75 hover:border-border hover:bg-foreground/[0.09] hover:text-foreground",
|
||||
? "shadow-[0_12px_26px_-8px_hsl(var(--primary)/0.65)]"
|
||||
: "shadow-[0_6px_14px_-6px_rgba(0,0,0,0.5)] hover:shadow-[0_10px_22px_-8px_rgba(0,0,0,0.6)]",
|
||||
)}
|
||||
>
|
||||
{/* top gloss */}
|
||||
<span
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-0 rounded-[inherit] bg-gradient-to-b from-white/10 to-transparent"
|
||||
/>
|
||||
{disabled ? (
|
||||
<span aria-disabled="true" className="flex h-full w-full cursor-not-allowed items-center justify-center">
|
||||
{inner}
|
||||
<span
|
||||
aria-disabled="true"
|
||||
className="flex h-full w-full cursor-not-allowed items-center justify-center rounded-[inherit]"
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
) : (
|
||||
<Link
|
||||
@@ -256,7 +247,7 @@ export function SiteDock({
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className="flex h-full w-full items-center justify-center rounded-[inherit]"
|
||||
>
|
||||
{inner}
|
||||
{icon}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user