Skip to content

Drawer

A pixel-art sidebar panel that slides in from the edge of the screen.

NameCategoryDescriptionTypeDefault
showOverlay? StyleWhether to show the overlay backdropbooleantrue
size? SizeDrawer widthstring'300px'
v-model? StateWhether the drawer is openbooleanfalse
placement? BehaviorDrawer placement side'left' | 'right''left'
lockScroll? BehaviorWhether to lock body scroll when openbooleantrue
closeOnClickOverlay? BehaviorWhether clicking the overlay closes the drawerbooleantrue
closeOnEsc? BehaviorWhether pressing Escape closes the drawerbooleantrue
title? ContentDrawer titlestring
update:modelValue? EventTriggered when open state changes(value: boolean) => void
open? EventTriggered when the drawer opens() => void
close? EventTriggered when the drawer closes() => void
default? SlotMain page content
sidebar? SlotDrawer sidebar content
open? ExposeOpen the drawer() => void
close? ExposeClose the drawer() => void
toggle? ExposeToggle drawer open/close() => void

Basic Usage

Use v-model to control the drawer open/close state. Content goes in the sidebar slot.

<template>
  <div>
    <px-button @click="open = true">Open Drawer</px-button>
    <px-drawer v-model="open">
      <p>Main page content</p>
      <template #sidebar>
        <p>Sidebar content goes here.</p>
      </template>
    </px-drawer>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const open = ref(false);
</script>

Placement

Use placement to slide the drawer from the left or right edge.

<template>
  <div class="demo-drawer">
    <px-button @click="leftOpen = true">Left (default)</px-button>
    <px-button @click="rightOpen = true">Right</px-button>
    <px-drawer v-model="leftOpen" placement="left">
      <template #sidebar>
        <p>Left sidebar content</p>
      </template>
    </px-drawer>
    <px-drawer v-model="rightOpen" placement="right">
      <template #sidebar>
        <p>Right sidebar content</p>
      </template>
    </px-drawer>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const leftOpen = ref(false);
const rightOpen = ref(false);
</script>

<style scoped>
.demo-drawer {
  display: flex;
  gap: 12px;
}
</style>

Custom Size

Use size to set the drawer width. Accepts any CSS width value.

<template>
  <div class="demo-drawer">
    <px-button @click="open200 = true">200px</px-button>
    <px-button @click="open50 = true">50%</px-button>
    <px-drawer v-model="open200" size="200px">
      <template #sidebar>
        <p>Narrow drawer (200px)</p>
      </template>
    </px-drawer>
    <px-drawer v-model="open50" size="50%">
      <template #sidebar>
        <p>Wide drawer (50%)</p>
      </template>
    </px-drawer>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const open200 = ref(false);
const open50 = ref(false);
</script>

<style scoped>
.demo-drawer {
  display: flex;
  gap: 12px;
}
</style>

Overlay

Control the backdrop overlay with show-overlay and close-on-click-overlay.

<template>
  <div class="demo-drawer">
    <px-button @click="withOverlay = true">With Overlay</px-button>
    <px-button @click="noOverlay = true">No Overlay</px-button>
    <px-button @click="noClose = true">No Close on Overlay</px-button>
    <px-drawer v-model="withOverlay">
      <template #sidebar>
        <p>Click overlay to close</p>
      </template>
    </px-drawer>
    <px-drawer v-model="noOverlay" :show-overlay="false">
      <template #sidebar>
        <p>No overlay background</p>
        <px-button @click="noOverlay = false">Close</px-button>
      </template>
    </px-drawer>
    <px-drawer v-model="noClose" :close-on-click-overlay="false">
      <template #sidebar>
        <p>Overlay click won't close</p>
        <px-button @click="noClose = false">Close</px-button>
      </template>
    </px-drawer>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const withOverlay = ref(false);
const noOverlay = ref(false);
const noClose = ref(false);
</script>

<style scoped>
.demo-drawer {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}
</style>

Title

Use the title prop to render a header with a pixel-dashed divider.

<template>
  <div>
    <px-button @click="open = true">Drawer with Title</px-button>
    <px-drawer v-model="open" title="Settings">
      <template #sidebar>
        <p>The header shows a pixel-dashed divider below the title.</p>
      </template>
    </px-drawer>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const open = ref(false);
</script>

Disable Escape Close

Set close-on-esc to false to prevent the drawer from closing on Escape key press.

<template>
  <div>
    <px-button @click="open = true">Escape Disabled</px-button>
    <px-drawer v-model="open" :close-on-esc="false">
      <template #sidebar>
        <p>Press Escape — nothing happens.</p>
        <px-button @click="open = false">Close Manually</px-button>
      </template>
    </px-drawer>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const open = ref(false);
</script>