Docs
Go back
Component by
OV
OblivionTSX
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>
);
}"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-modalDependencies
npm install framer-motion lucide-reactUsage Example
tsx
import GlassModal from "@/components/oblivion/glass-modal"
export default function Demo() {
return (
<div className="p-10 flex justify-center">
<GlassModal />
</div>
)
}import GlassModal from "@/components/oblivion/glass-modal"
export default function Demo() {
return (
<div className="p-10 flex justify-center">
<GlassModal />
</div>
)
}