← Back
UI

Side Drawer

Slide-in panel from left or right with backdrop overlay and smooth CSS transition.

click to open drawer

Menu

Awesome FE v2.0

SideDrawer.tsx93 lines
"use client";

import { useState } from "react";

const NAV = [
  { icon: "⊞", label: "Dashboard" },
  { icon: "◈", label: "Analytics" },
  { icon: "⊕", label: "Projects" },
  { icon: "◎", label: "Team" },
  { icon: "⚙", label: "Settings" },
];

export default function SideDrawer() {
  const [open, setOpen]     = useState(false);
  const [active, setActive] = useState("Dashboard");
  const [side, setSide]     = useState<"left" | "right">("left");

  return (
    <div className="relative w-full overflow-hidden rounded-2xl border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950" style={{ height: 360 }}>
      {/* page content */}
      <div className="flex flex-col items-center justify-center h-full gap-4">
        <div className="flex gap-3">
          <button
            onClick={() => { setSide("left"); setOpen(true); }}
            className="px-4 py-2 rounded-xl bg-violet-600 text-white text-sm font-medium hover:bg-violet-700 transition-colors"
          >
            ← Open Left
          </button>
          <button
            onClick={() => { setSide("right"); setOpen(true); }}
            className="px-4 py-2 rounded-xl border border-zinc-300 dark:border-zinc-700 text-zinc-700 dark:text-zinc-300 text-sm font-medium hover:border-zinc-400 dark:hover:border-zinc-600 transition-colors"
          >
            Open Right →
          </button>
        </div>
        <p className="text-xs font-mono text-zinc-400 dark:text-zinc-600">click to open drawer</p>
      </div>

      {/* overlay */}
      <div
        className="absolute inset-0 bg-black/40 transition-opacity duration-300"
        style={{ opacity: open ? 1 : 0, pointerEvents: open ? "auto" : "none" }}
        onClick={() => setOpen(false)}
      />

      {/* drawer */}
      <div
        className="absolute top-0 bottom-0 w-64 bg-white dark:bg-zinc-900 shadow-2xl flex flex-col transition-transform duration-300 ease-out"
        style={{
          [side]: 0,
          transform: open
            ? "translateX(0)"
            : side === "left" ? "translateX(-100%)" : "translateX(100%)",
        }}
      >
        {/* header */}
        <div className="flex items-center justify-between px-5 py-4 border-b border-zinc-100 dark:border-zinc-800">
          <p className="font-semibold text-sm text-zinc-900 dark:text-zinc-50">Menu</p>
          <button
            onClick={() => setOpen(false)}
            className="text-zinc-400 dark:text-zinc-600 hover:text-zinc-600 dark:hover:text-zinc-400 transition-colors text-lg leading-none"
          >
            ×
          </button>
        </div>

        {/* nav */}
        <nav className="flex-1 py-3 px-3">
          {NAV.map((item) => (
            <button
              key={item.label}
              onClick={() => { setActive(item.label); setOpen(false); }}
              className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm transition-colors mb-0.5 ${
                active === item.label
                  ? "bg-violet-50 dark:bg-violet-950/40 text-violet-700 dark:text-violet-300 font-medium"
                  : "text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800"
              }`}
            >
              <span className="text-base">{item.icon}</span>
              {item.label}
            </button>
          ))}
        </nav>

        {/* footer */}
        <div className="px-5 py-4 border-t border-zinc-100 dark:border-zinc-800">
          <p className="text-xs text-zinc-400 dark:text-zinc-600 font-mono">Awesome FE v2.0</p>
        </div>
      </div>
    </div>
  );
}

More in UI