패턴

드래그로 스냅·닫는 바텀시트

아래에서 올라오는 시트를 핸들로 끌어 절반/전체로 스냅하거나 닫기.

ReactPointer EventstranslateY 스냅스크롤 락ARIA dialog
라이브 데모
새 탭에서 열기
데모 불러오는 중…

제작 과정

한 줄 요약

스냅 높이(절반/전체)를 viewport 비율로 두고, 핸들을 끄는 동안 translateY를 손가락에 맞춰 따라가게 하다가, 놓을 때 임계값으로 스냅하거나 닫습니다.

이럴 때 필요해요

모바일에서 공유필터옵션을 모달 대신 친숙한 바텀시트로 띄울 때. 끌어서 높이를 바꾸거나 닫는 제스처가 자연스럽습니다.

어떻게 동작하나

  1. 핸들 onPointerDown/Move에서 시작점 대비 이동량(dragY)을 state로 추적, 드래그 중엔 transition을 끔.

  2. onPointerUp에서 충분히 아래로 끌었으면 닫고, 아니면 위/아래 방향에 따라 전체/절반 스냅으로 이동.

  3. 백드롭 클릭ESC로 닫고, 열려 있는 동안 body 스크롤을 잠금.

놓치기 쉬운 것

  • 드래그 중엔 transition을 꺼야 손가락을 따라오고, 놓을 때만 켜야 스냅이 부드러움.

  • setPointerCapture + touch-none으로 모바일 스크롤포인터 이탈을 막아야 함.

  • 접근성: role=dialog/aria-modal + ESC + 포커스 관리.

이런 곳에 써요

  • 모바일 공유/필터/옵션 시트

  • 지도미디어 앱의 디테일 패널

소스 코드

· 데모 페이지에서 자동 추출
import type { Metadata } from "next";
import { BottomSheetDemo } from "./-components/BottomSheetDemo";

export const metadata: Metadata = {
  title: "바텀시트 (드래그 스냅) (데모)",
  description:
    "아래에서 올라오는 바텀시트 — 핸들을 드래그해 절반/전체로 스냅하거나 아래로 끌어 닫기. Pointer 드래그 + translateY 추적 + 임계값 스냅, 백드롭·ESC·바디 스크롤 락.",
  robots: { index: false, follow: false },
};

export default function Page() {
  return (
    <main className="flex min-h-[100dvh] items-center justify-center bg-gradient-to-br from-slate-50 via-white to-emerald-50/40">
      <BottomSheetDemo />
    </main>
  );
}
27조회수

댓글