{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bell-quiet",
  "type": "registry:component",
  "title": "Bell Quiet",
  "description": "Empty-state art for notifications: a sleepy bell breathing out little z's – hover and it startles awake with a tiny ring, then dozes off again.",
  "categories": [
    "illustration",
    "Empty States"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/devl/bell-quiet.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport { AnimatePresence, motion, useInView, useReducedMotion } from \"motion/react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type BellQuietHue = \"sky\" | \"emerald\" | \"amber\" | \"violet\" | \"rose\" | \"orange\" | \"indigo\" | \"teal\";\n\nconst hues: Record<BellQuietHue, string> = {\n  sky: \"[--hue-hi:var(--color-sky-300)] [--hue:var(--color-sky-500)] [--hue-deep:var(--color-sky-700)]\",\n  emerald: \"[--hue-hi:var(--color-emerald-300)] [--hue:var(--color-emerald-500)] [--hue-deep:var(--color-emerald-700)]\",\n  amber: \"[--hue-hi:var(--color-amber-300)] [--hue:var(--color-amber-500)] [--hue-deep:var(--color-amber-700)]\",\n  violet: \"[--hue-hi:var(--color-violet-300)] [--hue:var(--color-violet-500)] [--hue-deep:var(--color-violet-700)]\",\n  rose: \"[--hue-hi:var(--color-rose-300)] [--hue:var(--color-rose-500)] [--hue-deep:var(--color-rose-700)]\",\n  orange: \"[--hue-hi:var(--color-orange-300)] [--hue:var(--color-orange-500)] [--hue-deep:var(--color-orange-700)]\",\n  indigo: \"[--hue-hi:var(--color-indigo-300)] [--hue:var(--color-indigo-500)] [--hue-deep:var(--color-indigo-700)]\",\n  teal: \"[--hue-hi:var(--color-teal-300)] [--hue:var(--color-teal-500)] [--hue-deep:var(--color-teal-700)]\",\n};\n\nexport interface BellQuietProps {\n  title?: string;\n  description?: string;\n  hue?: BellQuietHue;\n  className?: string;\n}\n\nconst W = 400;\nconst H = 210;\nconst CX = 200;\nconst TOP = 40;\n\nconst zs = [\n  { size: 13, x: 246, y: 74, delay: 0 },\n  { size: 17, x: 262, y: 56, delay: 0.9 },\n  { size: 22, x: 282, y: 36, delay: 1.8 },\n];\n\nconst BELL = `M${CX} ${TOP + 10} C ${CX - 30} ${TOP + 10} ${CX - 46} ${TOP + 34} ${CX - 46} ${TOP + 62} V ${TOP + 86} C ${CX - 46} ${TOP + 96} ${CX - 52} ${TOP + 102} ${CX - 60} ${TOP + 108} H ${CX + 60} C ${CX + 52} ${TOP + 102} ${CX + 46} ${TOP + 96} ${CX + 46} ${TOP + 86} V ${TOP + 62} C ${CX + 46} ${TOP + 34} ${CX + 30} ${TOP + 10} ${CX} ${TOP + 10} Z`;\n\nfunction useCalmMotion() {\n  const prefers = useReducedMotion();\n  const [mounted, setMounted] = useState(false);\n  useEffect(() => setMounted(true), []);\n  return mounted && !!prefers;\n}\n\nexport function BellQuiet({\n  title = \"You're all caught up\",\n  description = \"No new notifications. We'll ring when something needs you.\",\n  hue = \"amber\",\n  className,\n}: BellQuietProps) {\n  const uid = useId().replace(/:/g, \"\");\n  const reduce = useCalmMotion();\n  const root = useRef<HTMLDivElement>(null);\n  const live = useInView(root) && !reduce;\n  const [ring, setRing] = useState(0);\n  const [awake, setAwake] = useState(false);\n  const timer = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n  useEffect(() => () => clearTimeout(timer.current), []);\n\n  const poke = () => {\n    if (awake) return;\n    setRing((r) => r + 1);\n    setAwake(true);\n    clearTimeout(timer.current);\n    timer.current = setTimeout(() => setAwake(false), 1500);\n  };\n\n  const swing = { duration: 0.9, ease: \"easeOut\" as const, times: [0, 0.14, 0.32, 0.5, 0.68, 0.84, 1] };\n\n  return (\n    <div\n      ref={root}\n      className={cn(\n        \"relative w-full max-w-[420px] overflow-hidden rounded-2xl border bg-card p-4 shadow-xs/5 sm:p-6\",\n        hues[hue],\n        className,\n      )}\n    >\n      <div\n        aria-hidden\n        className=\"dot-grid pointer-events-none absolute inset-0 opacity-50 [mask-image:radial-gradient(closest-side,black,transparent)]\"\n      />\n      <button\n        aria-label=\"No notifications. Give the bell a ring\"\n        className=\"relative mx-auto block aspect-[40/21] w-full max-w-[380px] cursor-pointer rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n        onClick={poke}\n        onFocus={(e) => e.currentTarget.matches(\":focus-visible\") && poke()}\n        onPointerEnter={(e) => e.pointerType === \"mouse\" && poke()}\n        type=\"button\"\n      >\n        <svg aria-hidden className=\"absolute inset-0 size-full overflow-visible\" viewBox={`0 0 ${W} ${H}`}>\n          <defs>\n            <linearGradient id={`${uid}-bell`} x1=\"0.2\" x2=\"0.8\" y1=\"0\" y2=\"1\">\n              <stop offset=\"0\" stopColor=\"var(--hue-hi)\" />\n              <stop offset=\"0.55\" stopColor=\"var(--hue)\" />\n              <stop offset=\"1\" stopColor=\"var(--hue-deep)\" />\n            </linearGradient>\n          </defs>\n\n          <motion.ellipse\n            animate={live ? { rx: [46, 50, 46], opacity: [1, 0.75, 1] } : { rx: 46, opacity: 1 }}\n            className=\"fill-foreground/8\"\n            cx={CX}\n            cy={TOP + 150}\n            rx={46}\n            ry={6}\n            transition={live ? { duration: 3.4, ease: \"easeInOut\", repeat: Number.POSITIVE_INFINITY } : { duration: 0.4 }}\n          />\n\n          <AnimatePresence>\n            {awake && !reduce && (\n              <motion.g className=\"fill-none stroke-(--hue)\" exit={{ opacity: 0 }} key={`waves-${ring}`} strokeLinecap=\"round\" strokeWidth={3}>\n                {[-1, 1].map((side) =>\n                  [0, 1].map((n) => (\n                    <motion.path\n                      animate={{ opacity: [0, 1, 0], x: [0, side * 6] }}\n                      d={\n                        side < 0\n                          ? `M${CX - 70 - n * 12} ${TOP + 40 - n * 6} q -10 ${22 + n * 6} 0 ${44 + n * 12}`\n                          : `M${CX + 70 + n * 12} ${TOP + 40 - n * 6} q 10 ${22 + n * 6} 0 ${44 + n * 12}`\n                      }\n                      initial={{ opacity: 0 }}\n                      key={`${side}-${n}`}\n                      transition={{ delay: 0.05 + n * 0.1, duration: 0.6, ease: \"easeOut\" }}\n                    />\n                  )),\n                )}\n              </motion.g>\n            )}\n          </AnimatePresence>\n\n          <motion.g\n            animate={live ? { scale: [1, 1.025, 1] } : { scale: 1 }}\n            style={{ originX: `${CX}px`, originY: `${TOP + 120}px`, transformBox: \"view-box\" }}\n            transition={live ? { duration: 3.4, ease: \"easeInOut\", repeat: Number.POSITIVE_INFINITY } : { duration: 0.4 }}\n          >\n            <motion.g\n              animate={ring > 0 && !reduce ? { rotate: [0, 16, -13, 9, -5, 2, 0] } : { rotate: 0 }}\n              key={`swing-${ring}`}\n              style={{ originX: `${CX}px`, originY: `${TOP + 2}px`, transformBox: \"view-box\" }}\n              transition={swing}\n            >\n              <motion.g\n                animate={ring > 0 && !reduce ? { rotate: [0, -10, 14, -9, 5, -2, 0] } : { rotate: 0 }}\n                style={{ originX: `${CX}px`, originY: `${TOP + 60}px`, transformBox: \"view-box\" }}\n                transition={{ ...swing, delay: 0.06 }}\n              >\n                <circle className=\"fill-(--hue-deep)\" cx={CX} cy={TOP + 120} r={11} />\n              </motion.g>\n              <circle className=\"fill-(--hue-deep)\" cx={CX} cy={TOP + 4} r={8} />\n              <path d={BELL} fill={`url(#${uid}-bell)`} />\n              <path\n                className=\"fill-white/20\"\n                d={`M${CX - 30} ${TOP + 26} C ${CX - 38} ${TOP + 36} ${CX - 40} ${TOP + 50} ${CX - 40} ${TOP + 62} V ${TOP + 84} C ${CX - 40} ${TOP + 88} ${CX - 34} ${TOP + 88} ${CX - 34} ${TOP + 84} V ${TOP + 62} C ${CX - 34} ${TOP + 48} ${CX - 30} ${TOP + 36} ${CX - 24} ${TOP + 28} Z`}\n              />\n              <rect className=\"fill-(--hue-deep)\" height={14} rx={7} width={136} x={CX - 68} y={TOP + 102} />\n              <rect className=\"fill-white/15\" height={3} rx={1.5} width={110} x={CX - 55} y={TOP + 105} />\n\n              <g className=\"fill-none stroke-white\" strokeLinecap=\"round\" strokeWidth={4}>\n                <motion.path\n                  animate={{ opacity: awake ? 0 : 1 }}\n                  d={`M${CX - 26} ${TOP + 66} q 8 7 16 0`}\n                  initial={false}\n                  transition={{ duration: 0.12 }}\n                />\n                <motion.path\n                  animate={{ opacity: awake ? 0 : 1 }}\n                  d={`M${CX + 10} ${TOP + 66} q 8 7 16 0`}\n                  initial={false}\n                  transition={{ duration: 0.12 }}\n                />\n              </g>\n              <motion.g\n                animate={{ opacity: awake ? 1 : 0, scaleY: awake ? 1 : 0.3 }}\n                className=\"fill-white\"\n                initial={false}\n                style={{ originX: \"50%\", originY: \"50%\" }}\n                transition={{ duration: 0.14, ease: \"easeOut\" }}\n              >\n                <circle cx={CX - 18} cy={TOP + 66} r={5.5} />\n                <circle cx={CX + 18} cy={TOP + 66} r={5.5} />\n              </motion.g>\n            </motion.g>\n          </motion.g>\n        </svg>\n\n        {live &&\n          zs.map((z) => (\n            <motion.span\n              animate={awake ? { opacity: 0, scale: 0.6 } : { opacity: [0, 1, 1, 0], x: [0, 6, 10, 14], y: [6, -4, -12, -20] }}\n              aria-hidden\n              className=\"absolute font-semibold text-(--hue-deep) leading-none dark:text-(--hue-hi)\"\n              key={z.x}\n              style={{ fontSize: z.size, left: `${(z.x / W) * 100}%`, top: `${(z.y / H) * 100}%` }}\n              transition={\n                awake\n                  ? { duration: 0.15 }\n                  : { delay: z.delay, duration: 2.7, ease: \"easeOut\", repeat: Number.POSITIVE_INFINITY, times: [0, 0.25, 0.7, 1] }\n              }\n            >\n              z\n            </motion.span>\n          ))}\n      </button>\n\n      <div className=\"relative mt-3 text-center\">\n        <p className=\"text-balance font-medium text-foreground text-sm\">{title}</p>\n        <p className=\"mx-auto mt-1 max-w-[32ch] text-pretty text-muted-foreground text-xs\">{description}</p>\n      </div>\n    </div>\n  );\n}\n"
    }
  ]
}