Oblivion
Oblivion
Docs
Go back
Component by
OV
Oblivion
TSX
glass-drawer
"use client"

import React from "react";
import { motion, AnimatePresence } from "framer-motion";
import { X } from "lucide-react";

// Replaced with Tailwind classes for seamless dark/light mode support.

interface GlassDrawerProps {
  open: boolean;
  onClose: () => void;
  side?: "right" | "left" | "top" | "bottom";
  title: string;
  children: React.ReactNode;
}

export function GlassDrawer({ open, onClose, side = "right", title, children }: GlassDrawerProps) {
  const variants = {
    right:  { hidden: { x: "100%" }, visible: { x: 0 } },
    left:   { hidden: { x: "-100%" }, visible: { x: 0 } },
    bottom: { hidden: { y: "100%" }, visible: { y: 0 } },
    top:    { hidden: { y: "-100%" }, visible: { y: 0 } },
  };

  const placement = {
    right:  { top: 0, right: 0, bottom: 0, width: 360 },
    left:   { top: 0, left: 0, bottom: 0, width: 360 },
    bottom: { left: 0, right: 0, bottom: 0, height: 320 },
    top:    { left: 0, right: 0, top: 0, height: 320 },
  };

  const borderRadius = {
    right: "24px 0 0 24px",
    left: "0 24px 24px 0",
    bottom: "24px 24px 0 0",
    top: "0 0 24px 24px",
  };

  return (
    <AnimatePresence>
      {open && (
        <>
          <motion.div
            key="drawer-bg"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            onClick={onClose}
            className="fixed inset-0 z-50 bg-black/10 dark:bg-black/60 backdrop-blur-[2px]"
          />
          <motion.div
            key="drawer"
            variants={variants[side]}
            initial="hidden"
            animate="visible"
            exit="hidden"
            transition={{ type: "spring", stiffness: 340, damping: 34 }}
            className="fixed z-[51] flex flex-col bg-white/70 dark:bg-zinc-900/70 backdrop-blur-2xl border-black/5 dark:border-white/10 shadow-[0_24px_80px_rgba(0,0,0,0.1)] dark:shadow-[0_24px_80px_rgba(0,0,0,0.5)]"
            style={{
              ...placement[side],
              borderRadius: borderRadius[side],
              borderWidth: "1px",
            }}
          >
            <div className="flex items-center justify-between px-6 py-[22px] border-b border-black/5 dark:border-white/10">
              <span className="text-[15px] font-semibold text-neutral-900 dark:text-neutral-100">{title}</span>
              <motion.button
                whileHover={{ scale: 1.1 }}
                whileTap={{ scale: 0.95 }}
                onClick={onClose}
                className="flex items-center justify-center w-7 h-7 bg-black/5 dark:bg-white/10 hover:bg-black/10 dark:hover:bg-white/20 border border-black/5 dark:border-white/10 rounded-lg cursor-pointer text-neutral-600 dark:text-neutral-300 transition-colors"
              >
                <X size={14} />
              </motion.button>
            </div>
            <div className="flex-1 overflow-y-auto px-6 py-5">
              {children}
            </div>
          </motion.div>
        </>
      )}
    </AnimatePresence>
  );
}

export const componentMeta = {
  name: "Glass Drawer",
  description: "A sleek, side-anchored glassmorphism drawer for settings and navigation.",
  props: {
    open: { type: "boolean", description: "Whether the drawer is visible", default: "false" },
    onClose: { type: "function", description: "Callback when the drawer should close" },
    side: { type: "enum", description: "Which side the drawer appears from", default: '"right"' },
    title: { type: "string", description: "Drawer title", default: '"Settings"' },
    children: { type: "ReactNode", description: "Drawer content" }
  }
};

export default function GlassDrawerPreview() {
  const [open, setOpen] = React.useState(false);
  const [side, setSide] = React.useState<"right" | "left" | "top" | "bottom" | any>("right");

  const openDrawer = (s: typeof side) => {
    setSide(s);
    setOpen(true);
  };

  return (
    <div className="flex flex-col items-center justify-center min-h-[500px] w-full relative gap-4">
      
      <div className="flex gap-2 relative z-10">
        {(["left", "right", "top", "bottom"] as const).map((s) => (
          <motion.button
            key={s}
            whileHover={{ scale: 1.05 }}
            whileTap={{ scale: 0.95 }}
            onClick={() => openDrawer(s)}
            className="px-4 py-2 bg-white/70 dark:bg-white/10 border border-black/10 dark:border-white/20 rounded-xl text-neutral-900 dark:text-white text-xs font-semibold backdrop-blur-xl shadow-sm hover:bg-white dark:hover:bg-white/20 transition-all cursor-pointer"
          >
            {s.charAt(0).toUpperCase() + s.slice(1)}
          </motion.button>
        ))}
      </div>

      <GlassDrawer open={open} onClose={() => setOpen(false)} side={side} title="Component Settings">
        <div className="space-y-6">
          {["Layout", "Styling", "Animations", "Advanced"].map((label) => (
            <div key={label}>
              <div className="text-[10px] uppercase tracking-wider text-neutral-500 dark:text-zinc-500 font-bold mb-3">{label}</div>
              <div className="space-y-3">
                <div className="p-3 rounded-xl bg-black/5 dark:bg-white/5 border border-black/5 dark:border-white/10 flex items-center justify-between">
                  <span className="text-xs font-medium text-neutral-800 dark:text-zinc-300">Enable physics</span>
                  <div className="w-8 h-4 bg-black dark:bg-[#c8ff3e] rounded-full shadow-inner" />
                </div>
                <div className="p-3 rounded-xl bg-black/5 dark:bg-white/5 border border-black/5 dark:border-white/10 flex items-center justify-between">
                  <span className="text-xs font-medium text-neutral-800 dark:text-zinc-300">Blur intensity</span>
                  <span className="text-[10px] text-neutral-500 font-mono font-semibold">16px</span>
                </div>
              </div>
            </div>
          ))}
        </div>
      </GlassDrawer>
    </div>
  );
}
Save to favoritesCopy to Figma
React

Glass Drawer

A sleek, side-anchored glassmorphism drawer for settings and navigation.

Installation

$ npx @cosmoo/oblivion add glass-drawer
Dependenciesnpm install framer-motion lucide-react

Usage Example

tsx
import GlassDrawer from "@/components/oblivion/glass-drawer"

export default function Demo() {
  return (
    <div className="p-10 flex justify-center">
      <GlassDrawer />
    </div>
  )
}