caliper uidocs

Drill down menu

Free

A menu that goes deeper one level at a time: the next level slides in, Back slides it out, and the height eases to fit.

Installs components/ui/drill-down-menu.tsxDepends on lucide-reactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/drill-down-menu.json

Nothing chosen yet

When to use #

Use it for

  • Settings and nested menus on phones and in popovers.
  • Hierarchies three or four levels deep.

Skip it when

  • Flat menus; use a dropdown.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/drill-down-menu.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { Bell, Globe, Lock, Palette, Shield } from "lucide-react"
import { useState } from "react"
import { DrillDownMenu } from "@/components/ui/drill-down-menu"

export function DrillDownMenuDemo() {
  const [last, setLast] = useState("Nothing chosen yet");
  const pick = (what: string) => () => setLast(what);
  return (
    <div className="grid w-full max-w-xs gap-3">
      <DrillDownMenu
        label="Settings"
        items={[
          { id: "appearance", label: "Appearance", icon: <Palette />, hint: "System", items: [
            { id: "light", label: "Light", onSelect: pick("Appearance: Light") },
            { id: "dark", label: "Dark", onSelect: pick("Appearance: Dark") },
            { id: "system", label: "Match the system", onSelect: pick("Appearance: System") },
          ] },
          { id: "notifications", label: "Notifications", icon: <Bell />, hint: "On", items: [
            { id: "email", label: "Email", hint: "Daily", items: [
              { id: "instant", label: "As it happens", onSelect: pick("Email: as it happens") },
              { id: "daily", label: "Daily digest", onSelect: pick("Email: daily digest") },
              { id: "off", label: "Off", onSelect: pick("Email: off") },
            ] },
            { id: "push", label: "Push", hint: "Mentions", onSelect: pick("Push: mentions") },
          ] },
          { id: "privacy", label: "Privacy", icon: <Shield />, items: [
            { id: "profile", label: "Who sees your profile", hint: "Team", onSelect: pick("Profile: team") },
            { id: "activity", label: "Show activity status", hint: "On", onSelect: pick("Activity status: on") },
          ] },
          { id: "language", label: "Language", icon: <Globe />, hint: "English", onSelect: pick("Language: English") },
          { id: "security", label: "Sign out everywhere", icon: <Lock />, onSelect: pick("Signed out of other devices") },
        ]}
      />
      <p className="text-xs text-muted-foreground" role="status">{last}</p>
    </div>
  );
}

API reference #

DrillDownMenu

component
PropTypeDefault
items *readonly DrillItem[]—
label *string—
classNamestring—

Accessibility #

It is built on native elements and keeps aria-label and aria-hidden in step with state.

KeyBehaviour
↓Moves to the next item or value.
↑Moves to the previous item or value.
←Moves to the previous item or value.
EscapeCloses or cancels, then returns focus to where it came from.
→Moves to the next item or value.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.