React + MotionOverlays

Panneau glissant du bas

Un panneau à poignée qui monte du bas de l’écran et se ferme quand on le tire vers le bas.

Réglages

240px

Code

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"
      >
        Ouvrir la feuille
      </button>
      <AnimatePresence>
        {open && (
          <>
            <motion.button
              type="button"
              aria-label="Fermer"
              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">Votre prochaine escapade</h3>
              <p className="mt-1 text-sm text-[#86868b]">Enregistrez ce logement pour plus tard.</p>
              <button
                type="button"
                className="mt-5 w-full rounded-full bg-[#0071e3] py-3 text-sm font-medium text-white"
              >
                Enregistrer
              </button>
            </motion.div>
          </>
        )}
      </AnimatePresence>
    </div>
  );
}

Prompt pour l’IA

Collez-le dans ChatGPT, Claude ou Cursor : le bloc sera adapté à votre projet avec ces réglages, même sans React.

Plus dans Overlays

Où l’utiliser

Sur mobile, un panneau inférieur est plus agréable qu’une modale centrée : il s’ouvre là où se trouve déjà le pouce et se ferme d’un geste. Utilisez-le pour « Enregistrer ce logement » dans une application de voyage ou le choix de taille dans une boutique. Ce bottom sheet en React se ferme en glissant, comme dans les applications natives.

Gardez un contenu assez court pour tenir sans défilement interne ; pour un long formulaire, ouvrez plutôt une page complète. Réglez la hauteur pour que la page derrière reste visible, cela rappelle qu’on peut revenir.