{"_id":"@0xbridges/sheet","_rev":"4-ee08f26c42fa2152fd8c5e95413bd888","name":"@0xbridges/sheet","dist-tags":{"latest":"2.1.1"},"versions":{"1.0.0":{"name":"@0xbridges/sheet","version":"1.0.0","keywords":["react-native","bottom-sheet","top-sheet","sheet","gesture","reanimated","modal"],"author":{"url":"https://0xbridges.ai","name":"0xbridges"},"license":"Apache-2.0","_id":"@0xbridges/sheet@1.0.0","maintainers":[{"name":"zeroxbridges","email":"bridges@symmetry.fi"}],"homepage":"https://0xbridges.ai","bugs":{"url":"https://github.com/0xbridges/sheet/issues"},"dist":{"shasum":"686a2beec68c997e715720f08eb6fd63afd1e762","tarball":"https://registry.npmjs.org/@0xbridges/sheet/-/sheet-1.0.0.tgz","fileCount":10,"integrity":"sha512-2EiyTMJZRKxgaPfW2pZTzgZ16uhJSTLebyw/W9L0LWKDlnPi1U68Yu/1ZAlVJuXL0Oms+XGK5hAdlpHVCZagDw==","signatures":[{"sig":"MEUCIGVynJWhR7XK48ThSvR/Sa3osasSaL9rqmnjW0TVST9oAiEAnxEBNPihycqux5kbpKCbkvIE4Po30OsLkKr969oYWm8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":134394},"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","react-native":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"07508744468091b00bb920efe2a35a9d53a3e424","scripts":{"build":"tsc -p tsconfig.build.json","prepare":"npm run build","typecheck":"tsc --noEmit -p tsconfig.json","example:ios":"npm --prefix ./example run ios","example:web":"npm --prefix ./example run web","example:start":"npm --prefix ./example run start","example:android":"npm --prefix ./example run android","example:install":"npm install --prefix ./example","example:typecheck":"npm --prefix ./example run typecheck"},"_npmUser":{"name":"zeroxbridges","email":"bridges@symmetry.fi"},"repository":{"url":"git+https://github.com/0xbridges/sheet.git","type":"git"},"_npmVersion":"10.9.2","description":"Performance-first bottom & top sheets for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"22.15.0","react-native":"./dist/index.js","_hasShrinkwrap":false,"devDependencies":{"react":"19.2.0","typescript":"~5.9.2","@types/react":"~19.2.2","react-native":"0.83.4","react-native-reanimated":"4.2.1","react-native-gesture-handler":"~2.30.0","react-native-safe-area-context":"~5.6.2"},"peerDependencies":{"react":">=19.0.0","react-native":">=0.83.0","react-native-reanimated":">=4.2.0","react-native-gesture-handler":">=2.30.0","react-native-safe-area-context":">=5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/sheet_1.0.0_1776170964035_0.9661424774064158","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@0xbridges/sheet","version":"2.0.0","keywords":["react-native","bottom-sheet","top-sheet","sheet","gesture","reanimated","modal"],"author":{"url":"https://0xbridges.ai","name":"0xbridges"},"license":"Apache-2.0","_id":"@0xbridges/sheet@2.0.0","maintainers":[{"name":"zeroxbridges","email":"bridges@symmetry.fi"}],"homepage":"https://0xbridges.ai","bugs":{"url":"https://github.com/0xbridges/sheet/issues"},"dist":{"shasum":"9a27e6608e8e43537e4f32e47510d1ad6eff460b","tarball":"https://registry.npmjs.org/@0xbridges/sheet/-/sheet-2.0.0.tgz","fileCount":10,"integrity":"sha512-QCaCLmH7bJt+mzSP5EkQ8wZak7kxxkJY9YtWFzkTEmewWjbfLStdYiT4vnXg7IoHHXdZAJd/T15o9I30FGmxMw==","signatures":[{"sig":"MEQCIFd67R0jeRasaPxbiLrV4saEgwyQQCkvGtuyvyKKWj0VAiA84fvkZWUE31gMoOSN/kwkDjbDxk3jwYtq+P1vgZFZNQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":142319},"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","react-native":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"34e4abd9489186cd1cd693e0269626cee04c3e34","scripts":{"build":"tsc -p tsconfig.build.json","prepare":"npm run build","typecheck":"tsc --noEmit -p tsconfig.json","example:ios":"npm --prefix ./example run ios","example:web":"npm --prefix ./example run web","example:start":"npm --prefix ./example run start","example:android":"npm --prefix ./example run android","example:install":"npm install --prefix ./example","example:typecheck":"npm --prefix ./example run typecheck"},"_npmUser":{"name":"zeroxbridges","email":"bridges@symmetry.fi"},"repository":{"url":"git+https://github.com/0xbridges/sheet.git","type":"git"},"_npmVersion":"10.9.2","description":"Performance-first bottom & top sheets for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"22.15.0","react-native":"./dist/index.js","_hasShrinkwrap":false,"devDependencies":{"react":"19.2.0","typescript":"~5.9.2","@types/react":"~19.2.2","react-native":"0.83.4","react-native-reanimated":"4.2.1","react-native-gesture-handler":"~2.30.0","react-native-safe-area-context":"~5.6.2"},"peerDependencies":{"react":">=19.0.0","react-native":">=0.83.0","react-native-reanimated":">=4.2.0","react-native-gesture-handler":">=2.30.0","react-native-safe-area-context":">=5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/sheet_2.0.0_1776443749247_0.4467128670920222","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@0xbridges/sheet","version":"2.1.0","keywords":["react-native","bottom-sheet","top-sheet","sheet","gesture","reanimated","modal"],"author":{"url":"https://0xbridges.ai","name":"0xbridges"},"license":"Apache-2.0","_id":"@0xbridges/sheet@2.1.0","maintainers":[{"name":"zeroxbridges","email":"bridges@symmetry.fi"}],"homepage":"https://0xbridges.ai","bugs":{"url":"https://github.com/0xbridges/sheet/issues"},"dist":{"shasum":"352653f443297c839d73d949557d7ac46757e8f9","tarball":"https://registry.npmjs.org/@0xbridges/sheet/-/sheet-2.1.0.tgz","fileCount":10,"integrity":"sha512-kbOFBS7Gm0bT33GhQIz7umUX/aFpfeb7/TWUMczV5Y8deFX+YUGRinsUB7CqJaQo+1OukIkgxge7hwBYt7EzhQ==","signatures":[{"sig":"MEYCIQD8B7DOqPydo59pKJK8rYNUfm6vOqjLQhvPyW07dmi4lAIhAPw/Y+ErllRv3ymjBCzwDjr4YUSQ0xnMrBu+8nNZrOF/","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":146370},"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","react-native":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"17294ff52b6c6fcc1004b341960f251c56974407","scripts":{"build":"tsc -p tsconfig.build.json","prepare":"npm run build","typecheck":"tsc --noEmit -p tsconfig.json","example:ios":"npm --prefix ./example run ios","example:web":"npm --prefix ./example run web","example:start":"npm --prefix ./example run start","example:android":"npm --prefix ./example run android","example:install":"npm install --prefix ./example","example:typecheck":"npm --prefix ./example run typecheck"},"_npmUser":{"name":"zeroxbridges","email":"bridges@symmetry.fi"},"repository":{"url":"git+https://github.com/0xbridges/sheet.git","type":"git"},"_npmVersion":"10.9.2","description":"Performance-first bottom & top sheets for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"22.15.0","react-native":"./dist/index.js","_hasShrinkwrap":false,"devDependencies":{"react":"19.2.0","typescript":"~5.9.2","@types/react":"~19.2.2","react-native":"0.83.4","react-native-reanimated":"4.2.1","react-native-gesture-handler":"~2.30.0","react-native-safe-area-context":"~5.6.2"},"peerDependencies":{"react":">=19.0.0","react-native":">=0.83.0","react-native-reanimated":">=4.2.0","react-native-gesture-handler":">=2.30.0","react-native-safe-area-context":">=5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/sheet_2.1.0_1776456096911_0.22907562791347047","host":"s3://npm-registry-packages-npm-production"}},"2.1.1":{"name":"@0xbridges/sheet","description":"Performance-first bottom & top sheets for React Native.","exports":{".":{"types":"./dist/index.d.ts","react-native":"./dist/index.js","default":"./dist/index.js"},"./package.json":"./package.json"},"main":"./dist/index.js","react-native":"./dist/index.js","types":"./dist/index.d.ts","version":"2.1.1","license":"Apache-2.0","author":{"name":"0xbridges","url":"https://0xbridges.ai"},"repository":{"type":"git","url":"git+https://github.com/0xbridges/sheet.git"},"bugs":{"url":"https://github.com/0xbridges/sheet/issues"},"homepage":"https://0xbridges.ai","scripts":{"build":"tsc -p tsconfig.build.json","example:install":"npm install --prefix ./example","example:start":"npm --prefix ./example run start","example:ios":"npm --prefix ./example run ios","example:android":"npm --prefix ./example run android","example:web":"npm --prefix ./example run web","example:typecheck":"npm --prefix ./example run typecheck","prepare":"npm run build","typecheck":"tsc --noEmit -p tsconfig.json"},"sideEffects":false,"peerDependencies":{"react":">=19.0.0","react-native":">=0.83.0","react-native-gesture-handler":">=2.30.0","react-native-reanimated":">=4.2.0","react-native-safe-area-context":">=5.6.0"},"devDependencies":{"@types/react":"~19.2.2","react":"19.2.0","react-native":"0.83.4","react-native-gesture-handler":"~2.30.0","react-native-reanimated":"4.2.1","react-native-safe-area-context":"~5.6.2","typescript":"~5.9.2"},"keywords":["react-native","bottom-sheet","top-sheet","sheet","gesture","reanimated","modal"],"_id":"@0xbridges/sheet@2.1.1","gitHead":"fa107e759841226f1478b0534e6bde22adea3699","_nodeVersion":"22.15.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-+IWGFa/ivhhKjN5d3sILzagzhjcx1SrekgaQhypwuCnMrC3XPIpR/mISv4ttMTxnVuQ32rc/NrV6Jpzx+Xj/fQ==","shasum":"d968e099f21c2c5ae93bfdcde947339479cd454c","tarball":"https://registry.npmjs.org/@0xbridges/sheet/-/sheet-2.1.1.tgz","fileCount":10,"unpackedSize":146759,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH9D9TYqf0L/kborq4Vkon6gREzBw0EUutr3iWiWH45LAiAp7kV/d/7QfxMObBDMwAwFbpO0hyOWavAfMnNGmZ2rMA=="}]},"_npmUser":{"name":"zeroxbridges","email":"bridges@symmetry.fi"},"directories":{},"maintainers":[{"name":"zeroxbridges","email":"bridges@symmetry.fi"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sheet_2.1.1_1776457514247_0.0014085640642917951"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-14T12:49:23.922Z","modified":"2026-04-17T20:25:14.829Z","1.0.0":"2026-04-14T12:49:24.176Z","2.0.0":"2026-04-17T16:35:49.389Z","2.1.0":"2026-04-17T20:01:37.068Z","2.1.1":"2026-04-17T20:25:14.387Z"},"bugs":{"url":"https://github.com/0xbridges/sheet/issues"},"author":{"name":"0xbridges","url":"https://0xbridges.ai"},"license":"Apache-2.0","homepage":"https://0xbridges.ai","keywords":["react-native","bottom-sheet","top-sheet","sheet","gesture","reanimated","modal"],"repository":{"type":"git","url":"git+https://github.com/0xbridges/sheet.git"},"description":"Performance-first bottom & top sheets for React Native.","maintainers":[{"name":"zeroxbridges","email":"bridges@symmetry.fi"}],"readme":"# @0xbridges/sheet\n### Performant React Native Bottom & Top Sheets\n\n[![npm](https://img.shields.io/npm/v/@0xbridges/sheet)](https://www.npmjs.com/package/@0xbridges/sheet)   ![GitHub Repo stars](https://img.shields.io/github/stars/0xbridges/sheet)\n<img width=\"1954\" height=\"786\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/8fff6400-c4e7-497f-b1e5-2b2bff1f6637\" />\n<video width=\"470\" height=\"960\" alt=\"Video\" src=\"https://github.com/user-attachments/assets/f8940e78-7e61-42ec-ad3d-40169e83b201\" />\n\n`@0xbridges/sheet` is a React Native library for performant bottom sheets and top sheets where:\n\n- snap points accept pixels, percentages, `\"content\"` sizing, and measured anchors\n- detached sheets can morph into fullscreen with live corner-radius interpolation\n- dismissal rubber-bands at the floor instead of freezing when `dismissible={false}`\n- `collapsedHeight` adds a persistent peek state for maps-style attached sheets\n- scrollable content hands off between sheet drag and inner scroll automatically\n- the backdrop can be configured to pass through touches or close on tap\n\nThe library exports two main components: `BottomSheet` (slides up from the bottom) and `TopSheet` (slides down from the top). Both share the same snap-point system, gesture engine, and animation behavior.\n\nIf you want a shorter machine-oriented summary, see [`llms.txt`](./llms.txt).\n\n## What It Does\n\n`BottomSheet` is a flexible sheet container that slides up from the bottom of the screen. `TopSheet` is its counterpart that slides down from the top.\n\nBoth components handle:\n\n- spring-based snap animation\n- multi-stop snap points\n- horizontal gesture rejection\n- backdrop opacity interpolation\n- rubber-band overscroll at edges\n- detached floating card mode\n- fullscreen expansion with corner morph\n- scroll-to-drag handoff for inner scrollable content\n- anchor-based snap points measured from child layout\n\n## Core Behavior\n\nThese are the important behavior rules (they apply to both `BottomSheet` and `TopSheet` unless noted):\n\n1. The sheet snaps to the nearest snap point on release, projected by velocity.\n2. Swiping past the floor dismisses when `dismissible` is true.\n3. When `dismissible={false}`, swiping below the floor rubber-bands instead of dismissing. For `TopSheet` at fullscreen with `dismissible={false}`, dragging collapses back to the lowest snap point instead of dismissing.\n4. Backdrop opacity scales between floor and ceiling heights. Below the floor it fades toward zero.\n5. The backdrop blocks touches when `backdropPressBehavior=\"close\"` and passes them through with `\"none\"`.\n6. Anchor snap points are measured from child layout using `BottomSheetAnchor` / `TopSheetAnchor` marker views.\n7. `BottomSheetScrollView` / `TopSheetScrollView` enables scroll-to-drag handoff: the sheet drags until it reaches the ceiling, then inner scrolling takes over.\n8. Detached sheets float with configurable padding and can morph into fullscreen when `allowFullScreen` is set.\n\n### TopSheet-specific behavior\n\n- The drag handle starts at the **bottom** when collapsed and transitions to the **top** as the sheet expands to fullscreen.\n- At fullscreen the handle includes safe-area padding so content starts below the Dynamic Island / notch.\n- Fullscreen dismissal uses a slide-down gesture (like swiping a notification away). When `dismissible={false}`, this gesture collapses back to the lowest snap instead of dismissing.\n\n## Installation\n\nInstall the package and make sure your app already has the required gesture/animation setup.\n\n```bash\nnpm install @0xbridges/sheet react-native-gesture-handler react-native-reanimated react-native-safe-area-context\n```\n\nYour app must have:\n\n- `react-native-gesture-handler`\n- `react-native-reanimated`\n- `react-native-safe-area-context`\n- the normal configuration required by those libraries for your React Native or Expo setup\n\n## Quick Start\n\n### BottomSheet\n\n```tsx\nimport { BottomSheet } from \"@0xbridges/sheet\";\n\nexport function Example() {\n  return (\n    <BottomSheet\n      snapPoints={[\"content\", \"72%\"]}\n      allowFullScreen\n      initialSnapIndex={0}\n    >\n      <YourContent />\n    </BottomSheet>\n  );\n}\n```\n\n### TopSheet\n\n```tsx\nimport { TopSheet } from \"@0xbridges/sheet\";\n\nexport function Example() {\n  return (\n    <TopSheet\n      snapPoints={[\"42%\"]}\n      allowFullScreen\n      detached\n      detachedPadding={{ horizontal: 12, top: 12 }}\n      cornerRadius={56}\n      fullScreenCornerRadius={0}\n      initialSnapIndex={0}\n    >\n      <YourContent />\n    </TopSheet>\n  );\n}\n```\n\n## Snap Point Types\n\nSnap points define the heights the sheet can rest at. Pass them as `snapPoints`.\n\n### Pixel values\n\nA number is treated as an absolute height in points.\n\n```tsx\nsnapPoints={[220, 440]}\n```\n\n### Percentage values\n\nA string like `\"56%\"` is a percentage of the available height (viewport minus top inset).\n\n```tsx\nsnapPoints={[\"32%\", \"56%\", \"84%\"]}\n```\n\n### Content sizing\n\nThe string `\"content\"` measures the sheet content's natural height and uses it as a snap point.\n\n```tsx\nsnapPoints={[\"content\", \"72%\"]}\n```\n\nThe natural content height is measured once per presentation in an off-screen container, then the measurement tree unmounts so children render exactly once. If your content can change size while the sheet is open, prefer pixel, percentage, or anchor snap points instead.\n\n### Anchor snap points\n\nAnchor snap points are measured from `BottomSheetAnchor` marker views placed inside the sheet content. Each anchor produces a snap height equal to the anchor's bottom edge plus an optional offset.\n\n```tsx\nimport { createBottomSheetAnchor, BottomSheetAnchor } from \"@0xbridges/sheet\";\n\nconst summaryAnchor = createBottomSheetAnchor(\"summary\", { offset: 18 });\nconst detailAnchor = createBottomSheetAnchor(\"detail\", { offset: 18 });\n\n<BottomSheet snapPoints={[summaryAnchor, detailAnchor]} allowFullScreen>\n  <BottomSheetAnchor name=\"summary\">\n    <SummarySection />\n  </BottomSheetAnchor>\n  <BottomSheetAnchor name=\"detail\">\n    <DetailSection />\n  </BottomSheetAnchor>\n</BottomSheet>\n```\n\nThe `name` on `BottomSheetAnchor` must match the `key` passed to `createBottomSheetAnchor`.\n\n### Fullscreen ceiling\n\nWhen `allowFullScreen` is true, the sheet adds the full available height as an extra snap point above all configured stops.\n\n## Props Reference\n\n### `snapPoints`\n\nArray of snap point definitions. Accepts pixels, percentages, `\"content\"`, and anchor points. Default: `[\"content\"]`.\n\n### `initialSnapIndex`\n\nDefault: `0`\n\nWhich snap point to open at when the sheet first presents.\n\n### `open`\n\nControlled open state. When provided, the sheet is controlled and you must update this value in response to `onOpenChange`.\n\n### `defaultOpen`\n\nDefault: `true`\n\nInitial open state for uncontrolled usage.\n\n### `allowFullScreen`\n\nDefault: `false`\n\nAdds the full available height as an additional snap point above the highest configured stop.\n\n### `dismissible`\n\nDefault: `true`\n\nWhen true, swiping below the floor or tapping the backdrop dismisses the sheet. When false, the sheet rubber-bands at its lowest snap point.\n\n### `collapsedHeight`\n\nA snap point definition that becomes the sheet's persistent floor. The sheet cannot be dismissed below this height. Useful for maps-style peek states.\n\n### `detached`\n\nDefault: `false`\n\nFloats the sheet above the bottom edge with padding. Renders rounded corners on all four sides.\n\n### `detachedPadding`\n\nPadding around a detached sheet. Accepts a number (uniform) or an object with `bottom`, `horizontal`, `left`, `right`, `top`, and `vertical` fields.\n\n### `cornerRadius`\n\nDefault: `28`\n\nBorder radius for the sheet's top corners (all four corners when detached).\n\n### `fullScreenCornerRadius`\n\nCorner radius when the sheet is at fullscreen height. Defaults to `cornerRadius` for attached sheets and `0` for detached sheets. The radius interpolates live during drag.\n\n### `dragRegion`\n\nDefault: `\"sheet\"`\n\n- `\"sheet\"`: the entire sheet surface is draggable.\n- `\"handle\"`: only the handle area responds to drag. Use with `BottomSheetScrollView` for scroll-to-drag handoff.\n\n### `backdropOpacity`\n\nDefault: `0.34`\n\nMaximum backdrop opacity at the highest snap point. Set to `0` to hide the backdrop.\n\n### `backdropPressBehavior`\n\nDefault: `\"close\"`\n\n- `\"close\"`: tapping the backdrop dismisses the sheet.\n- `\"none\"`: the backdrop is visible but passes touches through to the content behind it.\n\n### `backdropColor`\n\nDefault: `\"#000000\"`\n\n### `backdropStyle`\n\nAdditional style applied to the backdrop pressable.\n\n### `handleVisible`\n\nDefault: `true`\n\nShows or hides the drag handle indicator.\n\n### `handleColor`\n\nDefault: `\"rgba(255, 255, 255, 0.42)\"`\n\n### `handleStyle`\n\nAdditional style applied to the handle area.\n\n### `sheetStyle`\n\nStyle applied to the sheet container. Use for background color.\n\n### `contentContainerStyle`\n\nStyle applied to the content wrapper inside the sheet.\n\n### `applyContentInset`\n\nDefault: `true`\n\nWhen true, adds bottom safe-area padding to the content container.\n\n### `contentBottomInset`\n\nDefault: `0`\n\nExtra bottom inset added to the content container, on top of the safe-area inset.\n\n### `topInset`\n\nDefault: `0`\n\nTop inset that limits the sheet's maximum height. When `allowFullScreen` is true, the larger of `topInset` and the safe-area top is used.\n\n### `style`\n\nStyle applied to the root overlay container.\n\n### `onOpenChange`\n\n`(open: boolean) => void`\n\nCalled when the sheet's open state changes. Required for controlled usage.\n\n### `onDismiss`\n\n`() => void`\n\nCalled when the sheet finishes dismissing.\n\n### `onSnapChange`\n\n`(index: number, height: number) => void`\n\nCalled when the sheet settles at a snap point. Receives the snap index and the resolved height in points.\n\n## Ref Methods\n\nAccess imperative methods via a ref.\n\n```tsx\nconst sheetRef = useRef<BottomSheetRef>(null);\n\n<BottomSheet ref={sheetRef} ...>\n\nsheetRef.current?.present();\nsheetRef.current?.dismiss();\nsheetRef.current?.expand();\nsheetRef.current?.snapToIndex(1);\n```\n\n### `present()`\n\nOpens the sheet at `initialSnapIndex`.\n\n### `dismiss()`\n\nCloses the sheet with a spring animation.\n\n### `expand()`\n\nSnaps to the highest configured snap point.\n\n### `snapToIndex(index)`\n\nSnaps to the snap point at the given index.\n\n## Scrollable Content\n\nUse `BottomSheetScrollView` for scrollable content inside the sheet.\n\nWhen `dragRegion=\"sheet\"`, the scroll view coordinates with the sheet gesture:\n\n- While the sheet is below its ceiling, dragging moves the sheet.\n- Once the sheet reaches its ceiling, inner scrolling activates.\n- Pulling down from scroll offset zero hands the gesture back to the sheet.\n\n```tsx\nimport { BottomSheet, BottomSheetScrollView, useBottomSheetInsets } from \"@0xbridges/sheet\";\n\nfunction SheetContent() {\n  const insets = useBottomSheetInsets();\n\n  return (\n    <BottomSheetScrollView\n      contentContainerStyle={{ paddingBottom: insets.bottom + 20 }}\n    >\n      <LongContent />\n    </BottomSheetScrollView>\n  );\n}\n\n<BottomSheet\n  snapPoints={[\"48%\", \"82%\"]}\n  allowFullScreen\n  dragRegion=\"sheet\"\n  applyContentInset={false}\n>\n  <SheetContent />\n</BottomSheet>\n```\n\n### `useBottomSheetInsets()`\n\nReturns `{ bottom: number }` representing the content bottom inset (safe area + `contentBottomInset`). Use this to add padding inside `BottomSheetScrollView`.\n\n### `onScroll`\n\n`BottomSheetScrollView` / `TopSheetScrollView` accept an optional `onScroll` callback typed as `(event: NativeScrollEvent) => void` — the raw native event, not a `NativeSyntheticEvent` wrapper. Read offsets directly off the event:\n\n```tsx\n<BottomSheetScrollView\n  onScroll={(event) => {\n    console.log(event.contentOffset.y);\n  }}\n/>\n```\n\nPass a Reanimated worklet to handle scroll on the UI thread with zero bridge crossings — recommended for parallax, sticky headers, and any per-frame work driven by scroll position:\n\n```tsx\nimport { useAnimatedScrollHandler, useSharedValue } from \"react-native-reanimated\";\n\nconst scrollY = useSharedValue(0);\nconst handleScroll = useAnimatedScrollHandler((event) => {\n  \"worklet\";\n  scrollY.value = event.contentOffset.y;\n});\n\n<BottomSheetScrollView onScroll={handleScroll}>...</BottomSheetScrollView>;\n```\n\nA regular JS function still works; it will be invoked via `runOnJS` with one bridge crossing per scroll event.\n\n## Detached Mode\n\nDetached sheets float above the bottom edge, rounded on all four corners.\n\n```tsx\n<BottomSheet\n  detached\n  detachedPadding={{ bottom: 16, horizontal: 16 }}\n  snapPoints={[\"46%\"]}\n>\n  <CardContent />\n</BottomSheet>\n```\n\nWhen combined with `allowFullScreen`, the sheet morphs from a floating card to fullscreen. The corner radius, margins, and shadow interpolate smoothly during the transition.\n\n## Non-Dismissible Sheets\n\n```tsx\n<BottomSheet\n  dismissible={false}\n  snapPoints={[220, \"56%\"]}\n>\n  <Content />\n</BottomSheet>\n```\n\nThe sheet rubber-bands at its lowest snap point instead of dismissing. Backdrop tap is ignored.\n\n## Collapsed Peek\n\n```tsx\n<BottomSheet\n  collapsedHeight={136}\n  dismissible={false}\n  backdropOpacity={0}\n  backdropPressBehavior=\"none\"\n  snapPoints={[\"48%\", \"84%\"]}\n>\n  <PeekContent />\n</BottomSheet>\n```\n\n`collapsedHeight` is prepended to the snap points as the floor. Combined with `dismissible={false}`, this creates a persistent peek that the user can swipe up to expand.\n\n---\n\n## TopSheet\n\n`TopSheet` is the top-of-screen counterpart to `BottomSheet`. It slides down from the top edge and supports the same snap-point system, detached mode, fullscreen morphing, and scroll handoff.\n\n### TopSheet Props Reference\n\n`TopSheet` accepts the same props as `BottomSheet` with these differences:\n\n| Prop | Default | Notes |\n|---|---|---|\n| `bottomInset` | `0` | Limits the sheet's maximum downward extent (analogous to `topInset` on BottomSheet). |\n| `contentTopInset` | `0` | Extra top inset added to the content container, on top of the safe-area inset. |\n| `applyContentInset` | `true` | When true, adds **top** safe-area padding to the content container (BottomSheet adds bottom). |\n\nAll other props (`snapPoints`, `allowFullScreen`, `detached`, `detachedPadding`, `cornerRadius`, `fullScreenCornerRadius`, `dismissible`, `dragRegion`, `backdropOpacity`, `backdropPressBehavior`, `handleVisible`, `handleColor`, `sheetStyle`, `contentContainerStyle`, `style`, `onOpenChange`, `onDismiss`, `onSnapChange`, etc.) work identically.\n\n### TopSheet Ref Methods\n\nSame as BottomSheet: `present()`, `dismiss()`, `expand()`, `snapToIndex(index)`.\n\n```tsx\nconst sheetRef = useRef<TopSheetRef>(null);\n\n<TopSheet ref={sheetRef} ...>\n\nsheetRef.current?.present();\nsheetRef.current?.dismiss();\nsheetRef.current?.expand();\nsheetRef.current?.snapToIndex(1);\n```\n\n### TopSheet Scrollable Content\n\nUse `TopSheetScrollView` for scrollable content inside a TopSheet, and `useTopSheetInsets` for safe-area padding.\n\n```tsx\nimport { TopSheet, TopSheetScrollView, useTopSheetInsets } from \"@0xbridges/sheet\";\n\nfunction SheetContent() {\n  const insets = useTopSheetInsets();\n\n  return (\n    <TopSheetScrollView\n      contentContainerStyle={{ paddingTop: insets.top + 10 }}\n    >\n      <LongContent />\n    </TopSheetScrollView>\n  );\n}\n\n<TopSheet\n  snapPoints={[\"48%\", \"82%\"]}\n  allowFullScreen\n  dragRegion=\"sheet\"\n  applyContentInset={false}\n>\n  <SheetContent />\n</TopSheet>\n```\n\n### TopSheet Detached to Fullscreen\n\nA detached TopSheet that morphs from a floating card to fullscreen:\n\n```tsx\n<TopSheet\n  detached\n  detachedPadding={{ horizontal: 12, top: 12 }}\n  cornerRadius={56}\n  fullScreenCornerRadius={0}\n  allowFullScreen\n  snapPoints={[\"42%\"]}\n>\n  <CardContent />\n</TopSheet>\n```\n\nWhen collapsed, the sheet is a rounded floating card at the top. Dragging the handle down expands it to fullscreen. Corner radius, margins, and shadow interpolate smoothly. The drag handle transitions from the bottom of the card to the top when fullscreen.\n\n### TopSheet Embedded in a Page\n\nA non-dismissible TopSheet can act as an embedded card that floats over scrollable page content:\n\n```tsx\nimport { TopSheet } from \"@0xbridges/sheet\";\nimport { useSafeAreaInsets } from \"react-native-safe-area-context\";\nimport { useWindowDimensions } from \"react-native\";\n\nexport function PageWithEmbeddedSheet() {\n  const { height: screenHeight } = useWindowDimensions();\n  const safeArea = useSafeAreaInsets();\n  const sheetHeight = screenHeight * 0.42 + safeArea.top + 12;\n\n  return (\n    <View style={{ flex: 1 }}>\n      <ScrollView contentContainerStyle={{ paddingTop: sheetHeight + 16 }}>\n        <PageContent />\n      </ScrollView>\n\n      <TopSheet\n        allowFullScreen\n        backdropOpacity={0}\n        backdropPressBehavior=\"none\"\n        cornerRadius={56}\n        defaultOpen\n        detached\n        detachedPadding={{ horizontal: 12, top: safeArea.top + 12 }}\n        dismissible={false}\n        dragRegion=\"sheet\"\n        fullScreenCornerRadius={0}\n        open\n        snapPoints={[\"42%\"]}\n      >\n        <CardContent />\n      </TopSheet>\n    </View>\n  );\n}\n```\n\nThe sheet floats at the top with no backdrop. Page content scrolls underneath it. Dragging the sheet expands it to fullscreen; from fullscreen it collapses back to the card (since `dismissible={false}` prevents full dismissal).\n\n### TopSheet Anchor Snap Points\n\nWorks the same as BottomSheet anchors, using `TopSheetAnchor` and `createTopSheetAnchor`:\n\n```tsx\nimport { TopSheet, TopSheetAnchor, createTopSheetAnchor } from \"@0xbridges/sheet\";\n\nconst headerAnchor = createTopSheetAnchor(\"header\", { offset: 18 });\n\n<TopSheet snapPoints={[headerAnchor]} allowFullScreen>\n  <TopSheetAnchor name=\"header\">\n    <HeaderSection />\n  </TopSheetAnchor>\n  <DetailSection />\n</TopSheet>\n```\n\n## Public Exports\n\nThe package exports:\n\n**BottomSheet**\n\n- `BottomSheet`\n- `BottomSheetAnchor`\n- `BottomSheetScrollView`\n- `createBottomSheetAnchor`\n- `useBottomSheetInsets`\n- `BottomSheetAnchorPoint` (type)\n- `BottomSheetAnchorProps` (type)\n- `BottomSheetDetachedPadding` (type)\n- `BottomSheetInsets` (type)\n- `BottomSheetProps` (type)\n- `BottomSheetRef` (type)\n- `BottomSheetSnapPoint` (type)\n- `BottomSheetScrollViewProps` (type)\n\n**TopSheet**\n\n- `TopSheet`\n- `TopSheetAnchor`\n- `TopSheetScrollView`\n- `createTopSheetAnchor`\n- `useTopSheetInsets`\n- `TopSheetAnchorPoint` (type)\n- `TopSheetAnchorProps` (type)\n- `TopSheetDetachedPadding` (type)\n- `TopSheetInsets` (type)\n- `TopSheetProps` (type)\n- `TopSheetRef` (type)\n- `TopSheetSnapPoint` (type)\n- `TopSheetScrollViewProps` (type)\n\n## Performance Notes\n\nThe library is designed around performance first.\n\nIt does the following:\n\n- keeps gesture math on the UI thread with Reanimated worklets\n- uses spring animations with velocity projection for natural snapping\n- measures anchor positions in an off-screen container to avoid layout thrashing\n- debounces anchor registration to batch rapid layout changes\n- structurally caches `snapPoints` and `collapsedHeight` so inline arrays (e.g. `snapPoints={[\"50%\", \"content\"]}`) don't cascade re-renders on every parent render\n- supports worklet `onScroll` handlers on the scroll view to skip the JS bridge during scroll\n\nFor best results:\n\n- keep children stable across renders\n- use `sheetStyle` for background color instead of wrapping in extra views\n- prefer pixel or percentage snap points over anchors when the heights are known ahead of time\n- set `applyContentInset={false}` when using `BottomSheetScrollView` and handle padding manually via `useBottomSheetInsets`\n\n## Local Demo\n\nThe repo includes a working Expo demo app in `example/`.\n\nIt covers bottom sheet scenarios (dynamic content, fixed height, percentage snaps, anchor snaps, detached cards, detached-to-fullscreen morphing, non-dismissible sheets, collapsible peek, multi-stop workflow snaps, scrollable fullscreen) and top sheet scenarios (detached, detached-to-fullscreen, scrollable fullscreen, and an embedded page example).\n\nFrom this package directory:\n\n```bash\nnpm install\nnpm run example:install\nnpm run example:start\n```\n","readmeFilename":"README.md"}