This component no longer exists. It was in ruixen-ui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-12 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
Bottom Drawers
ruixen-ui/bottom-drawersRemovedComponent
Snap-point bottom sheet — three snap heights (peek, half, full), drag between them, velocity-aware, progressive content reveal.
Live preview
live · rendered by the registry
Rendered by ruixen-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ruixen.com/r/bottom-drawers.jsonSource
1"use client";23import { useRef, useState, type ReactNode } from "react";4import { motion } from "motion/react";56/**7 * Bottom Drawers — snap-point bottom sheet.8 *9 * Three snap heights: peek, half, full. Drag between them.10 * Velocity-aware — flick to advance one snap. Spring physics.11 * Backdrop dims progressively. Tap outside to collapse.12 * Content reveals with opacity crossfades at each level.13 *14 * The mechanism IS the experience.15 */1617/* ── Types ── */1819export interface BottomDrawersProps {20 /** Content visible at peek height (always visible) */21 peek: ReactNode;22 /** Content visible at half height and above */23 half?: ReactNode;24 /** Content visible at full height only */25 full?: ReactNode;26 /** Starting snap: 0 = peek, 1 = half, 2 = full */27 defaultSnap?: 0 | 1 | 2;28 sound?: boolean;29}3031/* ── Constants ── */3233const DRAWER_H = 400;34const PEEK_H = 96;35const HALF_H = 240;3637/** Y offsets for each snap — higher value = more hidden */38const SNAPS = [39 DRAWER_H - PEEK_H, // peek: 304px down, 96px visible40 DRAWER_H - HALF_H, // half: 160px down, 240px visible41 0, // full: 0px down, all 400px visible42];4344/* ── Audio ── */4546let _ctx: AudioContext | null = null;47let _buf: AudioBuffer | null = null;4849function audioCtx() {50 if (!_ctx) {51 _ctx = new (window.AudioContext ||52 (window as unknown as { webkitAudioContext: typeof AudioContext })53 .webkitAudioContext)();54 }55 if (_ctx.state === "suspended") _ctx.resume();56 return _ctx;57}5859function ensureBuf(ac: AudioContext): AudioBuffer {60 if (_buf && _buf.sampleRate === ac.sampleRate) return _buf;61 const rate = ac.sampleRate;62 const len = Math.floor(rate * 0.003);63 const buf = ac.createBuffer(1, len, rate);64 const ch = buf.getChannelData(0);65 for (let i = 0; i < len; i++) {66 const t = i / len;67 ch[i] = (Math.random() * 2 - 1) * (1 - t) ** 4;68 }69 _buf = buf;70 return buf;71}7273function playTick(last: React.MutableRefObject<number>) {74 const now = performance.now();75 if (now - last.current < 80) return;76 last.current = now;77 try {78 const ac = audioCtx();79 const buf = ensureBuf(ac);80 const src = ac.createBufferSource();81 const gain = ac.createGain();82 src.buffer = buf;83 src.playbackRate.value = 1.0;84 gain.gain.value = 0.03;85 src.connect(gain);86 gain.connect(ac.destination);87 src.start();88 } catch {89 /* silent */90 }91}9293/* ── Component ── */9495export function BottomDrawers({96 peek,97 half,98 full,99// … truncated
What it pulls in
npm packages
