{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "features-7",
  "title": "Features 7: menu bar app tour",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A feature picker for menu bar apps: choose a feature and a miniature desktop shows the app's panel hanging from its menu bar icon, with quick capture, clipboard history, a focus timer and shortcuts drawn from tokens.",
  "dependencies": [
    "lucide-react",
    "motion@^12",
    "radix-ui"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/kbd.json",
    "https://ui.ballmac.com/r/media.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/blocks/features-7/features-7.tsx",
      "content": "// Ballmac UI: Features 7. https://ui.ballmac.com/blocks/features-7\n\"use client\"\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport { BatteryFull, Check, ClipboardList, Command, Link2, Palette, Pause, Play, Search, Timer, Wifi, Zap } from \"lucide-react\"\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\"\n\nimport { Kbd } from \"@/components/ballmac/kbd\"\nimport { cn } from \"@/lib/utils\"\nimport { Media, type MediaSource } from \"@/components/ballmac/media\"\n\ntype Features7Feature = {\n  /** Stable key. */\n  id: string\n  title: string\n  description: string\n  /** Keys that open it, shown as keycaps. */\n  keys?: string[]\n  icon?: React.ReactNode\n  /** What the menu bar panel shows. Decorative: the title and description carry the meaning. */\n  panel: React.ReactNode\n  /** Your own screenshot of this feature instead of the sample menu bar scene: an image URL (give it imageAlt), an object with alt text and a dark-mode file, or your own element. */\n  image?: MediaSource\n  /** Describes `image` when it is a plain URL. */\n  imageAlt?: string\n}\n\ntype Features7Props = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n  /** Label above the heading. */\n  eyebrow?: string\n  /** Section heading. */\n  title?: string\n  /** One or two sentences under the heading. */\n  description?: string\n  /** The features, one panel each. */\n  features?: Features7Feature[]\n  /** Feature shown first. Defaults to the first. */\n  defaultValue?: string\n  /** The time shown in the menu bar of the scene. */\n  time?: string\n}\n\nconst panelShell = \"bg-popover text-popover-foreground w-[19rem] max-w-full overflow-hidden rounded-2xl border shadow-[0_24px_60px_-20px_rgb(0_0_0/0.5)]\"\n\nfunction CapturePanel() {\n  return (\n    <div className={panelShell}>\n      <div className=\"flex items-center justify-between border-b px-4 py-2.5 text-xs\"><span className=\"font-semibold\">Quick capture</span><span className=\"text-muted-foreground\">Inbox</span></div>\n      <div className=\"space-y-3 p-4\">\n        <p className=\"min-h-16 text-[15px] leading-6\">Call Maya about the Q4 budget before Friday<span className=\"bg-foreground ms-0.5 inline-block h-4 w-px translate-y-0.5 animate-pulse align-middle motion-reduce:animate-none\" /></p>\n        <div className=\"flex flex-wrap gap-1.5\">{[\"#finance\", \"#follow-up\", \"Today\"].map((t) => <span key={t} className=\"bg-muted rounded-full px-2.5 py-1 text-xs\">{t}</span>)}</div>\n      </div>\n      <div className=\"bg-muted/50 flex items-center justify-between border-t px-4 py-2.5 text-xs\"><span className=\"text-muted-foreground\">Saves to Inbox</span><span className=\"flex items-center gap-1.5\">Save <Kbd>⌘</Kbd><Kbd>↩</Kbd></span></div>\n    </div>\n  )\n}\n\nfunction ClipboardPanel() {\n  const rows = [\n    { icon: ClipboardList, text: \"Invoice INV-1042 is overdue\", meta: \"Text\" },\n    { icon: Link2, text: \"acme.com/pricing\", meta: \"Link\" },\n    { icon: Palette, text: \"rgb(59, 130, 246)\", meta: \"Color\" },\n    { icon: ClipboardList, text: \"Call Maya about the Q4 budget\", meta: \"Text\" },\n  ]\n  return (\n    <div className={panelShell}>\n      <div className=\"border-b p-3\"><div className=\"bg-muted text-muted-foreground flex h-8 items-center gap-2 rounded-lg px-2.5 text-xs\"><Search className=\"size-3.5\" />Search history</div></div>\n      <ul className=\"p-1.5\">\n        {rows.map((r, i) => (\n          <li key={r.text} className={cn(\"flex items-center gap-3 rounded-lg px-2.5 py-2 text-sm\", i === 0 && \"bg-accent\")}>\n            <r.icon className=\"text-muted-foreground size-4 shrink-0\" />\n            <span className=\"min-w-0 flex-1 truncate\">{r.text}</span>\n            <span className=\"text-muted-foreground text-xs tabular-nums\">⌘{i + 1}</span>\n          </li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nfunction TimerPanel() {\n  return (\n    <div className={cn(panelShell, \"p-5 text-center\")}>\n      <div className=\"relative mx-auto flex size-36 items-center justify-center\">\n        <svg viewBox=\"0 0 100 100\" className=\"absolute inset-0 -rotate-90\" aria-hidden=\"true\"><circle cx=\"50\" cy=\"50\" r=\"44\" fill=\"none\" stroke=\"var(--muted)\" strokeWidth=\"6\" /><circle cx=\"50\" cy=\"50\" r=\"44\" fill=\"none\" stroke=\"var(--chart-2)\" strokeWidth=\"6\" strokeLinecap=\"round\" strokeDasharray=\"276\" strokeDashoffset=\"92\" /></svg>\n        <div><p className=\"text-4xl font-light tracking-tight tabular-nums\">16:42</p><p className=\"text-muted-foreground text-xs\">Focus</p></div>\n      </div>\n      <div className=\"mt-4 flex items-center justify-center gap-2\">\n        {[\"15\", \"25\", \"50\"].map((m, i) => <span key={m} className={cn(\"rounded-full border px-3 py-1 text-xs tabular-nums\", i === 1 && \"bg-foreground text-background border-transparent\")}>{m} min</span>)}\n      </div>\n      <div className=\"mt-4 flex items-center justify-center gap-3\"><span className=\"bg-foreground text-background flex size-10 items-center justify-center rounded-full\"><Pause className=\"size-4\" /></span><span className=\"flex size-10 items-center justify-center rounded-full border\"><Play className=\"size-4\" /></span></div>\n    </div>\n  )\n}\n\nfunction ShortcutsPanel() {\n  const rows: [string, string[]][] = [[\"New capture\", [\"⌃\", \"⌥\", \"N\"]], [\"Open clipboard\", [\"⌃\", \"⌥\", \"V\"]], [\"Start focus timer\", [\"⌃\", \"⌥\", \"F\"]], [\"Search everything\", [\"⌘\", \"Space\"]]]\n  return (\n    <div className={panelShell}>\n      <div className=\"border-b px-4 py-2.5 text-xs font-semibold\">Global shortcuts</div>\n      <ul className=\"divide-y\">\n        {rows.map(([a, keys]) => (\n          <li key={a} className=\"flex items-center justify-between px-4 py-3 text-sm\"><span>{a}</span><span className=\"flex gap-1\">{keys.map((k) => <Kbd key={k}>{k}</Kbd>)}</span></li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nconst defaultFeatures: Features7Feature[] = [\n  { id: \"capture\", title: \"Quick capture\", description: \"Jot a thought from any app without switching windows. It lands in your inbox, tagged and ready.\", keys: [\"⌃\", \"⌥\", \"N\"], icon: <Zap />, panel: <CapturePanel /> },\n  { id: \"clipboard\", title: \"Clipboard history\", description: \"Everything you copied today, searchable. Paste text, links and colors from the past with one shortcut.\", keys: [\"⌃\", \"⌥\", \"V\"], icon: <ClipboardList />, panel: <ClipboardPanel /> },\n  { id: \"timer\", title: \"Focus timer\", description: \"Start a session from the menu bar. A quiet ring counts down, and breaks arrive on time.\", keys: [\"⌃\", \"⌥\", \"F\"], icon: <Timer />, panel: <TimerPanel /> },\n  { id: \"shortcuts\", title: \"Global shortcuts\", description: \"Every action has a shortcut that works everywhere, and you can change all of them.\", keys: [\"⌘\", \"Space\"], icon: <Command />, panel: <ShortcutsPanel /> },\n]\n\nfunction Features7({\n  eyebrow = \"Lives in your menu bar\",\n  title = \"Always one keystroke away.\",\n  description = \"No window to open, no app to switch to. Ledger waits quietly in the menu bar until you need it.\",\n  features = defaultFeatures,\n  defaultValue,\n  time = \"Thu 9:41 AM\",\n  className,\n  ...props\n}: Features7Props) {\n  const reduce = useReducedMotion()\n  const [value, setValue] = React.useState(defaultValue ?? features[0]?.id ?? \"\")\n  const current = features.find((f) => f.id === value) ?? features[0]\n  if (!current) return null\n\n  return (\n    <section data-slot=\"features-7\" className={cn(\"mx-auto max-w-6xl px-4 py-20 sm:px-6 md:py-28\", className)} {...props}>\n      <div className=\"grid items-center gap-12 lg:grid-cols-[0.9fr_1.1fr] lg:gap-16\">\n        <div className=\"min-w-0\">\n          <p className=\"text-muted-foreground text-sm font-medium\">{eyebrow}</p>\n          <h2 className=\"mt-3 text-3xl font-semibold tracking-[-0.035em] text-balance sm:text-4xl lg:text-5xl\">{title}</h2>\n          <p className=\"text-muted-foreground mt-4 max-w-md text-lg text-pretty\">{description}</p>\n          <RadioGroupPrimitive.Root value={value} onValueChange={setValue} aria-label=\"Features\" orientation=\"vertical\" className=\"mt-8 grid gap-2\">\n            {features.map((f) => {\n              const on = f.id === value\n              return (\n                <RadioGroupPrimitive.Item\n                  key={f.id}\n                  value={f.id}\n                  className={cn(\"focus-visible:ring-ring/50 group/feature flex items-start gap-4 rounded-2xl border p-4 text-start outline-none transition-colors focus-visible:ring-[3px]\", on ? \"border-foreground bg-accent/50\" : \"hover:bg-accent/30\")}\n                >\n                  <span aria-hidden=\"true\" className={cn(\"flex size-10 shrink-0 items-center justify-center rounded-xl [&_svg]:size-5\", on ? \"bg-foreground text-background\" : \"bg-muted\")}>{f.icon}</span>\n                  <span className=\"min-w-0 flex-1\">\n                    <span className=\"flex items-center justify-between gap-3\">\n                      <span className=\"font-semibold\">{f.title}</span>\n                      {f.keys && <span aria-hidden=\"true\" className=\"hidden gap-1 sm:flex\">{f.keys.map((k) => <Kbd key={k}>{k}</Kbd>)}</span>}\n                    </span>\n                    <span className=\"text-muted-foreground mt-1 block text-sm text-pretty\">{f.description}</span>\n                    {f.keys && <span className=\"sr-only\">Shortcut: {f.keys.join(\" \")}</span>}\n                  </span>\n                  <RadioGroupPrimitive.Indicator className=\"mt-1\"><Check className=\"size-4\" aria-hidden=\"true\" /></RadioGroupPrimitive.Indicator>\n                </RadioGroupPrimitive.Item>\n              )\n            })}\n          </RadioGroupPrimitive.Root>\n        </div>\n\n        {/* A miniature desktop: the menu bar with the app's icon lit, and its panel hanging underneath. Decorative. */}\n        <Media media={current.image} alt={current.imageAlt} frame fallback={\n        <div aria-hidden=\"true\" className=\"relative isolate overflow-hidden rounded-3xl border\">\n          <div className=\"pointer-events-none absolute inset-0 -z-10 dark:brightness-[0.6] dark:saturate-[1.2]\">\n            <div className=\"absolute inset-0 bg-linear-to-br from-chart-1 via-chart-4 to-chart-5\" />\n            <div className=\"absolute inset-0 bg-[radial-gradient(70%_60%_at_15%_100%,var(--chart-2),transparent_70%),radial-gradient(60%_55%_at_85%_0%,var(--chart-3),transparent_70%)] opacity-70\" />\n            <div className=\"absolute -inset-x-1/4 top-[45%] h-[70%] rounded-[50%] bg-white/15 blur-2xl\" />\n          </div>\n          <div className=\"bg-background/55 text-foreground flex h-7 items-center gap-3 border-b border-white/20 px-3 text-[13px] backdrop-blur-2xl dark:border-white/[0.06]\">\n            <Command className=\"size-3.5\" /><span className=\"font-bold\">Finder</span><span className=\"max-sm:hidden\">File</span><span className=\"max-sm:hidden\">Edit</span><span className=\"max-sm:hidden\">View</span>\n            <span className=\"ms-auto flex items-center gap-3\">\n              <span className=\"bg-foreground/15 flex size-5 items-center justify-center rounded-md\"><span className=\"bg-foreground text-background flex size-4 items-center justify-center rounded-[4px] text-[10px] font-bold\">L</span></span>\n              <Wifi className=\"size-3.5 max-sm:hidden\" /><BatteryFull className=\"size-4\" /><span className=\"tabular-nums max-sm:hidden\">{time}</span>\n            </span>\n          </div>\n          <div className=\"flex min-h-[26rem] items-start justify-end px-3 pt-2 pb-8 sm:px-6\">\n            <div className=\"relative\">\n              <span className=\"bg-popover absolute -top-1.5 end-6 size-3 rotate-45 rounded-[3px] border-t border-s\" />\n              <AnimatePresence mode=\"wait\" initial={false}>\n                <motion.div\n                  key={current.id}\n                  initial={reduce ? false : { opacity: 0, y: -8, scale: 0.97, filter: \"blur(4px)\" }}\n                  animate={{ opacity: 1, y: 0, scale: 1, filter: \"blur(0px)\" }}\n                  exit={reduce ? undefined : { opacity: 0, y: -4, scale: 0.98, filter: \"blur(4px)\" }}\n                  transition={{ duration: 0.22, ease: [0.22, 1, 0.36, 1] }}\n                  className=\"relative\"\n                >\n                  {current.panel}\n                </motion.div>\n              </AnimatePresence>\n            </div>\n          </div>\n        </div>\n        } />\n      </div>\n    </section>\n  )\n}\n\nexport { Features7, type Features7Props, type Features7Feature }\n",
      "type": "registry:component",
      "target": "@components/ballmac/blocks/features-7/features-7.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "features",
      "menu bar",
      "mac",
      "tour",
      "shortcuts",
      "panel",
      "utility"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "For menu bar and utility apps. Pass features=[{ id, title, description, keys?, icon?, panel }] where panel is the UI that hangs from the menu bar icon; time sets the clock in the mock menu bar.",
      "whenToUse": [
        "Menu bar utilities and background apps",
        "Anything the user reaches with a shortcut"
      ],
      "whenNotToUse": [
        "Window-based apps (use features-4 or showcase-1)"
      ],
      "composesWith": [
        "hero-5",
        "showcase-1",
        "download-1",
        "pricing-4"
      ],
      "a11y": [
        {
          "keys": "Arrow Up / Down",
          "action": "Moves between features and selects; the list is a radio group"
        },
        {
          "keys": "Shortcuts",
          "action": "Each feature spells its shortcut out for screen readers; the miniature desktop is hidden from assistive technology"
        },
        {
          "keys": "Reduced motion",
          "action": "Panels swap without animation"
        }
      ],
      "customization": [
        "features: panel is any node; keys are shown as keycaps",
        "defaultValue, time"
      ]
    },
    "examples": [
      "features-7-demo",
      "features-7-two"
    ],
    "url": "https://ui.ballmac.com/components/features-7"
  },
  "categories": [
    "blocks",
    "features"
  ],
  "type": "registry:block"
}