Hoja inferior deslizable
Un panel que sube desde abajo con un asa y se cierra al arrastrarlo hacia abajo o tocar fuera.
Ajustes
Código
import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
const spring = { type: "spring" as const, stiffness: 300, damping: 30 };
interface BottomSheetProps {
height?: number;
}
export default function BottomSheet({ height = 240 }: BottomSheetProps) {
const [open, setOpen] = useState(false);
return (
<div className="relative flex h-64 w-full flex-col items-center justify-center overflow-hidden rounded-2xl bg-black/5 text-[#1d1d1f] dark:text-[#f5f5f7] dark:bg-white/10 sm:h-72">
{/* Relleno: da a la caja 320 px de ancho natural sin impedir que encoja en pantallas estrechas */}
<svg aria-hidden className="h-0 w-80 max-w-full" />
<button
type="button"
onClick={() => setOpen(true)}
className="rounded-full bg-[#0071e3] px-5 py-2.5 text-sm font-medium text-white"
>
Abrir sheet
</button>
<AnimatePresence>
{open && (
<>
<motion.button
type="button"
aria-label="Cerrar"
className="absolute inset-0 bg-[#1d1d1f]/10 dark:bg-[#f5f5f7]/10"
onClick={() => setOpen(false)}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
<motion.div
drag="y"
dragConstraints={{ top: 0, bottom: 100 }}
onDragEnd={(_, info) => {
if (info.offset.y > 60) setOpen(false);
}}
initial={{ y: "100%" }}
animate={{ y: 0 }}
exit={{ y: "100%" }}
transition={spring}
className="absolute inset-x-0 bottom-0 rounded-t-[28px] bg-white dark:bg-[#1c1c1e] p-5 shadow-[0_4px_12px_rgba(0,0,0,0.04),0_24px_60px_rgba(0,0,0,0.1)]"
style={{ minHeight: `min(${height}px, 100%)` }}
>
<div className="mx-auto mb-5 h-1.5 w-10 rounded-full bg-black/5 dark:bg-white/10" />
<h3 className="text-lg font-semibold">Tu próxima escapada</h3>
<p className="mt-1 text-sm text-[#86868b]">Guarda este alojamiento para más tarde.</p>
<button
type="button"
className="mt-5 w-full rounded-full bg-[#0071e3] py-3 text-sm font-medium text-white"
>
Guardar
</button>
</motion.div>
</>
)}
</AnimatePresence>
</div>
);
}
Prompt para IA
Pégalo en ChatGPT, Claude o Cursor y adaptará el bloque a tu proyecto con estos ajustes, aunque no uses React.
Más en Overlays
Dónde usarlo
En el móvil, una hoja inferior es más amable que un modal centrado: se abre donde ya está el pulgar y se aparta con un gesto. Úsala para «Guardar este alojamiento» en una app de viajes, elegir talla en una tienda o compartir un artículo. Este bottom sheet en React se cierra arrastrando, como en las apps nativas.
Deja el contenido lo bastante corto para que quepa sin scroll dentro de la hoja; si hace falta un formulario largo, abre una página completa. Ajusta la altura para que la página de detrás siga asomando, así se entiende que se puede volver.