Local Build and Deploy / deploy (push) Successful in 1m2s
- Strong, obvious active state: accent-tinted background, bold text, accent icon and left accent border so the current section is unmistakable - Inactive items stay fully readable (white on dark) with a clear hover - Separate nav sections with dividers and bolder uppercase headers - Fix invisible mobile hamburger hover (bg-black/10 -> accent tint)
50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import type { ReactNode } from "react";
|
|
import { usePathname } from "next/navigation";
|
|
|
|
/**
|
|
* Sidebar link that highlights when its route is active. "/admin" matches
|
|
* exactly; every other entry also matches its sub-routes (e.g. /admin/radio is
|
|
* active on /admin/radio/history) so the current section stays lit.
|
|
*/
|
|
export function AdminNavLink({
|
|
href,
|
|
icon,
|
|
children,
|
|
}: {
|
|
href: string;
|
|
icon?: ReactNode;
|
|
children: ReactNode;
|
|
}) {
|
|
const pathname = usePathname() ?? "";
|
|
const active =
|
|
href === "/admin" ? pathname === "/admin" : pathname === href || pathname.startsWith(`${href}/`);
|
|
|
|
return (
|
|
<Link
|
|
href={href}
|
|
aria-current={active ? "page" : undefined}
|
|
className={`group flex items-center gap-2.5 rounded-xl px-3 py-2 text-sm no-underline transition-all duration-150 ${
|
|
active
|
|
? "bg-[var(--admin-accent)]/20 font-semibold text-[var(--admin-sidebar-text-readable)] border-l-2 border-[var(--admin-accent)] shadow-sm"
|
|
: "font-medium text-[var(--admin-sidebar-text-readable)] hover:bg-[var(--admin-accent)]/10 hover:no-underline"
|
|
}`}
|
|
>
|
|
{icon ? (
|
|
<span
|
|
className={`flex-none transition-colors ${
|
|
active
|
|
? "text-[var(--admin-accent)]"
|
|
: "text-[var(--admin-sidebar-text-readable)] opacity-75 group-hover:opacity-100"
|
|
}`}
|
|
>
|
|
{icon}
|
|
</span>
|
|
) : null}
|
|
<span className="truncate">{children}</span>
|
|
</Link>
|
|
);
|
|
}
|