| Author: | viliket |
|---|---|
| Views Total: | 39 views |
| Official Page: | Go to website |
| Last Update: | October 31, 2025 |
| License: | MIT |
Preview:

Description:
Pure Web Bottom Sheet is a lightweight, framework-agnostic Web Component that creates smooth, mobile-native bottom sheets using CSS scroll snap mechanics.
The component supports server-side rendering, works across frameworks like React and Vue, and integrates with standard HTML elements like <dialog> for proper accessibility.
It’s ideal for developers who want authentic mobile interaction patterns without the overhead of animation libraries or framework-specific solutions.
Features:
- CSS-Driven Interactions: Uses CSS scroll snap and scroll-driven animations for smooth sheet positioning.
- Framework Agnostic: Works as a standard Web Component with vanilla HTML or integrates via wrapper components for React and Vue with built-in SSR support.
- Minimal JavaScript: Core functionality runs entirely in CSS on modern browsers, with JavaScript handling only backward compatibility and optional features like swipe-to-dismiss.
- Native Element Integration: Leverages HTML
<dialog>and Popover API for proper modal behavior, focus management, and keyboard navigation without custom accessibility implementations. - Declarative Shadow DOM Support: Includes SSR templates that prevent flash of unstyled content when rendering bottom sheets open on initial page load.
- Multiple Scroll Modes: Supports standard positioning, nested scrolling where content scrolls independently, and expand-to-scroll mode where sheets must reach full height before content becomes scrollable.
- Customizable Snap Points: Define multiple sheet positions using CSS custom properties with any length units.
See It In Action:
Use Cases:
- Mobile-First Web Apps: Perfect for displaying contextual actions, filters, or additional information without navigating to a new screen, mimicking a native app experience.
- Interactive Overlays: Use it for things like cookie consents, selection menus, or configuration panels where you need a non-intrusive UI that can be easily dismissed.
- Data Display: An excellent choice for showing details on a map or supplemental information in a dashboard without covering the main content completely.
- Complex Forms: Break down long forms into manageable steps within a sheet, which is especially useful on smaller viewports.
How To Use It (Vanilla JS):
1. Install the Pure Web Bottom Sheet package via npm for build tool workflows:
# NPM $ npm install pure-web-bottom-sheet
2. Or use the CDN for quick prototyping:
// Non-dismissible bottom sheet
import { BottomSheet } from "https://cdn.jsdelivr.net/npm/pure-web-bottom-sheet/+esm";
// Dismissible bottom sheet
import { registerSheetElements } from "https://cdn.jsdelivr.net/npm/pure-web-bottom-sheet/+esm";3. Create a non-dismissable bottom sheet:
<bottom-sheet tabindex="0">
<!-- Define snap points -->
<div slot="snap" style="--snap: 25%"></div>
<div slot="snap" style="--snap: 50%" class="initial"></div>
<div slot="snap" style="--snap: 75%"></div>
<div slot="header">
<h2>Bottom Sheet Header</h2>
</div>
<div slot="footer">
<h2>Bottom Sheet Footer</h2>
</div>
<p>Bottomsheet Content</p>
</bottom-sheet>
<script type="module">
import { BottomSheet } from "https://cdn.jsdelivr.net/npm/pure-web-bottom-sheet/+esm";
customElements.define("bottom-sheet", BottomSheet);
</script>4. For dismissible bottom sheets that open and close, wrap the bottom sheet in a <dialog> element with the dialog manager:
<bottom-sheet-dialog-manager>
<dialog id="sheet-dialog">
<bottom-sheet swipe-to-dismiss tabindex="0">
<div slot="snap" style="--snap: 40%"></div>
<div slot="snap" style="--snap: 80%" class="initial"></div>
<div slot="header">
<h2>Bottom Sheet Header</h2>
</div>
<p>Bottom Sheet Content</p>
</bottom-sheet>
</dialog>
</bottom-sheet-dialog-manager>
<button id="open-btn">Open Sheet</button>
<script type="module">
import { registerSheetElements } from "https://cdn.jsdelivr.net/npm/pure-web-bottom-sheet/+esm";
registerSheetElements();
document.getElementById("open-btn").addEventListener("click", () => {
document.getElementById("sheet-dialog").showModal();
});
</script>5. <bottom-sheet> attributes:
content-height: Makes the sheet’s max height dependent on its content size.nested-scroll: Allows the content inside the sheet to scroll independently of the sheet’s snap position.expand-to-scroll: A modifier fornested-scrollwhere content only becomes scrollable after the sheet is fully expanded.swipe-to-dismiss: Enables swiping the sheet down to close it, typically used within a<dialog>.nested-scroll-optimization: Applies resize optimization for nested scroll mode to prevent reflows during sheet resizing. Experimental feature.
6. Slots:
- default: The main content of the bottom sheet.
snap: Used to define snap points. Each element should have a--snapCSS custom property (e.g.,style="--snap: 50%"). Addclass="initial"to one to set the default open position.header: Content that sticks to the top of the sheet.footer: Content that sticks to the bottom of the sheet.
7. CSS Custom Properties
--sheet-max-height: Controls the maximum height of the sheet (e.g.,90dvh).--sheet-background: Sets the background of the sheet.--sheet-border-radius: Adjusts the top corner border radius.
8. Events
snap-position-change: ACustomEventthat fires when the sheet snaps to a new position. Thedetail.snapPositionwill be"0"(fully expanded),"2"(fully collapsed/closed), or"1"(intermediate).
9. The web component also provides convenient wrappers for React, Vue, and Astro. See the readme.md in the package for more details.
FAQs:
Q: How do I prevent content scrolling until the sheet fully expands?
A: Use both nested-scroll and expand-to-scroll attributes together. The sheet must reach its maximum height before the content area becomes scrollable. This creates the behavior seen in many native mobile apps where you pull the sheet up first, then scroll content. On browsers without scroll-driven animation support, the component automatically switches the overflow-y property based on snap position.
Q: Can I animate the sheet opening with custom timing?
A: The dialog wrapper element supports CSS transitions on the translate property. Override the default transition by targeting bottom-sheet-dialog-manager::slotted(dialog) with your preferred timing function and duration. The sheet uses scroll snap for positioning after it opens, but the initial appearance from offscreen is pure CSS transition.
Q: My bottom sheet doesn’t close when I swipe down. What’s wrong?
A: First, ensure the <bottom-sheet> element has the swipe-to-dismiss attribute. Second, it must be placed inside a <dialog> element that is managed by a <bottom-sheet-dialog-manager>. The manager listens for the snap event that indicates the sheet is in the “closed” position and then calls dialog.close(). This feature won’t work on a standalone sheet.
Q: Can I have the sheet’s height adjust to its content automatically?
A: Yes. By default, the sheet expands to --sheet-max-height. If you want it to only be as tall as its content, add the content-height attribute to the <bottom-sheet> element. This is useful for sheets with dynamic or minimal content.







