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

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

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

interface GlassModalProps {
  open: boolean;
  onClose: () => void;
  title: string;
  children: React.ReactNode;
}

export function GlassModal({ open, onClose, title, children }: GlassModalProps) {
  useEffect(() => {
    const handler = (e: KeyboardEvent) => e.key === "Escape" && onClose?.();
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [onClose]);

  return (
    <AnimatePresence>
      {open && (
        <>
          <motion.div
            key="backdrop"
            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="modal"
            initial={{ opacity: 0, scale: 0.92, y: 24 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.94, y: 16 }}
            transition={{ type: "spring", stiffness: 380, damping: 30 }}
            className="fixed inset-0 z-[51] flex items-center justify-center pointer-events-none"
          >
            <div
              className="w-full max-w-[460px] pointer-events-auto rounded-[24px] p-7 md:p-8 bg-white/70 dark:bg-zinc-900/70 backdrop-blur-2xl border border-black/5 dark:border-white/10 shadow-[0_32px_80px_rgba(0,0,0,0.1)] dark:shadow-[0_32px_80px_rgba(0,0,0,0.5)]"
            >
              {/* Header */}
              <div className="flex items-center justify-between mb-5">
                <span className="text-base 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>
              {children}
            </div>
          </motion.div>
        </>
      )}
    </AnimatePresence>
  );
}

export const componentMeta = {
  name: "Glass Modal",
  description: "A premium glassmorphism modal with spring animations and backdrop blur.",
  props: {
    open: { type: "boolean", description: "Whether the modal is visible", default: "false" },
    onClose: { type: "function", description: "Callback when the modal should close" },
    title: { type: "string", description: "Modal title", default: '"Confirm action"' },
    children: { type: "ReactNode", description: "Modal content" }
  }
};

export default function GlassModalPreview() {
  const [open, setOpen] = React.useState(false);
  return (
    <div className="flex flex-col items-center justify-center min-h-[500px] w-full relative p-8">
      
      <motion.button
        whileHover={{ scale: 1.05 }}
        whileTap={{ scale: 0.95 }}
        onClick={() => setOpen(true)}
        className="relative z-10 px-8 py-3 bg-white/70 dark:bg-white/10 border border-black/10 dark:border-white/20 rounded-2xl text-neutral-900 dark:text-white font-semibold backdrop-blur-xl shadow-xl transition-all hover:bg-white dark:hover:bg-white/20"
      >
        Open Glass Modal
      </motion.button>

      <GlassModal open={open} onClose={() => setOpen(false)} title="Confirm action">
        <div className="space-y-4">
          <p className="text-sm text-neutral-600 dark:text-zinc-400 leading-relaxed">
            Are you sure you want to deploy this component to production? This action will push your changes live immediately.
          </p>
          <div className="flex gap-3 justify-end mt-6">
            <button
              onClick={() => setOpen(false)}
              className="px-5 py-2 rounded-xl bg-black/5 dark:bg-white/5 border border-black/5 dark:border-white/10 text-neutral-600 dark:text-zinc-400 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10 transition-colors"
            >
              Cancel
            </button>
            <button
              onClick={() => setOpen(false)}
              className="px-5 py-2 rounded-xl bg-black text-white dark:bg-[#c8ff3e] dark:text-black text-sm font-bold shadow-md hover:bg-neutral-800 dark:hover:bg-[#b8ef2e] transition-colors dark:shadow-[0_0_20px_rgba(200,255,62,0.3)]"
            >
              Deploy
            </button>
          </div>
        </div>
      </GlassModal>
    </div>
  );
}
Save to favoritesCopy to Figma
React

Glass Modal

A premium glassmorphism modal with spring animations and backdrop blur.

Installation

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

Usage Example

tsx
import GlassModal from "@/components/oblivion/glass-modal"

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