Smooth Drawer
Smooth Drawer is a React and Tailwind CSS overlay component from Kokonut UI, licensed MIT. Copy it and paste it into your project.
Kokonut UI
MIT
overlays
What it is
Smooth Drawer is a React and Tailwind CSS overlay component from Kokonut UI, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i motion
Licence
This component comes from Kokonut UI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Smooth Drawer
"use client";
/**
* @author: @dorianbaffier
* @description: Smooth Drawer
* @version: 1.0.0
* @date: 2025-06-26
* @license: MIT
* @website: https://kokonutui.com
* @github: https://github.com/kokonut-labs/kokonutui
*/
import { Fingerprint } from "lucide-react";
import { motion } from "motion/react";
import Image from "next/image";
import Link from "next/link";
import type * as React from "react";
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
interface PriceTagProps {
price: number;
discountedPrice: number;
}
function PriceTag({ price, discountedPrice }: PriceTagProps) {
return (
<div className="mx-auto flex max-w-fit items-center justify-around gap-4">
<div className="flex items-baseline gap-2">
<span className="bg-gradient-to-br from-zinc-900 to-zinc-700 bg-clip-text font-bold text-4xl text-transparent dark:from-white dark:to-zinc-300">
${discountedPrice}
</span>
<span className="text-lg text-zinc-400 line-through dark:text-zinc-500">
${price}
</span>
</div>
<div className="flex flex-col items-center gap-0.5">
<span className="font-medium text-sm text-zinc-900 dark:text-zinc-100">
Lifetime access
</span>
<span className="text-xs text-zinc-700 dark:text-zinc-300">
One-time payment
</span>
</div>
</div>
);
}
interface DrawerDemoProps extends React.HTMLAttributes<HTMLDivElement> {
title?: string;
description?: string;
primaryButtonText?: string;
secondaryButtonText?: string;
onPrimaryAction?: () => void;
onSecondaryAction?: () => void;
price?: number;
discountedPrice?: number;
}
const drawerVariants = {
hidden: {
y: "100%",
opacity: 0,
rotateX: 5,
transition: {
type: "spring",
stiffness: 300,
damping: 30,
},
},
visible: {
y: 0,
opacity: 1,
rotateX: 0,
transition: {
type: "spring",
stiffness: 300,
damping: 30,
mass: 0.8,
staggerChildren: 0.07,
delayChildren: 0.2,
},
},
};
const itemVariants = {
hidden: {
y: 20,
opacity: 0,
transition: {
type: "spring",
stiffness: 300,
damping: 30,
},
},
visible: {
y: 0,
opacity: 1,
transition: {
type: "spring",
stiffness: 300,
damping: 30,
mass: 0.8,
},
},
};
export default function SmoothDrawer({
title = "KokonutUI - Pro",
description = "100+ collection of UI Components and templates built for React, Next.js, and Tailwind CSS. Spend no time on design and focus on shipping.",
primaryButtonText = "Buy Now",
secondaryButtonText = "Maybe Later",
onSecondaryAction,
price = 169,
discountedPrice = 99,
}: DrawerDemoProps) {
const handleSecondaryClick = () => {
onSecondaryAction?.();
};
return (
<Drawer>
<DrawerTrigger asChild>
<Button variant="outline">Open Drawer</Button>
</DrawerTrigger>
<DrawerContent className="mx-auto max-w-fit rounded-2xl p-6 shadow-xl">
<motion.div
animate="visible"
className="mx-auto w-full max-w-[340px] space-y-6"
initial="hidden"
variants={drawerVariants as any}
>
<motion.div variants={itemVariants as any}>
<DrawerHeader className="space-y-2.5 px-0">
<DrawerTitle className="flex items-center gap-2.5 font-semibold text-2xl tracking-tighter">
<motion.div variants={itemVariants as any}>
<div className="rounded-xl bg-gradient-to-br from-zinc-100 to-zinc-200 p-1.5 shadow-inner dark:from-zinc-800 dark:to-zinc-900">
<Image alt="Logo" height={32} src="/logo.svg" width={32} />
</div>
</motion.div>
<motion.span variants={itemVariants as any}>
{title}
</motion.span>
</DrawerTitle>
<motion.div variants={itemVariants as any}>
<DrawerDescription className="text-sm text-zinc-600 leading-relaxed tracking-tighter dark:text-zinc-400">
{description}
</DrawerDescription>
</motion.div>
</DrawerHeader>
</motion.div>
<motion.div variants={itemVariants as any}>
<PriceTag discountedPrice={discountedPrice} price={price} />
</motion.div>
<motion.div variants={itemVariants as any}>
<DrawerFooter className="flex flex-col gap-3 px-0">
<div className="w-full">
<Link
className="group relative inline-flex h-11 w-full items-center justify-center overflow-hidden rounded-xl bg-gradient-to-r from-rose-500 to-pink-500 font-semibold text-sm text-white tracking-wide shadow-lg shadow-rose-500/20 transition-all duration-500 hover:from-rose-600 hover:to-pink-600 hover:shadow-rose-500/30 hover:shadow-xl dark:from-rose-600 dark:to-pink-600 dark:hover:from-rose-500 dark:hover:to-pink-500"
href="https://kokonutui.pro/#pricing"
target="_blank"
>
<motion.span
className="absolute inset-0 translate-x-[-200%] bg-gradient-to-r from-transparent via-white/20 to-transparent"
transition={{
duration: 1.5,
ease: "easeInOut",
repeat: 0,
}}
whileHover={{
x: ["-200%", "200%"],
}}
/>
<motion.div
animate={{ opacity: 1 }}
className="relative flex items-center gap-2 tracking-tighter"
initial={{ opacity: 0 }}
transition={{ duration: 0.3 }}
>
{primaryButtonText}
<motion.div
animate={{
rotate: [0, 15, -15, 0],
y: [0, -2, 2, 0],
}}
transition={{
duration: 2,
ease: "easeInOut",
repeat: Number.POSITIVE_INFINITY,
repeatDelay: 1,
}}
>
<Fingerprint className="h-4 w-4" />
</motion.div>
</motion.div>
</Link>
</div>
<DrawerClose asChild>
<Button
className="h-11 w-full rounded-xl border-zinc-200 font-semibold text-sm tracking-tighter transition-colors hover:bg-zinc-100 dark:border-zinc-800 dark:hover:bg-zinc-800/80"
onClick={handleSecondaryClick}
variant="outline"
>
{secondaryButtonText}
</Button>
</DrawerClose>
</DrawerFooter>
</motion.div>
</motion.div>
</DrawerContent>
</Drawer>
);
}