Component

Vue Bottom Sheet Component

<DomBottomSheet>

A modal content sheet with configurable snap heights, a draggable and keyboard-operable handle, scrolling body, and pinned footer. Uses the shared drawer for focus, dismissal, and scroll locking. Works standalone or in the DomAppShell overlay slot.

Read, expand, and reply

Project discussion

1200px

Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
import { ref } from 'vue';
import { DomBottomSheet, DomButton, DomTextareaComposer } from '@getdom/studio';

const open = ref(false);
const reply = ref('');
const replies = ref([]);

/** Adds a local comment and clears the draft while leaving the sheet open. */
function addComment(value) {
	if (!value.trim()) return;
	replies.value.push(value.trim());
	reply.value = '';
}
</script>

<template>
	<div class="flex min-h-48 items-center justify-center bg-secondary/20 p-6">
		<DomButton @click="open = true">Open project notes</DomButton>
		<DomBottomSheet v-model="open" title="Project notes" :snap-points="[0, 0.4, 0.7, 0.95]" :initial-snap-point="0.7">
			<div class="space-y-5 text-sm leading-7 text-muted-fg">
				<p class="font-semibold text-canvas-fg">A calmer project overview</p>
				<p>Keep the next action visible, give recent work a little more space, and move secondary details into the project sheet.</p>
				<p>The first review is on Thursday. We’ll walk through the inbox, check how messages move into the archive, and try replying on a phone.</p>
				<p>Bring one example of a task that currently takes too many steps. We’ll use those examples to choose the next improvement.</p>
				<p>The aim is a useful first release: a clear list, a reliable reply flow, and a way to undo changes.</p>
				<div v-for="(comment, index) in replies" :key="index" class="rounded-xl bg-secondary p-4"><p class="font-medium text-canvas-fg">You</p><p class="whitespace-pre-wrap">{{ comment }}</p></div>
				<p v-if="replies.length" role="status" class="text-xs">Comment added to this preview.</p>
			</div>
			<template #footer><DomTextareaComposer v-model="reply" chrome="none" aria-label="Comment on project notes" placeholder="Add a comment…" :rows="1" :max-rows="3" submit-label="Add comment" :submit-on-enter="false" @submit="addComment" /></template>
		</DomBottomSheet>
	</div>
</template>

Sizing and interaction

snapPoints are fractions of the available sheet viewport, capped to leave a top safe-area gap. This example uses [0, 0.4, 0.7, 0.95]. Zero enables drag dismissal. Without zero, dragging stops at the smallest visible height. Escape and Close remain available.

Drag the handle to resize; the body keeps native scrolling at every height. Click the handle to cycle, or focus it and use Arrow Up/Down, Home, and End. Focusing an input expands the sheet to its largest stop. An interrupted gesture restores the previous stop.

Use v-model:snapPoint for a controlled height. Otherwise the sheet resets to initialSnapPoint on each open. Content remains mounted, so unsent input survives dismissal.

The standalone sheet teleports to its document body. Inside DomAppShell, put it in #overlay for containment, safe areas, and the shell’s keyboard sizing. Explicit contained requires a positioned, sized parent. The footer stays pinned while the body scrolls.

Try the full-screen inbox for the combined swipe, sheet, and reply flow. Check software keyboard behaviour on target devices; an iframe preview cannot reproduce its containing page’s visual viewport.

Reference

Props

Control props

NameTypeTSDefaultDescription
modelValuebooleanbooleanfalseWhether the modal sheet is open.
titlestringstring''Visible title and accessible dialog name.
labelstringstring'Details'Accessible dialog name when supplying a custom header without a title.
snapPoints
ts
type Array<number> = unknown;
arrayArray<number>[0,0.5,0.9]Fractions of the available height, from 0 to 1. Include 0 to allow drag dismissal.
initialSnapPointnumbernumber0.5Preferred height on each open, rounded to the nearest non-zero stop.
snapPoint
ts
type number | null = unknown;
numbernumber | nullOptional controlled height. Use v-model:snapPoint; null uses internal state.
containedbooleanbooleanDefaults to contained in DomAppShell, viewport otherwise. Use the shell overlay slot.
staticbooleanbooleanfalseDisable backdrop dismissal. Close and Escape remain available; omit zero from snapPoints to disable drag dismissal.
keyboardInset
ts
type number | null = unknown;
numbernumber | nullOptional native keyboard inset for standalone sheets. Contained sheets inherit the resized shell.

Auto-generated from Bottom sheet.props and inline _edit hints.

Slots

NameScopeDescription
#(default){ close, expand }Scrollable sheet content.
#headerCustom heading content beside the close button.
#footer{ close, expand }Pinned controls, such as a reply composer.

Events

NamePayloadDescription
@update:modelValuebooleanOpen or dismiss the sheet.
@update:snapPointnumberThe selected fraction of the available sheet viewport.

Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.

Keyboard

  • Handle: Arrow Up / DownExpand or collapse to the next visible stop.
  • Handle: Home / EndChoose the smallest or largest visible stop.
  • Handle: Enter / SpaceCycle through visible snap heights.
  • EscapeDismiss and return focus to the opener.