Morphing Modal
beui/morphing-modalFreeComponent
Family-app-style modal. A single panel that morphs its height as you navigate between inner views, with blur cross-fade on content.
Install it
npx shadcn@latest add https://beui.dev/r/morphing-modal.jsonSource
1"use client";2// beui.dev/components/motion/morphing-modal34import {5 AnimatePresence,6 motion,7 useReducedMotion,8} from "motion/react";9import { type ReactNode, useEffect } from "react";10import { EASE_OUT, SPRING_PANEL } from "@/lib/ease";11import { cn } from "@/lib/utils";1213export interface MorphingModalProps {14 /** Which view is currently shown. `null` closes the modal. */15 viewId: string | null;16 onClose: () => void;17 children: ReactNode;18 /** "bottom" anchors to the viewport bottom (mobile-like). "center" centers vertically. */19 placement?: "bottom" | "center";20 className?: string;21}2223export function MorphingModal({24 viewId,25 onClose,26 children,27 placement = "bottom",28 className,29}: MorphingModalProps) {30 const open = viewId !== null;31 const reduce = useReducedMotion();32 const enterY = reduce ? 0 : placement === "bottom" ? 40 : 20;33 const enterScale = reduce ? 1 : 0.97;3435 useEffect(() => {36 if (!open) return;37 const prev = document.body.style.overflow;38 document.body.style.overflow = "hidden";39 return () => {40 document.body.style.overflow = prev;41 };42 }, [open]);4344 return (45 <div46 aria-hidden={!open}47 inert={!open}48 className={cn(49 "fixed inset-0 z-[80]",50 open ? "pointer-events-auto" : "pointer-events-none",51 )}52 >53 <motion.button54 type="button"55 aria-label="Close modal"56 initial={false}57 animate={{ opacity: open ? 1 : 0 }}58 transition={{ duration: 0.2, ease: EASE_OUT }}59 onClick={onClose}60 className={cn(61 "absolute inset-0 bg-background/5 [backdrop-filter:blur(14px)_saturate(140%)] [-webkit-backdrop-filter:blur(14px)_saturate(140%)]",62 open ? "pointer-events-auto" : "pointer-events-none",63 )}64 />6566 <div67 className={cn(68 "pointer-events-none absolute inset-0 flex justify-center px-4",69 placement === "bottom" ? "items-end pb-8" : "items-center",70 )}71 >72 <AnimatePresence initial={false}>73 {open ? (74 <motion.div75 key="panel"76 layout77 initial={{ opacity: 0, y: enterY, scale: enterScale }}78 animate={{ opacity: 1, y: 0, scale: 1 }}79 exit={{80 opacity: 0,81 y: enterY,82 scale: reduce ? 1 : 0.98,83 transition: { duration: 0.18, ease: EASE_OUT },84 }}85 transition={SPRING_PANEL}86// … truncated
What it pulls in
npm packages
Files it writes
More from beui
All 104 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Action Swap Bluraction-swap-blur | beui | Components | Free | 3 deps · 4 files | |
| Action Swap Cascadeaction-swap-cascade | beui | Components | Free | 3 deps · 4 files | |
| Action Swap Rollaction-swap-roll | beui | Components | Free | 3 deps · 4 files | |
| Agent Activityagent-activity | beui | Components | Free | 4 deps · 9 files | |
| Agent Loading States Agent Progressagent-progress | beui | Components | Free | 3 deps · 3 files | |
| AI Sidebarai-sidebar | beui | Components | Free | 4 deps · 5 files | |
| Animated Badgeanimated-badge | beui | Components | Free | 4 deps · 3 files | |
| Number Animation Animated Numberanimated-number | beui | Components | Free | 3 deps · 3 files |
