{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hud",
  "title": "HUD",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "The macOS volume and brightness HUD: a dark frosted square with a big icon and a 16-segment bar, or a slim capsule with a smooth bar. Pops in, restarts its timer when the value changes, fades out and announces the new level.",
  "dependencies": [
    "motion@^12",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/hud.tsx",
      "content": "// Ballmac UI: HUD. https://ui.ballmac.com/components/hud\n\"use client\"\n\nimport * as React from \"react\"\nimport { Keyboard, Sun, SunMedium, Volume, Volume1, Volume2, VolumeX } from \"lucide-react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype HudKind = \"volume\" | \"brightness\" | \"keyboard\"\n\ntype HudProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  /** What is being adjusted. Chooses the icon and the spoken name. */\n  kind?: HudKind\n  /** Level from 0 to 100. */\n  value: number\n  /** Show a muted speaker instead of the level (volume only). */\n  muted?: boolean\n  /** Whether the HUD is on screen. */\n  visible: boolean\n  /** Called with false when the HUD times out. */\n  onVisibleChange?: (visible: boolean) => void\n  /** How long the HUD stays after the last change, in milliseconds. */\n  duration?: number\n  /** \"mac\" is the dark square with a segmented bar; \"pill\" is a slim capsule with a smooth bar. */\n  variant?: \"mac\" | \"pill\"\n}\n\nconst NAMES: Record<HudKind, string> = { volume: \"Volume\", brightness: \"Brightness\", keyboard: \"Keyboard brightness\" }\n\nfunction HudIcon({ kind, value, muted }: { kind: HudKind; value: number; muted?: boolean }) {\n  if (kind === \"brightness\") return value > 50 ? <Sun aria-hidden=\"true\" /> : <SunMedium aria-hidden=\"true\" />\n  if (kind === \"keyboard\") return <Keyboard aria-hidden=\"true\" />\n  if (muted || value === 0) return <VolumeX aria-hidden=\"true\" />\n  if (value < 34) return <Volume aria-hidden=\"true\" />\n  if (value < 67) return <Volume1 aria-hidden=\"true\" />\n  return <Volume2 aria-hidden=\"true\" />\n}\n\n/**\n * The macOS volume and brightness HUD. It sits at the bottom center of its positioned parent (a capsule sits at the top),\n * pops in, restarts its timer whenever `value` changes, and fades out. Screen readers hear the new level.\n */\nfunction Hud({ kind = \"volume\", value, muted = false, visible, onVisibleChange, duration = 1800, variant = \"mac\", className, ...props }: HudProps) {\n  const reduce = useReducedMotion()\n  const level = Math.max(0, Math.min(100, Math.round(value)))\n  const shown = muted && kind === \"volume\" ? 0 : level\n  const name = NAMES[kind]\n\n  React.useEffect(() => {\n    if (!visible) return\n    const timer = setTimeout(() => onVisibleChange?.(false), duration)\n    return () => clearTimeout(timer)\n  }, [visible, level, muted, duration, onVisibleChange])\n\n  const filled = Math.round((shown / 100) * 16)\n  const pill = variant === \"pill\"\n  const motionProps = reduce\n    ? { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 } }\n    : pill\n      ? { initial: { opacity: 0, y: -14, scale: 0.96 }, animate: { opacity: 1, y: 0, scale: 1 }, exit: { opacity: 0, y: -8, scale: 0.98 } }\n      : { initial: { opacity: 0, scale: 0.9 }, animate: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 0.96 } }\n\n  return (\n    <>\n      <p role=\"status\" className=\"sr-only\">\n        {visible ? (muted && kind === \"volume\" ? \"Volume muted\" : `${name} ${level} percent`) : \"\"}\n      </p>\n      <AnimatePresence>\n        {visible && (\n          <motion.div\n            key=\"hud\"\n            data-slot=\"hud\"\n            data-variant={variant}\n            aria-hidden=\"true\"\n            className={cn(\n              \"pointer-events-none absolute left-1/2 z-[60] -translate-x-1/2 text-white\",\n              pill\n                ? \"top-4 flex h-11 w-[min(18rem,calc(100%-2rem))] items-center gap-3 rounded-full border border-white/15 bg-neutral-900/75 px-4 shadow-[0_12px_32px_-8px_rgb(0_0_0/0.55)] backdrop-blur-2xl backdrop-saturate-150\"\n                : \"bottom-[8%] flex aspect-square w-[11.5rem] max-w-[60%] flex-col items-center justify-between rounded-[26px] border border-white/10 bg-neutral-900/70 px-[1.1rem] pt-[1.5rem] pb-[1.4rem] shadow-[0_20px_50px_-10px_rgb(0_0_0/0.6)] backdrop-blur-3xl backdrop-saturate-150\",\n              className\n            )}\n            transition={{ type: \"spring\", stiffness: 520, damping: 34, mass: 0.7 }}\n            {...(motionProps as object)}\n            {...(props as object)}\n          >\n            {pill ? (\n              <>\n                <span className=\"flex size-6 shrink-0 items-center justify-center [&_svg]:size-5\">\n                  <HudIcon kind={kind} value={shown} muted={muted} />\n                </span>\n                <span className=\"h-1.5 flex-1 overflow-hidden rounded-full bg-white/25\">\n                  <span className=\"block h-full rounded-full bg-white transition-[width] duration-150 motion-reduce:transition-none\" style={{ width: `${shown}%` }} />\n                </span>\n                <span className=\"w-9 text-end text-sm font-medium tabular-nums\">{shown}%</span>\n              </>\n            ) : (\n              <>\n                <span className=\"flex flex-1 items-center justify-center [&_svg]:size-[4.4rem] [&_svg]:stroke-[1.4]\">\n                  <HudIcon kind={kind} value={shown} muted={muted} />\n                </span>\n                <span className=\"flex w-full justify-between gap-[3px]\">\n                  {Array.from({ length: 16 }, (_, i) => (\n                    <span\n                      key={i}\n                      className={cn(\"h-[9px] flex-1 rounded-[1.5px] transition-colors duration-100 motion-reduce:transition-none\", i < filled ? \"bg-white\" : \"bg-white/30\")}\n                    />\n                  ))}\n                </span>\n              </>\n            )}\n          </motion.div>\n        )}\n      </AnimatePresence>\n    </>\n  )\n}\n\n/** State for a HUD: `show()` makes it visible; the HUD hides itself after its `duration`, and a new `value` restarts that timer. */\nfunction useTransientHud() {\n  const [visible, setVisible] = React.useState(false)\n  const show = React.useCallback(() => setVisible(true), [])\n  const hide = React.useCallback(() => setVisible(false), [])\n  return { visible, show, hide, onVisibleChange: setVisible }\n}\n\nexport { Hud, useTransientHud, type HudProps, type HudKind }\n",
      "type": "registry:ui",
      "target": "@components/ballmac/hud.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "hud",
      "volume",
      "brightness",
      "macos",
      "overlay",
      "feedback"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "<Hud kind=\"volume\" value={60} visible={visible} onVisibleChange={setVisible} /> inside a relative container. useTransientHud() gives { visible, show, hide, onVisibleChange }.",
      "whenToUse": [
        "Keyboard-driven level changes in desktop UI demos",
        "Transient feedback for hardware-style controls"
      ],
      "whenNotToUse": [
        "Toasts and messages (toast)",
        "Persistent controls (slider)"
      ],
      "composesWith": [
        "control-center",
        "menu-bar",
        "keyboard-shortcuts"
      ],
      "a11y": [
        {
          "keys": "Screen readers",
          "action": "A polite status message says the new level; the visual HUD is hidden from them"
        },
        {
          "keys": "Reduced motion",
          "action": "Fades without scaling"
        }
      ],
      "customization": [
        "kind: volume | brightness | keyboard",
        "variant: mac | pill",
        "muted, duration, value 0 to 100"
      ]
    },
    "examples": [
      "hud-demo",
      "hud-pill"
    ],
    "url": "https://ui.ballmac.com/components/hud"
  },
  "categories": [
    "macos"
  ],
  "type": "registry:ui"
}