npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue-bottom-sheets

v0.4.0

Published

Accessible, draggable bottom sheet component for Vue 3 with snap points — inspired by react-spring-bottom-sheet.

Readme

vue-bottom-sheets

Accessible, draggable bottom sheet component for Vue 3 with snap points — inspired by react-spring-bottom-sheet.

Installation

npm install vue-bottom-sheets
# or: pnpm add vue-bottom-sheets

Usage

<script setup lang="ts">
import { ref } from 'vue'
import { BottomSheet } from 'vue-bottom-sheet'

const open = ref(false)
</script>

<template>
  <button @click="open = true">Open</button>

  <BottomSheet v-model:open="open" aria-label="Example sheet">
    <template #header>
      <h2>Title</h2>
    </template>

    <p>Sheet content goes here.</p>

    <template #footer>
      <button @click="open = false">Close</button>
    </template>
  </BottomSheet>
</template>

Global registration (plugin)

import { createApp } from 'vue'
import VueBottomSheet from 'vue-bottom-sheets'

createApp(App).use(VueBottomSheet) // registers <BottomSheet> globally

Snap points

Provide a fixed array of pixel heights, or a function that computes them from the measured geometry:

<BottomSheet
  v-model:open="open"
  :snap-points="({ maxHeight, minHeight }) => [Math.min(minHeight, 120), maxHeight * 0.5, maxHeight - 40]"
  :default-snap="({ snapPoints }) => snapPoints[1]"
/>

The resolver receives { height, maxHeight, headerHeight, footerHeight, minHeight } (all in pixels).

Imperative API

<script setup lang="ts">
import { ref } from 'vue'
import { BottomSheet } from 'vue-bottom-sheet'

const sheet = ref<InstanceType<typeof BottomSheet> | null>(null)

// Jump to the tallest snap point.
function expand() {
  sheet.value?.snapTo(({ snapPoints }) => snapPoints[snapPoints.length - 1])
}
</script>

<template>
  <BottomSheet ref="sheet" v-model:open="open">…</BottomSheet>
</template>

Props

| Prop | Type | Default | Description | | ---------------------- | ------------------------------------- | ------------ | ---------------------------------------------------------------- | | open | boolean | false | Controlled open state. Use with v-model:open. | | snapPoints | number[] \| (props) => number[] | content size | Heights (px) the sheet can rest at. | | defaultSnap | number \| (props) => number | largest | Snap point used when opening. | | blocking | boolean | true | Render backdrop, trap focus and lock scroll. | | closeOnBackdropClick | boolean | true | Dismiss when the backdrop is clicked. | | closeOnEscape | boolean | true | Dismiss when Escape is pressed. | | scrollLocking | boolean | true | Lock body scroll while open and blocking. | | expandOnContentDrag | boolean | false | Allow dragging the sheet from its content area. | | handle | boolean | true | Render the built-in handle pill (and header strip). Set false to use your own handle. | | maxHeight | number | viewport | Hard cap on sheet height (px). | | ariaLabel | string | — | Accessible label applied as aria-label. | | skipInitialTransition| boolean | false | Skip the open animation on first mount. | | theme | BottomSheetTheme | — | Visual overrides (colors, radius, shadow, …). See Theming. |

Events

| Event | Payload | Description | | -------------- | ---------- | --------------------------------------------------- | | update:open | boolean | Emitted for v-model:open. | | dismiss | — | User requested dismissal (backdrop/escape/drag). | | snap | number | Sheet settled on a snap point (px). | | spring-start | — | A transition started. | | spring-end | — | A transition finished. |

Slots

| Slot | Slot props | Description | | --------- | ----------------- | ------------------------------------------------------ | | default | dragHandleProps | Scrollable body content. | | header | dragHandleProps | Sticky header above the content; the drag handle. | | footer | — | Sticky footer below the content (rendered if present). |

Custom drag handle

By default the sheet renders its own handle strip. To make a consumer-supplied element the grab region instead — for example a header that already lives inside your content — set :handle="false" and spread the dragHandleProps slot prop onto that element:

<BottomSheet v-model:open="open" :handle="false">
  <template #default="{ dragHandleProps }">
    <!-- Your own header IS the drag handle; the ✕ still clicks. -->
    <header class="my-header" v-bind="dragHandleProps">
      <h2>Title</h2>
      <button @click="open = false">✕</button>
    </header>

    <p>Scrollable body…</p>
  </template>
</BottomSheet>

dragHandleProps wires up the pointer listeners and applies touch-action: none so the browser won't claim the touch for scrolling. A drag only begins once the pointer moves past a small threshold, so taps/clicks on buttons inside the handle still fire. The slot prop is also available on #header.

Theming

Pass a theme object — each key is optional and falls back to the default. Numeric keys (maxWidth, radius, transitionDuration, zIndex) also accept a plain number (px, px, ms, unitless):

<BottomSheet
  v-model:open="open"
  :theme="{
    radius: 24,
    background: '#111',
    color: '#fff',
    backdrop: 'rgba(0, 0, 0, 0.6)',
    handle: 'rgba(255, 255, 255, 0.3)',
  }"
/>

| Key | Type | CSS variable | Default | | -------------------- | ------------------ | -------------------------- | ---------------------------------- | | maxWidth | string \| number | --vbs-max-width | 640px | | radius | string \| number | --vbs-radius | 16px | | background | string | --vbs-bg | #ffffff | | color | string | --vbs-color | #1a1a1a | | backdrop | string | --vbs-backdrop | rgba(0, 0, 0, 0.45) | | handle | string | --vbs-handle | rgba(0, 0, 0, 0.18) | | shadow | string | --vbs-shadow | 0 -8px 40px rgba(0, 0, 0, 0.18) | | transitionDuration | string \| number | --vbs-transition-duration| 320ms | | transitionEasing | string | --vbs-transition-easing | cubic-bezier(0.22, 1, 0.36, 1) | | zIndex | string \| number | --vbs-z-index | 1000 |

Prefer CSS? The same --vbs-* custom properties can be set on .vbs (or any ancestor) instead of using the prop. prefers-reduced-motion is respected automatically.

Development

pnpm install
pnpm dev          # run the playground at http://localhost:5173
pnpm test         # run unit tests
pnpm build        # type-check + build the library to dist/
pnpm lint         # lint & autofix

License

GPL-3.0-or-later © Pavel Sergienko