Drawer

A Drawer for Rust. Inspired by the amazing work of Emil Kowalski.

  • Rust/UI Icons - CopyCopy Demo

Vaul Drawer Demo

Click the button below to open the drawer

use leptos::prelude::*;

use crate::components::_coming_soon::drawer::{
    Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerHandle, DrawerTitle, DrawerTrigger,
};

#[component]
pub fn DemoDrawer() -> impl IntoView {
    view! {
        <div class="text-center">
            <h1 class="mb-4 text-4xl font-bold text-foreground">Vaul Drawer Demo</h1>
            <p class="mb-8 text-muted-foreground">Click the button below to open the drawer</p>
            <DrawerTrigger>Open Drawer</DrawerTrigger>
        </div>

        <Drawer>
            <DrawerContent>
                <DrawerHandle />
                <DrawerBody class="justify-center items-center">
                    <DrawerTitle>Drawer Title</DrawerTitle>
                    <DrawerDescription>Drag down to close or click outside.</DrawerDescription>

                    <DrawerClose>Close</DrawerClose>
                </DrawerBody>
            </DrawerContent>
        </Drawer>
    }
}

Installation

# Coming soon :)

Usage

// Coming soon 🦀

Examples

Family

  • Rust/UI Icons - CopyCopy Demo

Vaul Drawer Demo

Click the button below to open the drawer

use icons::{FileText, Lock, TriangleAlert, X};
use leptos::prelude::*;

use crate::components::_coming_soon::drawer::{Drawer, DrawerContent, DrawerTrigger};

#[component]
pub fn DemoDrawerFamily() -> impl IntoView {
    view! {
        <div class="text-center">
            <h1 class="mb-4 text-4xl font-bold text-foreground">Vaul Drawer Demo</h1>
            <p class="mb-8 text-muted-foreground">Click the button below to open the drawer</p>
            <DrawerTrigger>Open Drawer</DrawerTrigger>
        </div>

        <Drawer>
            <DrawerContent class="overflow-hidden right-4 left-4 pb-6 mx-auto mb-4 max-w-[361px] rounded-[36px]">

                // <!-- Content -->
                <header class="flex justify-between items-center mb-4 border-b h-[72px] border-neutral-100">
                    <h2 class="text-lg font-semibold text-foreground">Options</h2>
                    <button
                        data-name="DrawerClose"
                        class="flex justify-center items-center rounded-full transition-colors size-8 bg-neutral-100 text-neutral-500 hover:bg-neutral-200"
                    >
                        <X class="size-3" />
                    </button>
                </header>

                <div class="space-y-3">
                    <button class="flex gap-4 items-center px-4 w-full h-12 text-base font-medium rounded-2xl transition-colors bg-neutral-100 text-foreground hover:bg-neutral-200">
                        <Lock class="size-[18px]" />
                        <span>View Private Key</span>
                    </button>

                    <button class="flex gap-4 items-center px-4 w-full h-12 text-base font-medium rounded-2xl transition-colors bg-neutral-100 text-foreground hover:bg-neutral-200">
                        <FileText class="size-[18px]" />
                        <span>View Recovery Phase</span>
                    </button>

                    <button class="flex gap-4 items-center px-4 w-full h-12 text-base font-medium text-red-600 bg-red-50 rounded-2xl transition-colors hover:bg-red-100">
                        <TriangleAlert class="size-[18px]" />
                        <span>Remove Wallet</span>
                    </button>
                </div>
            </DrawerContent>
        </Drawer>
    }
}

Focus

  • Rust/UI Icons - CopyCopy Demo

Focus Drawer Demo

Click the button below to open the drawer

use leptos::prelude::*;

use crate::components::_coming_soon::drawer::{
    Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerHandle, DrawerTitle, DrawerTrigger,
};
use crate::components::ui::input::Input;
use crate::components::ui::label::Label;
use crate::components::ui::textarea::Textarea;

#[component]
pub fn DemoDrawerFocus() -> impl IntoView {
    view! {
        <div class="text-center">
            <h1 class="mb-4 text-4xl font-bold text-foreground">Focus Drawer Demo</h1>
            <p class="mb-8 text-muted-foreground">Click the button below to open the drawer</p>
            <DrawerTrigger>Open Drawer</DrawerTrigger>
        </div>

        <Drawer>
            <DrawerContent>
                <DrawerHandle />

                <DrawerBody>
                    <DrawerTitle class="text-center">Drawer Title</DrawerTitle>
                    <DrawerDescription>
                        Test keyboard navigation: Press Tab to cycle through elements, Shift+Tab to go back, and Escape to close.
                    </DrawerDescription>

                    // <!-- Form elements for testing tab trapping -->
                    <form class="flex flex-col gap-4">
                        <div class="flex flex-col gap-2">
                            <Label html_for="test-input">Text Input</Label>
                            <Input attr:id="test-input" attr:placeholder="Type something..." />
                        </div>

                        <div class="flex flex-col gap-2">
                            <Label html_for="test-email">Email</Label>
                            <Input attr:id="test-email" attr:r#type="email" attr:placeholder="[email protected]" />
                        </div>

                        <div class="flex flex-col gap-2">
                            <Label html_for="test-select">Select</Label>
                            <select
                                id="test-select"
                                class="py-2 px-3 w-full rounded-md border focus:ring-2 focus:outline-none border-neutral-200 focus:ring-primary"
                            >
                                <option>Option 1</option>
                                <option>Option 2</option>
                                <option>Option 3</option>
                            </select>
                        </div>

                        <div class="flex flex-col gap-2">
                            <Label html_for="test-textarea">Textarea</Label>
                            <Textarea attr:id="test-textarea" attr:rows="4" attr:placeholder="Write a comment..." />
                        </div>
                    </form>

                    <DrawerClose>Close</DrawerClose>

                </DrawerBody>

            </DrawerContent>
        </Drawer>
    }
}

Nested

  • Rust/UI Icons - CopyCopy Demo

Nested Drawer Demo

Click the button below to open the drawer

use leptos::prelude::*;
use leptos_meta::Stylesheet;

use crate::components::_coming_soon::drawer::DrawerHandle;

#[component]
pub fn DemoDrawerNested() -> impl IntoView {
    view! {
        // <Stylesheet href="/drawer.css" />
        <Stylesheet href="/drawer_nested.css" />

        <div>
            <div class="text-center">
                <h1 class="mb-4 text-4xl font-bold text-foreground">Nested Drawer Demo</h1>
                <p class="mb-8 text-muted-foreground">Click the button below to open the drawer</p>
                <button
                    id="drawer-trigger"
                    class="py-3 px-6 font-medium rounded-lg transition-opacity hover:opacity-90 bg-primary text-primary-foreground"
                >
                    Open Drawer
                </button>
            </div>

            // <!-- Drawer Overlay -->
            <div
                id="drawer-overlay"
                class="hidden drawer-overlay"
                data-vaul-overlay=""
                data-vaul-snap-points="false"
                data-vaul-animate="true"
                data-state="closed"
            ></div>

            // <!-- Drawer Content -->
            <div
                id="drawer-content"
                class="hidden bg-white drawer-content rounded-t-[10px]"
                data-vaul-drawer=""
                data-vaul-drawer-direction="bottom"
                data-vaul-snap-points="false"
                data-vaul-animate="true"
                data-state="closed"
                style="--initial-transform: 100%;"
            >
                // <!-- Drawer Body -->
                <div class="flex overflow-y-auto flex-col flex-1 gap-4 p-6">
                    // <!-- Handle inside DrawerBody -->
                    <DrawerHandle />

                    <h2 class="text-2xl font-bold text-foreground">Parent Drawer</h2>
                    <p class="text-muted-foreground">
                        Click the button below to open a nested drawer and see the parent drawer scale down.
                    </p>

                    <button
                        id="open-nested-drawer"
                        class="py-2 px-4 w-full font-medium rounded-md transition-opacity hover:opacity-90 focus:ring-2 focus:ring-offset-2 focus:outline-none bg-secondary text-secondary-foreground focus:ring-secondary"
                    >
                        Open Nested Drawer
                    </button>

                    <button
                        id="drawer-close"
                        class="py-2 px-4 w-full font-medium rounded-md transition-opacity hover:opacity-90 focus:ring-2 focus:ring-offset-2 focus:outline-none bg-primary text-primary-foreground focus:ring-primary"
                    >
                        Close
                    </button>
                </div>
            </div>

            // <!-- Nested Drawer Overlay -->
            <div
                id="nested-drawer-overlay"
                class="hidden drawer-overlay overlay-nested"
                data-vaul-overlay=""
                data-vaul-snap-points="false"
                data-vaul-animate="true"
                data-state="closed"
            ></div>

            // <!-- Nested Drawer Content -->
            <div
                id="nested-drawer-content"
                class="hidden bg-white drawer-content drawer-nested rounded-t-[10px]"
                data-vaul-drawer=""
                data-vaul-drawer-direction="bottom"
                data-vaul-snap-points="false"
                data-vaul-animate="true"
                data-state="closed"
                style="--initial-transform: 100%;"
            >
                // <!-- Nested Drawer Body -->
                <div class="flex overflow-y-auto flex-col flex-1 gap-4 p-6">
                    // <!-- Handle inside DrawerBody -->
                    <DrawerHandle />

                    <h2 class="text-2xl font-bold text-foreground">Nested Drawer</h2>
                    <p class="text-muted-foreground">
                        Notice how the parent drawer scales down when this nested drawer opens.
                    </p>

                    <button
                        id="nested-drawer-close"
                        class="py-2 px-4 w-full font-medium rounded-md transition-opacity hover:opacity-90 focus:ring-2 focus:ring-offset-2 focus:outline-none bg-primary text-primary-foreground focus:ring-primary"
                    >
                        Close Nested Drawer
                    </button>
                </div>
            </div>
        </div>

        <script type="module" src="/drawer_v3.js"></script>
    }
}

Non Dismissable

  • Rust/UI Icons - CopyCopy Demo

Vaul Drawer Demo

Click the button below to open the drawer

use leptos::prelude::*;

use crate::components::_coming_soon::drawer::{
    Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerHandle, DrawerTitle, DrawerTrigger,
};

#[component]
pub fn DemoDrawerNonDismissable() -> impl IntoView {
    view! {
        <div class="text-center">
            <h1 class="mb-4 text-4xl font-bold text-foreground">Vaul Drawer Demo</h1>
            <p class="mb-8 text-muted-foreground">Click the button below to open the drawer</p>
            <DrawerTrigger>Open Drawer</DrawerTrigger>
        </div>

        <Drawer>
            <DrawerContent style="--initial-transform: 100%; pointer-events: auto;" dismissible="false">
                <DrawerHandle />

                <DrawerBody>
                    <DrawerTitle>Are you absolutely sure?</DrawerTitle>
                    <DrawerDescription>
                        This action cannot be undone. This will permanently delete your account and remove your data from our servers.
                    </DrawerDescription>

                    <DrawerClose>Confirm</DrawerClose>
                </DrawerBody>
            </DrawerContent>
        </Drawer>
    }
}

Scrollable

  • Rust/UI Icons - CopyCopy Demo

Scrollable Drawer Demo

Click the button below to open the drawer

use leptos::prelude::*;

use crate::components::_coming_soon::drawer::{
    Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerHandle, DrawerTitle, DrawerTrigger,
};

#[component]
pub fn DemoDrawerScrollable() -> impl IntoView {
    view! {
        <div class="text-center">
            <h1 class="mb-4 text-4xl font-bold text-foreground">Scrollable Drawer Demo</h1>
            <p class="mb-8 text-muted-foreground">Click the button below to open the drawer</p>
            <DrawerTrigger>Open Drawer</DrawerTrigger>
        </div>

        <Drawer>
            <DrawerContent style="--initial-transform: 100%; pointer-events: auto;">
                <DrawerHandle />

                <DrawerBody class="overflow-y-auto pr-4 max-h-[300px]">
                    <DrawerTitle class="text-center">Ira Glass on Taste</DrawerTitle>
                    <DrawerDescription>
                        Nobody tells this to people who are beginners, I wish someone told me. All of us who do creative work, we get into it because we have good taste.
                    </DrawerDescription>
                    <DrawerDescription>
                        "But there is this gap. For the first couple years you make stuff, it's just not that good. It's trying to be good, it has potential, but it's not. But your taste, the thing that got you into the game, is still killer. And your taste is why your work disappoints you. A lot of people never get past this phase, they quit."
                    </DrawerDescription>
                    <DrawerDescription>
                        " Most people I know who do interesting, creative work went through years of this. We know our work doesn't have this special thing that we want it to have. We all go through this. And if you are just starting out or you are still in this phase, you gotta know its normal and the most important thing you can do is do a lot of work"
                    </DrawerDescription>
                    <DrawerDescription>
                        " Put yourself on a deadline so that every week you will finish one story. It is only by going through a volume of work that you will close that gap, and your work will be as good as your ambitions. And I took longer to figure out how to do this than anyone I've ever met. It's gonna take awhile. It's normal to take awhile. You've just gotta fight your way through."
                    </DrawerDescription>

                    <DrawerClose>Close</DrawerClose>
                </DrawerBody>
            </DrawerContent>
        </Drawer>
    }
}

Side

  • Rust/UI Icons - CopyCopy Demo

Vaul Drawer Demo

Click the button below to open the drawer

use leptos::prelude::*;

use crate::components::_coming_soon::drawer::{
    Drawer, DrawerBody, DrawerContent, DrawerDescription, DrawerPosition, DrawerTitle, DrawerTrigger,
};

#[component]
pub fn DemoDrawerSide() -> impl IntoView {
    view! {
        <div class="text-center">
            <h1 class="mb-4 text-4xl font-bold text-foreground">Vaul Drawer Demo</h1>
            <p class="mb-8 text-muted-foreground">Click the button below to open the drawer</p>
            <DrawerTrigger>Open Drawer</DrawerTrigger>
        </div>

        <Drawer>
            <DrawerContent
                position=DrawerPosition::Right
                class="top-0 bottom-0 left-auto h-full max-h-full rounded-t-none w-[300px] rounded-l-[10px]"
            >
                <DrawerBody>
                    <DrawerTitle class="mt-4 text-base">Different Directions</DrawerTitle>
                    <DrawerDescription>It supports all directions.</DrawerDescription>
                    <DrawerDescription>
                        This one specifically is not touching the edge of the screen, but that is not required for a side drawer.
                    </DrawerDescription>
                </DrawerBody>
            </DrawerContent>
        </Drawer>
    }
}

Side Floating

  • Rust/UI Icons - CopyCopy Demo

Vaul Drawer Demo

Click the button below to open the drawer

use leptos::prelude::*;

use crate::components::_coming_soon::drawer::{
    Drawer, DrawerContent, DrawerDescription, DrawerPosition, DrawerTitle, DrawerTrigger, DrawerVariant,
};

#[component]
pub fn DemoDrawerSideFloating() -> impl IntoView {
    view! {
        <div class="text-center">
            <h1 class="mb-4 text-4xl font-bold text-foreground">Vaul Drawer Demo</h1>
            <p class="mb-8 text-muted-foreground">Click the button below to open the drawer</p>
            <DrawerTrigger>Open Drawer</DrawerTrigger>
        </div>

        <Drawer>
            <DrawerContent
                position=DrawerPosition::Right
                variant=DrawerVariant::Floating
                class="top-2 right-2 bottom-2 left-auto max-h-full outline-none w-[300px] rounded-[16px] rounded-t-[16px]"
                style="--initial-transform: calc(100% + 8px);"
            >
                <DrawerTitle class="mt-4 text-base">Different Directions</DrawerTitle>
                <DrawerDescription>It supports all directions.</DrawerDescription>
                <DrawerDescription>
                    This one specifically is not touching the edge of the screen, but that is not required for a side drawer.
                </DrawerDescription>
            </DrawerContent>
        </Drawer>
    }
}

Side Scrollable

  • Rust/UI Icons - CopyCopy Demo

Vaul Drawer Demo

Click the button below to open the drawer

use leptos::prelude::*;

use crate::components::_coming_soon::drawer::{
    Drawer, DrawerBody, DrawerContent, DrawerDescription, DrawerPosition, DrawerTitle, DrawerTrigger,
};

#[component]
pub fn DemoDrawerSideScrollable() -> impl IntoView {
    view! {
        <div class="text-center">
            <h1 class="mb-4 text-4xl font-bold text-foreground">Vaul Drawer Demo</h1>
            <p class="mb-8 text-muted-foreground">Click the button below to open the drawer</p>
            <DrawerTrigger>Open Drawer</DrawerTrigger>
        </div>

        <Drawer>
            <DrawerContent
                position=DrawerPosition::Left
                class="top-0 bottom-0 right-auto h-full max-h-full rounded-t-none w-[300px] rounded-r-[10px]"
            >
                <DrawerBody class="overflow-y-auto pr-4 h-full">
                    <DrawerTitle class="mt-4 text-base">Scrollable Side Drawer</DrawerTitle>
                    <DrawerDescription>
                        This drawer contains 50 scrollable items to demonstrate side scrolling behavior.
                    </DrawerDescription>

                    <div class="mt-4 space-y-2">
                        {(1..=50)
                            .map(|i| {
                                view! {
                                    <div class="p-3 rounded-md border bg-muted border-border">
                                        <p class="text-sm font-medium">Item {i}</p>
                                        <p class="text-xs text-muted-foreground">This is item number {i}in the list</p>
                                    </div>
                                }
                            })
                            .collect_view()}
                    </div>
                </DrawerBody>
            </DrawerContent>
        </Drawer>
    }
}