Overview
The Bottom Sheet component slides up from the bottom of the viewport, presenting contextual actions, modal forms, sharing trays, or filter menus optimized for mobile thumb navigation. It includes native touch swipe gestures, mouse drag support, backdrop dismissal, keyboard accessibility (Escape to dismiss), and smooth spring-like animations.
Unlike standard desktop modals, bottom sheets preserve user flow on mobile devices by staying anchored to the bottom edge and offering intuitive swipe-down dismiss gestures.
<!-- Trigger button -->
<button type="button" class="btn btn-primary" data-boodoo-toggle="bottom-sheet" data-boodoo-target="#demoSheet">
<span>Open Bottom Sheet</span>
</button>
<!-- Bottom Sheet structure -->
<div class="bottom-sheet" id="demoSheet" tabindex="-1" role="dialog" aria-labelledby="sheetTitle" aria-modal="true">
<div class="bottom-sheet-handle-wrap" aria-hidden="true">
<div class="bottom-sheet-handle"></div>
</div>
<div class="bottom-sheet-header">
<h5 class="bottom-sheet-title" id="sheetTitle"><span>Quick Actions</span></h5>
<button type="button" class="btn-close" data-boodoo-dismiss="bottom-sheet" aria-label="Close"></button>
</div>
<div class="bottom-sheet-body">
<p>Sheet content with touch gestures and drag-to-dismiss.</p>
</div>
<div class="bottom-sheet-footer">
<button type="button" class="btn btn-secondary" data-boodoo-dismiss="bottom-sheet">
<span>Cancel</span>
</button>
<button type="button" class="btn btn-primary" data-boodoo-dismiss="bottom-sheet">
<span>Confirm</span>
</button>
</div>
</div>
Anatomy & Sizing Variants
Bottom sheets support three distinct height sizing presets to accommodate different mobile content densities:
| Class Name | Max Height | Ideal Use Case |
|---|---|---|
.bottom-sheet |
90vh (default) | General-purpose actions, detailed forms, and long content lists with internal scroll. |
.bottom-sheet-half |
50vh | Compact action sheets, share menus, confirmation dialogs, and filter trays. |
.bottom-sheet-full |
100vh | Full-screen mobile workflows, checkout wizards, and modal detail views. |
Touch & Drag Gestures
Boodoo's Bottom Sheet includes built-in touch and mouse gesture recognition with zero external dependencies:
- Handle Dragging: The
.bottom-sheet-handle-wrapbar provides an accessible grab target. Dragging down smoothly translates the sheet in real time. - Swipe-to-Dismiss Threshold: Dragging beyond 120 pixels downwards automatically triggers dismissal with a fluid slide-down exit animation.
- Snap-Back Elasticity: Releasing the drag before the threshold smoothly snaps the sheet back to its fully expanded position.
- Backdrop Click: Tapping or clicking outside the sheet dismisses the sheet and frees body scroll lock.
Accessibility & Keyboard Interactions
Boodoo ensures mobile overlay components meet WCAG 2.2 AA accessibility guidelines:
- Keyboard Dismissal: Pressing the Escape key immediately closes the active bottom sheet and cleans up document event listeners.
- ARIA Dialog Semantics: Apply
role="dialog"andaria-modal="true"to the root.bottom-sheetelement. Label the dialog usingaria-labelledby="[id-of-title]". - Body Scroll Lock: While the bottom sheet is open,
overflow: hiddenis applied todocument.bodyto prevent background scrolling underneath the sheet. - Handle Wrap ARIA: The drag handle is marked
aria-hidden="true"so screen readers focus directly on meaningful titles and actionable controls.
JavaScript API & Lifecycle
Manage Bottom Sheet instances programmatically via vanilla JavaScript:
import { BottomSheet } from 'boodoo';
// Retrieve or create an instance
const sheetEl = document.getElementById('demoSheet');
const sheet = BottomSheet.getOrCreateInstance(sheetEl);
// Programmatic control
sheet.show();
sheet.hide();
sheet.toggle();
// Full lifecycle teardown (detaches gesture listeners & removes backdrop)
sheet.dispose();
Lifecycle Events
Custom events allow you to hook into bottom sheet state transitions:
| Event Type | Description |
|---|---|
boodoo.show.bottomSheet |
Fires immediately when show() is called. Cancellable via event.preventDefault(). |
boodoo.shown.bottomSheet |
Fires when the sheet has completed its slide-up transition and backdrop is visible. |
boodoo.hide.bottomSheet |
Fires immediately when hide() is called. Cancellable via event.preventDefault(). |
boodoo.hidden.bottomSheet |
Fires when the sheet has fully slid off-screen and the backdrop is removed from the DOM. |
Multi-Framework Integration
Integrate Bottom Sheet across your favorite frontend stacks:
React Integration
import React from 'react';
import { useDisclosure } from 'boodoo/react';
export function QuickShareSheet() {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<>
<button type="button" className="btn btn-primary" onClick={onOpen}>
<span>Share Document</span>
</button>
{isOpen && (
<div className="bottom-sheet show" role="dialog" aria-modal="true">
<div className="bottom-sheet-handle-wrap">
<div className="bottom-sheet-handle"></div>
</div>
<div className="bottom-sheet-header">
<h5 className="bottom-sheet-title"><span>Share Options</span></h5>
<button type="button" className="btn-close" onClick={onClose} aria-label="Close"></button>
</div>
<div className="bottom-sheet-body">
<p>Copy shareable link or export as PDF.</p>
</div>
</div>
)}
</>
);
}
Vue 3 Integration
<script setup>
import { useDisclosure } from 'boodoo/vue';
const { isOpen, open, close } = useDisclosure();
</script>
<template>
<button type="button" class="btn btn-primary" @click="open">
<span>Open Actions</span>
</button>
<div v-if="isOpen" class="bottom-sheet show" role="dialog" aria-modal="true">
<div class="bottom-sheet-handle-wrap"><div class="bottom-sheet-handle"></div></div>
<div class="bottom-sheet-header">
<h5 class="bottom-sheet-title"><span>Actions</span></h5>
<button type="button" class="btn-close" @click="close" aria-label="Close"></button>
</div>
<div class="bottom-sheet-body">
<p>Perform rapid batch tasks on mobile.</p>
</div>
</div>
</template>
React Native Mobile
For native iOS and Android apps, use Boodoo's pure primitive surfaces without DOM dependencies:
import React, { useState } from 'react';
import { View, Text, StyleSheet, Modal, Pressable } from 'react-native';
import { Button } from 'boodoo/react-native';
export function MobileBottomSheet() {
const [visible, setVisible] = useState(false);
return (
<View style={styles.container}>
<Button variant="primary" label="Open Native Sheet" onPress={() => setVisible(true)} />
<Modal visible={visible} transparent animationType="slide" onRequestClose={() => setVisible(false)}>
<Pressable style={styles.backdrop} onPress={() => setVisible(false)}>
<View style={styles.sheet}>
<View style={styles.handle} />
<Text style={styles.title}>Native Action Sheet</Text>
<Text style={styles.body}>Rendered with zero DOM dependencies.</Text>
</View>
</Pressable>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
backdrop: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'flex-end' },
sheet: { backgroundColor: '#ffffff', borderTopLeftRadius: 20, borderTopRightRadius: 20, padding: 20 },
handle: { width: 40, height: 4, backgroundColor: '#cbd5e1', borderRadius: 2, alignSelf: 'center', marginBottom: 16 },
title: { fontSize: 18, fontWeight: '600', marginBottom: 8 },
body: { fontSize: 14, color: '#64748b' }
});
Related Components
Explore other contextual overlays and dialog systems in Boodoo:
- Modal: Centered dialogs and lightboxes for desktop and tablet screens.
- Offcanvas: Full-height sliding sidebars from left, right, top, or bottom edges.
- Speed Dial: Floating mobile action buttons with expanding quick-action menus.
Quick Share & Actions
Swipe down on the handle or drag anywhere to dismiss this bottom sheet.
Filter Results
This is a half-height sheet variant using .bottom-sheet-half, ideal for quick option selections.
Full-Height Sheet
This is a full-height sheet variant using .bottom-sheet-full, covering the full viewport on mobile for immersive task flows.