Skip to main content

Overview

The Drawer (or Slideover) component provides a panel that slides in from any edge of the screen. It’s ideal for navigation menus, filters, or additional content without disrupting the main view.

Components

The Drawer is composed of multiple subcomponents:
  • Drawer.Root - The main container that manages drawer state
  • Drawer.Content - The drawer content area
  • Drawer.Backdrop - Optional backdrop overlay
  • Drawer.Header - Optional header section
  • Drawer.Body - Main content body
  • Drawer.Footer - Optional footer section
  • Drawer.Title - Drawer title element
  • Drawer.Description - Drawer description element

Basic Usage

<script>
  import { Drawer } from '@svelte-atoms/core/components/drawer';
  
  let isOpen = $state(false);
</script>

<button onclick={() => (isOpen = true)}>Open Drawer</button>

<Drawer.Root bind:open={isOpen}>
  <Drawer.Backdrop />
  <Drawer.Content>
    <Drawer.Header>
      <Drawer.Title>Navigation</Drawer.Title>
      <Drawer.Description>Site navigation menu</Drawer.Description>
    </Drawer.Header>
    
    <Drawer.Body>
      <nav>
        <a href="/home">Home</a>
        <a href="/about">About</a>
        <a href="/contact">Contact</a>
      </nav>
    </Drawer.Body>
  </Drawer.Content>
</Drawer.Root>

Drawer.Root Props

open
boolean
default:"false"
Controls the open/closed state of the drawer. Can be bound with bind:open.
disabled
boolean
default:"false"
When true, disables drawer interactions.
portal
string | PortalBond
default:"'root.l1'"
Portal destination for rendering the drawer. Uses layer 1 by default.
onclose
(event: Event, bond: DrawerBond) => void
Callback fired when the drawer closes.
factory
Factory<DrawerBond>
Optional factory function to customize drawer bond creation.
as
keyof HTMLElementTagNameMap
default:"'div'"
HTML element type for the root element.
initial
(node: HTMLElement, bond: DrawerBond) => void
Initial animation state. Default uses animateDrawerRoot({ duration: 0 }).
animate
(node: HTMLElement, bond: DrawerBond) => void
Animation function for the drawer root. Default uses animateDrawerRoot({}).
children
Snippet<[{ drawer: DrawerBond }]>
Render function that receives the drawer bond instance.

Drawer.Content Props

animate
(node: HTMLElement) => void
Animation function for the content. Default uses animateDrawerContent() with left side.
initial
(node: HTMLElement) => void
Initial animation state.
enter
(node: HTMLElement) => void
Animation function when content enters.
exit
(node: HTMLElement) => void
Animation function when content exits.

Drawer.Backdrop Props

Extends standard HTML div attributes. Renders a semi-transparent backdrop behind the drawer.

Drawer.Header Props

Extends standard HTML div attributes. Use for styling and layout of the header area.

Drawer.Body Props

Extends standard HTML div attributes. Contains the main drawer content. Extends standard HTML div attributes. Typically contains action buttons.

Drawer.Title Props

Extends standard HTML heading attributes. Default element is h2.

Drawer.Description Props

Extends standard HTML paragraph attributes. Default element is p.

Animation Hooks

animateDrawerRoot

Animates the drawer backdrop with opacity transition.
import { animateDrawerRoot } from '@svelte-atoms/core/components/drawer/motion';

<Drawer.Root animate={animateDrawerRoot({ duration: 0.3, ease: 'easeInOut' })}>
  <!-- content -->
</Drawer.Root>
Parameters:
  • duration (number): Animation duration in seconds. Default: DURATION.fast / 1000
  • delay (number): Animation delay in seconds. Default: 0
  • ease (Easing | Easing[]): Easing function. Default: 'easeInOut'

animateDrawerContent

Animates the drawer content sliding in from the specified side.
import { animateDrawerContent } from '@svelte-atoms/core/components/drawer/motion';

<Drawer.Content 
  animate={animateDrawerContent({ ease: 'easeOut', side: 'right', duration: 0.3 })}
>
  <!-- content -->
</Drawer.Content>
Parameters:
  • duration (number): Animation duration in seconds. Default: DURATION.fast / 1000
  • delay (number): Animation delay in seconds. Default: 0
  • ease (Easing | Easing[]): Easing function. Default: 'easeInOut'
  • side ('left' | 'right' | 'top' | 'bottom'): Side to slide from. Default: 'left'

Examples

Left Drawer (Default)

<script>
  import { Drawer, animateDrawerContent, animateDrawerRoot } from '@svelte-atoms/core/components/drawer';
  
  let isOpen = $state(false);
</script>

<button onclick={() => (isOpen = true)}>Open Menu</button>

<Drawer.Root 
  bind:open={isOpen}
  animate={animateDrawerRoot({})}
>
  <Drawer.Content
    class="flex min-h-full w-md flex-col border-r p-8 shadow-md"
    animate={animateDrawerContent({ ease: 'easeOut', side: 'left' })}
  >
    <Drawer.Header class="flex items-center justify-between">
      <Drawer.Title class="text-lg font-semibold">Navigation</Drawer.Title>
      <Drawer.Description class="text-sm text-neutral-500">
        Browse the site sections
      </Drawer.Description>
    </Drawer.Header>
    
    <div>
      <nav>
        <a href="/">Home</a>
        <a href="/about">About</a>
      </nav>
    </div>
  </Drawer.Content>
</Drawer.Root>

Right Drawer

<Drawer.Root bind:open={isOpen}>
  <Drawer.Content
    class="inset-y-0 flex w-md flex-col border-l p-8 shadow-sm"
    animate={animateDrawerContent({ ease: 'easeOut', side: 'right' })}
  >
    <div>Right side content</div>
  </Drawer.Content>
</Drawer.Root>

Top Drawer

<Drawer.Root bind:open={isOpen}>
  <Drawer.Content
    class="flex w-full min-w-full flex-col border-b p-8 shadow-md"
    animate={animateDrawerContent({ ease: 'easeOut', side: 'top' })}
  >
    <div>Top content</div>
  </Drawer.Content>
</Drawer.Root>

Bottom Drawer

<Drawer.Root bind:open={isOpen}>
  <Drawer.Content
    class="flex w-full min-w-full flex-col border-t p-8 shadow-md"
    animate={animateDrawerContent({ ease: 'easeOut', side: 'bottom' })}
  >
    <div>Bottom content</div>
  </Drawer.Content>
</Drawer.Root>

With Backdrop

<Drawer.Root 
  bind:open={isOpen}
  class="backdrop-blur-md"
>
  <Drawer.Backdrop />
  <Drawer.Content animate={animateDrawerContent({ side: 'left' })}>
    <!-- content -->
  </Drawer.Content>
</Drawer.Root>

Attachment Hooks

Drawer provides utility functions for common interactions:

clickoutDrawer

Closes the drawer when clicking outside the content area.
import { clickoutDrawer } from '@svelte-atoms/core/components/drawer';

<Drawer.Content
  {@attach clickoutDrawer((_, bond) => {
    bond?.state?.close?.();
  })}
>
  <!-- content -->
</Drawer.Content>

toggleDrawer

Toggles the drawer open state.
import { toggleDrawer } from '@svelte-atoms/core/components/drawer';

<button {@attach toggleDrawer()}>Toggle Drawer</button>

openDrawer

Opens the drawer.
import { openDrawer } from '@svelte-atoms/core/components/drawer';

<button {@attach openDrawer()}>Open Drawer</button>

closeDrawer

Closes the drawer.
import { closeDrawer } from '@svelte-atoms/core/components/drawer';

<button {@attach closeDrawer()}>Close Drawer</button>

Accessibility

  • Manages focus when opened
  • Can be closed by clicking outside (with clickoutDrawer attachment)
  • Supports keyboard navigation within content
  • Backdrop provides visual separation from main content

Extension Points

You can extend prop types for customization:
declare module '@svelte-atoms/core/components/drawer' {
  interface DrawerExtendProps {
    customProp?: string;
  }
}

Build docs developers (and LLMs) love