BottomSheet
Konsta-based sheet that slides up from the bottom of the viewport for short, mobile-first confirmations (archive, filter, pick). It teleports to <body>, so no ancestor transform/filter/overflow can trap it.
vue
<script setup lang="ts">
import RyderBottomSheet from "@opeolluwa/ryder/components/feedback/BottomSheet.vue"
</script>
<template>
<RyderBottomSheet v-model:open="open" title="Archive order" @submit="open = false">
<p>Sheet body.</p>
</RyderBottomSheet>
</template>Examples
vue
<script setup lang="ts">
import { ref } from "vue";
import BottomSheet from "../../../src/components/feedback/BottomSheet.vue";
const open = ref(false);
</script>
<template>
<div class="space-y-3">
<button
type="button"
class="rounded-lg bg-primary-600 px-4 py-2 text-sm font-medium text-white hover:bg-primary-700"
@click="open = true"
>
Open bottom sheet
</button>
<BottomSheet
v-model:open="open"
title="Archive orders"
description="Archived orders leave the active list."
submit-label="Archive"
@submit="open = false"
@cancel="open = false"
>
<p class="text-sm text-gray-500 dark:text-white/50">
Body content scrolls between the fixed header and the footer row.
</p>
<p class="pt-2 text-sm text-gray-500 dark:text-white/50">
Tapping the backdrop (or the X) emits
<code class="font-mono">cancel</code> and closes the sheet on its own.
</p>
</BottomSheet>
</div>
</template>Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Required; the sheet's heading, rendered next to the close button. |
description | string | undefined | undefined | Subtext under the heading (hidden when empty). |
loading | boolean | undefined | undefined | Falsy by default; truthy shows a spinner on submit and disables the close and footer buttons. |
submitDisabled | boolean | undefined | undefined | Falsy by default; truthy disables only the default submit button (e.g. until a form is valid). |
dismissible | boolean | true | When false, the close button is hidden and backdrop taps are ignored. |
submitLabel | string | undefined | undefined | Falls back to "Submit" in the template when unset. |
cancelLabel | string | undefined | undefined | Falls back to "Cancel" in the template when unset. |
Emits
| Event | Payload | When |
|---|---|---|
submit | — | The built-in submit button was tapped. The sheet stays open — the app closes it. A custom footer slot never triggers it. |
cancel | — | Close button, backdrop tap (when dismissible), or the built-in cancel button was tapped. The sheet also closes itself. |
leave | — | open flipped to false; the close transition starts. |
after:leave | — | 400 ms after leave — mirrors konsta's duration-400, so Teleported content can unmount at the right time. |
Slots
| Slot | Props | Content |
|---|---|---|
default | — | Scrollable body between the header and the footer (max-h-[85dvh]). |
footer | — | Replaces the built-in cancel/submit row entirely. |
Notes
- Konsta dependency (
kSheet) plus Nuxt UI'sUButtonfor the close button; the konsta theme CSS (konsta/vue/theme.css) must be loaded in the app. - The built-in submit button emits
submitand does not close the sheet — the app owns closing. Cancel does both. - Body area handles the safe-area:
padding-bottom: env(safe-area-inset-bottom). - The 400 ms mirror of konsta's transition exists so
after:leavelands at the right moment — the same contract Nuxt UI overlays use.