React + MotionOverlays

Bottom Sheet

Ein Panel, das mit Griff von unten hochfährt und sich schließt, wenn du es nach unten ziehst oder daneben tippst.

Einstellungen

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"
      >
        Sheet öffnen
      </button>
      <AnimatePresence>
        {open && (
          <>
            <motion.button
              type="button"
              aria-label="Schließen"
              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">Dein nächster Kurztrip</h3>
              <p className="mt-1 text-sm text-[#86868b]">Speichere diese Unterkunft für später.</p>
              <button
                type="button"
                className="mt-5 w-full rounded-full bg-[#0071e3] py-3 text-sm font-medium text-white"
              >
                Speichern
              </button>
            </motion.div>
          </>
        )}
      </AnimatePresence>
    </div>
  );
}

Prompt für KI

Füg ihn in ChatGPT, Claude oder Cursor ein und der Baustein wird mit diesen Einstellungen an dein Projekt angepasst, auch ohne React.

Mehr aus Overlays

Wofür du ihn nutzen kannst

Auf dem Handy ist ein Bottom Sheet freundlicher als ein zentriertes Modal: Es öffnet sich dort, wo der Daumen schon ist, und lässt sich wegwischen. Nutz es für „Unterkunft merken“ in einer Reise-App, die Größenwahl im Modeshop oder Teilen-Optionen in einem Blog. Dieses Bottom Sheet in React schließt per Ziehen, wie man es von nativen Apps kennt.

Halte den Inhalt so kurz, dass im Sheet nicht gescrollt werden muss. Für ein langes Formular öffne lieber eine eigene Seite. Wähl die Höhe so, dass die Seite dahinter noch zu sehen ist. Dann ist klar, dass man zurückkann.