The Complete Production SOP: How to Deploy and Run Vaul Unstyled iOS-Style Mobile Drawer Gesture Primitive in Autonomous AI Agents
Production SOP to install & configure Vaul mobile drawer gesture primitive in Claude Code, Codex, and Cursor with snap points and physics-based drag.
Technical Overview & Architectural Context
Autonomous coding agents frequently stumble when implementing touch-first UI components like mobile drawers. Without a standardized primitive SOP, agents routinely invent fragile custom touch listeners, forget viewport scaling meta configurations, mishandle backdrop click boundaries, or construct broken CSS touch-action rules that prevent native scroll chaining. These ad-hoc implementations introduce severe stuttering on physical iOS Safari devices, trap touch events, and trigger hydration mismatches between server-rendered layouts and dynamic client viewports. The 'vaul-mobile-drawer-gesture' skill enforces Emil Kowalski's battle-tested Vaul primitive (built on Radix UI dialog primitives). By equipping Claude Code, Codex, Cursor, and Windsurf with deterministic component hierarchy guidelines—mandating Drawer.Root, Drawer.Portal, Drawer.Overlay, and Drawer.Content alongside explicit snap point typing and grab handle accessibility—the agent bypasses guesswork. It consistently produces native-feeling, 60fps drag-to-dismiss sheets with background page scaling and keyboard dismissal.
Prerequisites & Environment Requirements
Verified Search Intents & Long-Tail Queries Solved
This operational tutorial is engineered to provide deterministic solutions for these developer search queries:
Step-by-Step Execution Playbook
Local Environment Setup & SKILL.md Mounting
Mount the skill manifest into your local agent skills registry or workspace configuration folder.
Autonomous runtimes discover capabilities through standardized SKILL.md locations. In Claude Code, Cursor, or Windsurf, mount the skill under the project root's `.agents/skills` or `.claude/skills` directory so the agent's context indexer parses frontmatter triggers such as 'build mobile bottom drawer vaul' and 'snap points vaul drawer'.
mkdir -p .agents/skills/vaul-mobile-drawer-gesture && curl -fsSL https://askills.dev/api/skills/vaul-mobile-drawer-gesture/raw -o .agents/skills/vaul-mobile-drawer-gesture/SKILL.md---
name: "vaul-mobile-drawer-gesture"
description: "Build fluid, touch-friendly mobile bottom drawers with physics-based drag-to-dismiss."
metadata:
author: "Emil Kowalski"
version: "1.0.0"
runtime_compatibility:
- "codex"
- "claude-code"
- "cursor"
- "windsurf"
triggers:
- "build mobile bottom drawer vaul"
- "snap points vaul drawer"
- "drag to dismiss drawer"
permissions:
fs_read: true
fs_write: "workspace"
shell_exec: false
net_access: false
---Keep the SKILL.md focused on semantic anatomy and edge-case contracts. Agents do not need complete 500-line sample files in context when concise architectural patterns yield identical zero-shot accuracy.
Ensure runtime hot-reloading detects new skills by restarting Claude Code CLI or triggering a re-index via Cursor Command Palette (Developer: Reload Window).
Dependency Installation & Component Scaffolding
Install the Vaul package and scaffold a clean, typed React drawer primitive.
Vaul requires `vaul` as a peer dependency alongside React. If your project uses Next.js App Router, the drawer component must include the 'use client' directive because touch gesture recognition relies on client-side DOM events and window metrics.
pnpm add vaul'use client';
import * as React from 'react';
import { Drawer } from 'vaul';
interface MobileDrawerProps {
open?: boolean;
onOpenChange?: (open: boolean) => void;
trigger?: React.ReactNode;
title: string;
description?: string;
children: React.ReactNode;
snapPoints?: (string | number)[];
}
export function MobileDrawer({
open,
onOpenChange,
trigger,
title,
description,
children,
snapPoints = ['320px', 1],
}: MobileDrawerProps) {
const [snap, setSnap] = React.useState<number | string | null>(snapPoints[0]);
return (
<Drawer.Root
open={open}
onOpenChange={onOpenChange}
snapPoints={snapPoints}
activeSnapPoint={snap}
setActiveSnapPoint={setSnap}
shouldScaleBackground
>
{trigger && <Drawer.Trigger asChild>{trigger}</Drawer.Trigger>}
<Drawer.Portal>
<Drawer.Overlay className="fixed inset-0 z-50 bg-black/40 backdrop-blur-sm transition-opacity duration-300" />
<Drawer.Content className="fixed bottom-0 left-0 right-0 z-50 mt-24 flex max-h-[96%] flex-col rounded-t-[10px] bg-white dark:bg-zinc-900 outline-none">
<div className="flex-1 overflow-y-auto rounded-t-[10px] p-4">
<div className="mx-auto mb-4 h-1.5 w-12 flex-shrink-0 rounded-full bg-zinc-300 dark:bg-zinc-700" />
<div className="mx-auto max-w-md">
<Drawer.Title className="text-lg font-semibold text-zinc-900 dark:text-zinc-100">
{title}
</Drawer.Title>
{description && (
<Drawer.Description className="mt-1 text-sm text-zinc-500 dark:text-zinc-400">
{description}
</Drawer.Description>
)}
<div className="mt-4">{children}</div>
</div>
</div>
</Drawer.Content>
</Drawer.Portal>
</Drawer.Root>
);
}When using `shouldScaleBackground`, ensure your main layout container includes the `vaul-drawer-wrapper` attribute or wraps children inside a `<div vaul-drawer-wrapper className="bg-white min-h-screen">` container to enable seamless iOS-style background scale effects.
Forgetting the explicit grab handle bar or failing to set `flex-1 overflow-y-auto` inside `Drawer.Content` will break internal scrolling on long content views.
Intent Matching & Prompt Execution
Invoke the autonomous agent with unambiguous trigger phrases to build custom sheet experiences.
Provide the agent with exact functional specifications such as multiple snap steps, nested actions, or form inputs. The agent will reference the mounted skill to select the correct snap point API and accessibility attributes.
# Example invoking Claude Code with explicit trigger phrase
claude "build mobile bottom drawer vaul for user profile settings with snap points ['250px', '500px', 1] and drag to dismiss"Always state numeric or percentage snap points explicitly in prompts to prevent the agent from mixing up ratio indices and pixel heights.
Do not mix Radix Dialog and Vaul Drawer imports in the same component file without aliasing, as both expose `Root`, `Portal`, and `Content` names.
Verification, Linting & Quality Gate
Verify TypeScript compilation, DOM hydration, and styling constraints before merging.
Execute compile-time type verification and lint tests to ensure `activeSnapPoint` typing matches `snapPoints` array values and CSS rules conform to project standards.
pnpm tsc --noEmit && pnpm lint{
"scripts": {
"typecheck": "tsc --noEmit",
"lint": "next lint || eslint ."
}
}Inspect the generated pull request or git diff specifically for touch-action properties on scrollable inner lists.
Failing to mark parent pages with dynamic viewport configurations can cause iOS Safari virtual keyboard expansion to push drawer content outside the visible screen.
Real-World Scenario: Implementing an iOS-Style Mobile Cart & Checkout Drawer with Snap Points
“Refactor our mobile checkout summary into an unstyled iOS-style bottom sheet. Use vaul-mobile-drawer-gesture rules to build mobile bottom drawer vaul with snap points ['200px', '480px', 1], drag to dismiss drawer, grab handle, and background scaling on Next.js 14.”
Runtime Troubleshooting & Failure Modes
Root Cause: Occurs when Next.js Server Components import Vaul directly without the 'use client' directive, or when importing named exports incorrectly instead of `{ Drawer } from 'vaul'`.
// Ensure client boundary at top of component file:
'use client';
import { Drawer } from 'vaul';Root Cause: Vaul looks for an element tagged with `vaul-drawer-wrapper` in the DOM tree to apply scale down transitions when the drawer opens.
// In app/layout.tsx:
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className="bg-background font-sans antialiased">
<div vaul-drawer-wrapper="" className="min-h-screen bg-white dark:bg-zinc-950">
{children}
</div>
</body>
</html>
);
}Frequently Asked Questions (Vaul Unstyled iOS-Style Mobile Drawer Gesture Primitive SOP)
Q:Can I use Vaul Unstyled iOS-Style Mobile Drawer Gesture Primitive across Codex, Claude Code, and Cursor simultaneously?
Yes. This skill adheres strictly to the universal SKILL.md specification. Once installed in `.agents/skills` or `.claude/skills`, all leading runtimes read the frontmatter triggers, dependency checklists, and procedural constraints identically.
Q:How does Vaul Unstyled iOS-Style Mobile Drawer Gesture Primitive prevent context window overflow during long agent sessions?
It provides precise atomic recipes and exact structural contracts instead of multi-page generic UI tutorials. By scoping instructions to critical components (`Drawer.Root`, `snapPoints`, `vaul-drawer-wrapper`), agents spend under 350 tokens per reference.
Q:Does this skill require escalated root or container permissions?
No. The skill is rated 98/100 on security, requiring only read/write workspace permissions (`fs_write: 'workspace'`). It requires no shell execution or external network access during agent runtime.
Ready to equip your autonomous agent with Vaul Unstyled iOS-Style Mobile Drawer Gesture Primitive?
Copy the complete audited SKILL.md specification or download the 1-click zip bundle directly from our verified registry.