{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "floating-nav",
  "title": "Floating nav",
  "author": "Tori Bryan",
  "description": "A pill of destinations floating above the bottom of a phone screen that steps aside on scroll.",
  "dependencies": [
    "class-variance-authority",
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/floating-nav.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva } from \"class-variance-authority\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype FloatingNavItem = {\n  /** Stable, unique identifier. Matched against `value`. */\n  value: string\n  /** Visible name of the destination. Also its accessible name. */\n  label: string\n  /** An icon, usually a 20px Lucide icon. Hidden from screen readers. Leave it out for a text item, whose label always shows. */\n  icon?: React.ReactNode\n  /** Renders the item as a link. Without it, the item is a button. */\n  href?: string\n}\n\ntype FloatingNavProps = Omit<React.ComponentProps<\"nav\">, \"onChange\"> & {\n  /** Destinations, in order from left to right. Three to five read best. */\n  items: FloatingNavItem[]\n  /** The current item, when controlled. */\n  value?: string\n  /** The item current on first render, when uncontrolled. */\n  defaultValue?: string\n  /** Called when an item is pressed. Call `event.preventDefault()` on a link to route it yourself. */\n  onValueChange?: (\n    value: string,\n    event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>\n  ) => void\n  /** `active` shows only the current item's label; `always` stacks every label under its icon. */\n  labels?: \"active\" | \"always\"\n  /** `sm` tightens the pill: 36px items and 16px icons instead of 44px and 20px. */\n  size?: \"sm\" | \"default\"\n  /** `fixed` floats above the page at the bottom of the viewport; `static` sits in the flow. */\n  position?: \"fixed\" | \"static\"\n  /** Slides the bar away while the page scrolls down and back when it scrolls up. */\n  hideOnScroll?: boolean\n  /** `glass` is Apple's liquid glass: a clear bar that bends the page behind its edges, with a glass lens on the current item. */\n  variant?: \"default\" | \"glass\"\n}\n\nconst floatingNavVariants = cva(\n  \"pointer-events-auto relative flex max-w-full [scrollbar-width:none] items-center overflow-x-auto rounded-full shadow-lg\",\n  {\n    variants: {\n      variant: {\n        default: \"border border-border bg-popover-overlay backdrop-blur-md\",\n        // The surface is drawn by the layers in GlassSurface, under the items.\n        // Visible overflow, so the lens can lift past the bar's edges.\n        glass: \"isolate overflow-visible\",\n      },\n      size: { sm: \"gap-0.5 p-1\", default: \"gap-1 p-1.5\" },\n    },\n    defaultVariants: { variant: \"default\", size: \"default\" },\n  }\n)\n\nconst floatingNavItemVariants = cva(\n  \"relative flex shrink-0 items-center justify-center rounded-full font-medium text-muted-foreground transition-colors outline-none select-none hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring-subtle data-[current]:text-primary-foreground data-[current]:hover:text-primary-foreground\",\n  {\n    variants: {\n      variant: {\n        default: \"\",\n        // Glass has no fill to read against, so every label is in the text\n        // color and the lens marks the current one.\n        glass:\n          \"text-foreground data-[current]:text-foreground data-[current]:hover:text-foreground\",\n      },\n      labels: {\n        active: \"\",\n        always: \"flex-col gap-0.5 leading-4\",\n      },\n      size: { sm: \"\", default: \"\" },\n    },\n    compoundVariants: [\n      {\n        labels: \"active\",\n        size: \"default\",\n        className: \"h-11 min-w-11 px-3 text-sm\",\n      },\n      {\n        labels: \"always\",\n        size: \"default\",\n        className: \"h-14 min-w-16 px-3 text-[11px]\",\n      },\n      {\n        labels: \"active\",\n        size: \"sm\",\n        className: \"h-9 min-w-9 px-2.5 text-[13px]\",\n      },\n      {\n        labels: \"always\",\n        size: \"sm\",\n        className: \"h-12 min-w-14 px-2 text-[10px]\",\n      },\n    ],\n    defaultVariants: { variant: \"default\", labels: \"active\", size: \"default\" },\n  }\n)\n\n// Past this distance from the top, scrolling down hides the bar. Near the top\n// it always shows, so a page that barely scrolls never loses its nav.\nconst HIDE_AFTER = 64\n// Ignores the jitter of momentum scrolling and small layout shifts.\nconst SCROLL_THRESHOLD = 8\n\nconst SPRING = {\n  type: \"spring\",\n  stiffness: 520,\n  damping: 40,\n  mass: 0.8,\n} as const\n\n// Looser, so the glass lens overshoots a little as it lands, like a drop.\nconst GLASS_SPRING = {\n  type: \"spring\",\n  stiffness: 560,\n  damping: 34,\n  mass: 0.7,\n} as const\n\n// How far the glass bends the page at its edges, in CSS pixels, and how wide\n// the bending band along each edge is.\nconst REFRACTION = 14\nconst REFRACTION_EDGE = 12\n\n/*\n * A displacement map for one axis: neutral gray through the middle, and\n * along each edge a ramp that pulls the page in from further inside, so\n * content bends toward the rim the way it does through a lens.\n */\nfunction edgeMap(axis: \"x\" | \"y\", width: number, height: number) {\n  const length = axis === \"x\" ? width : height\n  const band = Math.min(REFRACTION_EDGE, length / 2) / length\n  const channel = (value: number) =>\n    axis === \"x\" ? `rgb(${value},128,128)` : `rgb(128,${value},128)`\n  const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"${axis === \"x\" ? 1 : 0}\" y2=\"${axis === \"y\" ? 1 : 0}\"><stop offset=\"0\" stop-color=\"${channel(255)}\"/><stop offset=\"${band}\" stop-color=\"${channel(128)}\"/><stop offset=\"${1 - band}\" stop-color=\"${channel(128)}\"/><stop offset=\"1\" stop-color=\"${channel(0)}\"/></linearGradient></defs><rect width=\"100%\" height=\"100%\" fill=\"url(#g)\"/></svg>`\n  return `data:image/svg+xml,${encodeURIComponent(svg)}`\n}\n\n/*\n * The liquid glass surface, under the items. A tinted, lightly frosted layer\n * works everywhere. Over it, a layer whose backdrop runs through an SVG\n * displacement filter bends what's behind the edges; only Chromium applies\n * url() in backdrop-filter, and elsewhere that layer is clear, so the bar\n * falls back to the blur and rim.\n */\nfunction GlassSurface() {\n  const filterId = `floating-nav-glass-${React.useId().replace(/:/g, \"\")}`\n  const surfaceRef = React.useRef<HTMLLIElement>(null)\n  const [box, setBox] = React.useState<{ width: number; height: number }>()\n\n  // The surface fills the list, so its own size is the bar's.\n  React.useLayoutEffect(() => {\n    const element = surfaceRef.current\n    if (!element) return\n    const observer = new ResizeObserver(() =>\n      setBox({\n        width: Math.round(element.offsetWidth),\n        height: Math.round(element.offsetHeight),\n      })\n    )\n    observer.observe(element)\n    return () => observer.disconnect()\n  }, [])\n\n  return (\n    <li\n      ref={surfaceRef}\n      aria-hidden=\"true\"\n      data-slot=\"floating-nav-glass\"\n      className=\"pointer-events-none absolute inset-0 -z-10 rounded-full\"\n    >\n      {box ? (\n        <svg width=\"0\" height=\"0\" className=\"absolute\">\n          <filter\n            id={filterId}\n            x=\"0\"\n            y=\"0\"\n            width={box.width}\n            height={box.height}\n            filterUnits=\"userSpaceOnUse\"\n            colorInterpolationFilters=\"sRGB\"\n          >\n            <feImage\n              href={edgeMap(\"x\", box.width, box.height)}\n              width={box.width}\n              height={box.height}\n              preserveAspectRatio=\"none\"\n              result=\"x\"\n            />\n            <feImage\n              href={edgeMap(\"y\", box.width, box.height)}\n              width={box.width}\n              height={box.height}\n              preserveAspectRatio=\"none\"\n              result=\"y\"\n            />\n            <feDisplacementMap\n              in=\"SourceGraphic\"\n              in2=\"x\"\n              scale={REFRACTION}\n              xChannelSelector=\"R\"\n              yChannelSelector=\"G\"\n              result=\"bentX\"\n            />\n            <feDisplacementMap\n              in=\"bentX\"\n              in2=\"y\"\n              scale={REFRACTION}\n              xChannelSelector=\"R\"\n              yChannelSelector=\"G\"\n            />\n          </filter>\n        </svg>\n      ) : null}\n      <span className=\"absolute inset-0 rounded-full bg-glass backdrop-blur-xs backdrop-saturate-150\" />\n      <span\n        className=\"absolute inset-0 rounded-full\"\n        style={box ? { backdropFilter: `url(#${filterId})` } : undefined}\n      />\n      <span className=\"absolute inset-0 rounded-full ring-1 inset-shadow-[0_1px_1px] ring-glass-edge inset-shadow-glass-edge\" />\n    </li>\n  )\n}\n\n// How far the lens grows as it lifts off the bar, and how far it magnifies\n// what's under it: the shift at its rim, as a share of its width.\nconst LIFT = { scaleX: 1.06, scaleY: 1.22 }\n// Lifting and settling are quicker than the trip between items.\nconst LIFT_SPRING = { type: \"spring\", stiffness: 900, damping: 40 } as const\nconst MAGNIFY = 0.2\n// Red bends most and blue least, which splits color into fringes at the rim.\nconst DISPERSION = { R: 1, G: 0.95, B: 0.9 }\n// How long the lens stays lifted after a press, so it can travel lifted.\nconst SETTLE_AFTER = 200\n\n/*\n * A magnifying map for one axis: a ramp right across the lens, steeper at\n * the rim, so content near the middle swells and content at the rim bends.\n */\nfunction lensMap(axis: \"x\" | \"y\", width: number, height: number) {\n  const channel = (value: number) =>\n    axis === \"x\" ? `rgb(${value},128,128)` : `rgb(128,${value},128)`\n  const stops = [\n    [0, 255],\n    [0.12, 160],\n    [0.5, 128],\n    [0.88, 96],\n    [1, 0],\n  ]\n    .map(\n      ([at, value]) => `<stop offset=\"${at}\" stop-color=\"${channel(value!)}\"/>`\n    )\n    .join(\"\")\n  const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"${axis === \"x\" ? 1 : 0}\" y2=\"${axis === \"y\" ? 1 : 0}\">${stops}</linearGradient></defs><rect width=\"100%\" height=\"100%\" fill=\"url(#g)\"/></svg>`\n  return `data:image/svg+xml,${encodeURIComponent(svg)}`\n}\n\n/*\n * The glass lens on the current item. At rest it's a tinted pill under the\n * icon and label. Lifted, while pressed and while it travels to a new item,\n * it grows past the bar, clears, and moves over the item, magnifying it with\n * a fringe of color at the rim, the way iOS lifts its tab bar's selection.\n * The magnifying runs on url() in backdrop-filter, so only in Chromium;\n * elsewhere the lens still lifts and clears.\n */\nfunction GlassLens({\n  layoutId,\n  lifted,\n  transition,\n}: {\n  layoutId: string\n  lifted: boolean\n  transition: object\n}) {\n  const filterId = `floating-nav-lens-${React.useId().replace(/:/g, \"\")}`\n  const lensRef = React.useRef<HTMLSpanElement>(null)\n  const [box, setBox] = React.useState<{ width: number; height: number }>()\n\n  React.useLayoutEffect(() => {\n    const element = lensRef.current\n    if (!element) return\n    const observer = new ResizeObserver(() =>\n      setBox({\n        width: Math.round(element.offsetWidth),\n        height: Math.round(element.offsetHeight),\n      })\n    )\n    observer.observe(element)\n    return () => observer.disconnect()\n  }, [])\n\n  const bend = box ? Math.round(box.width * MAGNIFY) : 0\n  const channel = (key: keyof typeof DISPERSION, row: string) => {\n    if (!box) return null\n    const scale = bend * DISPERSION[key]\n    return (\n      <>\n        <feDisplacementMap\n          in=\"SourceGraphic\"\n          in2=\"x\"\n          scale={scale}\n          xChannelSelector=\"R\"\n          yChannelSelector=\"G\"\n          result={`${key}x`}\n        />\n        <feDisplacementMap\n          in={`${key}x`}\n          in2=\"y\"\n          scale={scale}\n          xChannelSelector=\"R\"\n          yChannelSelector=\"G\"\n          result={`${key}xy`}\n        />\n        <feColorMatrix\n          in={`${key}xy`}\n          type=\"matrix\"\n          values={`${row} 0 0 0 1 0`}\n          result={key}\n        />\n      </>\n    )\n  }\n\n  return (\n    <motion.span\n      ref={lensRef}\n      layoutId={layoutId}\n      aria-hidden=\"true\"\n      data-slot=\"floating-nav-lens\"\n      data-lifted={lifted ? \"\" : undefined}\n      initial={false}\n      animate={lifted ? LIFT : { scaleX: 1, scaleY: 1 }}\n      transition={{ ...transition, scaleX: LIFT_SPRING, scaleY: LIFT_SPRING }}\n      className={cn(\n        \"absolute inset-0 rounded-full transition-[background-color,box-shadow] duration-200\",\n        lifted\n          ? \"z-10 bg-transparent shadow-lg ring-1 inset-shadow-[0_1px_2px] ring-glass-edge inset-shadow-glass-edge\"\n          : \"bg-glass-lens inset-shadow-[0_1px_1px] inset-shadow-glass-edge\"\n      )}\n      style={\n        lifted && box ? { backdropFilter: `url(#${filterId})` } : undefined\n      }\n    >\n      {box ? (\n        <svg width=\"0\" height=\"0\" className=\"absolute\">\n          <filter\n            id={filterId}\n            x=\"0\"\n            y=\"0\"\n            width={box.width}\n            height={box.height}\n            filterUnits=\"userSpaceOnUse\"\n            colorInterpolationFilters=\"sRGB\"\n          >\n            <feImage\n              href={lensMap(\"x\", box.width, box.height)}\n              width={box.width}\n              height={box.height}\n              preserveAspectRatio=\"none\"\n              result=\"x\"\n            />\n            <feImage\n              href={lensMap(\"y\", box.width, box.height)}\n              width={box.width}\n              height={box.height}\n              preserveAspectRatio=\"none\"\n              result=\"y\"\n            />\n            {channel(\"R\", \"1 0 0 0 0  0 0 0 0 0  0 0 0 0 0\")}\n            {channel(\"G\", \"0 0 0 0 0  0 1 0 0 0  0 0 0 0 0\")}\n            {channel(\"B\", \"0 0 0 0 0  0 0 0 0 0  0 0 1 0 0\")}\n            <feBlend in=\"R\" in2=\"G\" mode=\"screen\" result=\"RG\" />\n            <feBlend in=\"RG\" in2=\"B\" mode=\"screen\" />\n          </filter>\n        </svg>\n      ) : null}\n    </motion.span>\n  )\n}\n\n/*\n * Whether the glass lens is lifted: from a press until a beat after it ends,\n * so a tap lifts it for the whole trip to the new item.\n */\nfunction useLift(enabled: boolean) {\n  const [lifted, setLifted] = React.useState(false)\n  const timer = React.useRef<number | undefined>(undefined)\n\n  React.useEffect(() => () => window.clearTimeout(timer.current), [])\n\n  const lift = React.useCallback(() => {\n    if (!enabled) return\n    window.clearTimeout(timer.current)\n    setLifted(true)\n  }, [enabled])\n  const settle = React.useCallback(() => {\n    window.clearTimeout(timer.current)\n    timer.current = window.setTimeout(() => setLifted(false), SETTLE_AFTER)\n  }, [])\n\n  return { lifted: enabled && lifted, lift, settle }\n}\n\nfunction useHiddenOnScroll(enabled: boolean) {\n  const [hidden, setHidden] = React.useState(false)\n\n  React.useEffect(() => {\n    if (!enabled) return\n    let last = window.scrollY\n    const onScroll = () => {\n      const y = window.scrollY\n      const delta = y - last\n      if (Math.abs(delta) < SCROLL_THRESHOLD) return\n      setHidden(delta > 0 && y > HIDE_AFTER)\n      last = y\n    }\n    window.addEventListener(\"scroll\", onScroll, { passive: true })\n    return () => window.removeEventListener(\"scroll\", onScroll)\n  }, [enabled])\n\n  return [enabled && hidden, setHidden] as const\n}\n\n/*\n * Each label's natural width in CSS pixels, for the current item to grow\n * into. Motion would measure \"auto\" on screen instead, which inside a zoomed\n * or scaled parent comes out in the wrong units, so the label ends a few\n * pixels short and snaps. scrollWidth isn't scaled. Measured again once web\n * fonts load, since they change the width, and whenever `key` changes: the\n * labels, their number or the size they're set in.\n */\nfunction useLabelWidths(key: string, enabled: boolean) {\n  const labels = React.useRef<(HTMLSpanElement | null)[]>([])\n  const [widths, setWidths] = React.useState<number[]>([])\n\n  React.useLayoutEffect(() => {\n    if (!enabled) return\n    let canceled = false\n    const measure = () => {\n      if (canceled) return\n      // A pixel over, since scrollWidth rounds and would clip the last letter.\n      setWidths(\n        labels.current.map((label) => (label ? label.scrollWidth + 1 : 0))\n      )\n    }\n    measure()\n    document.fonts?.ready.then(measure)\n    return () => {\n      canceled = true\n    }\n  }, [key, enabled])\n\n  return [labels, widths] as const\n}\n\nfunction FloatingNav({\n  items,\n  value: valueProp,\n  defaultValue,\n  onValueChange,\n  labels = \"active\",\n  size = \"default\",\n  position = \"fixed\",\n  hideOnScroll = false,\n  variant = \"default\",\n  className,\n  onFocus,\n  \"aria-label\": ariaLabel = \"Main\",\n  ...props\n}: FloatingNavProps) {\n  const [uncontrolled, setUncontrolled] = React.useState(defaultValue)\n  const value = valueProp !== undefined ? valueProp : uncontrolled\n  const [hidden, setHidden] = useHiddenOnScroll(hideOnScroll)\n  const reduceMotion = useReducedMotion()\n  const indicatorId = React.useId()\n  const glass = variant === \"glass\"\n  const transition = reduceMotion\n    ? { duration: 0 }\n    : glass\n      ? GLASS_SPRING\n      : SPRING\n  const { lifted, lift, settle } = useLift(glass && !reduceMotion)\n  const [labelRefs, labelWidths] = useLabelWidths(\n    [size, ...items.map((item) => item.label)].join(\"\\n\"),\n    labels !== \"always\"\n  )\n\n  return (\n    <nav\n      data-slot=\"floating-nav\"\n      data-position={position}\n      data-labels={labels}\n      data-size={size}\n      data-variant={variant}\n      data-hidden={hidden ? \"\" : undefined}\n      aria-label={ariaLabel}\n      // A keyboard user tabbing into a hidden bar brings it back.\n      onFocus={(event) => {\n        onFocus?.(event)\n        setHidden(false)\n      }}\n      className={cn(\n        \"flex justify-center\",\n        position === \"fixed\" &&\n          \"pointer-events-none fixed inset-x-0 bottom-[calc(env(safe-area-inset-bottom)+1rem)] z-50 px-4\",\n        className\n      )}\n      {...props}\n    >\n      <motion.ul\n        data-slot=\"floating-nav-list\"\n        initial={false}\n        animate={\n          hidden ? { y: \"calc(100% + 2rem)\", opacity: 0 } : { y: 0, opacity: 1 }\n        }\n        // Glass swells a touch under a finger, as Apple's bars do.\n        whileTap={glass && !reduceMotion ? { scale: 1.03 } : undefined}\n        onPointerDown={lift}\n        onPointerUp={settle}\n        onPointerCancel={settle}\n        transition={transition}\n        className={cn(\n          floatingNavVariants({ variant, size }),\n          // Out of sight, it mustn't catch taps meant for the page beneath.\n          // Its items stay focusable, so tabbing in still brings it back.\n          hidden && \"pointer-events-none\"\n        )}\n      >\n        {glass ? <GlassSurface /> : null}\n        {items.map((item, index) => {\n          const current = item.value === value\n          const handleClick = (\n            event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>\n          ) => {\n            if (valueProp === undefined) setUncontrolled(item.value)\n            onValueChange?.(item.value, event)\n            // Enter and Space lift it too, for the trip to the new item.\n            if (item.value !== value) {\n              lift()\n              settle()\n            }\n          }\n          const content = (\n            <>\n              {current && glass ? (\n                <GlassLens\n                  layoutId={indicatorId}\n                  lifted={lifted}\n                  transition={transition}\n                />\n              ) : current ? (\n                <motion.span\n                  layoutId={indicatorId}\n                  aria-hidden=\"true\"\n                  transition={transition}\n                  className=\"absolute inset-0 rounded-full bg-primary\"\n                />\n              ) : null}\n              {item.icon != null ? (\n                <span\n                  aria-hidden=\"true\"\n                  className={cn(\n                    \"relative flex items-center justify-center [&_svg]:shrink-0\",\n                    size === \"sm\"\n                      ? \"size-4 [&_svg]:size-4\"\n                      : \"size-5 [&_svg]:size-5\"\n                  )}\n                >\n                  {item.icon}\n                </span>\n              ) : null}\n              {item.icon == null ? (\n                <span className=\"relative whitespace-nowrap\">{item.label}</span>\n              ) : labels === \"always\" ? (\n                <span className=\"relative\">{item.label}</span>\n              ) : (\n                // Kept in the DOM at zero width, so every item keeps its name\n                // and the current one can grow into it.\n                <motion.span\n                  ref={(label) => {\n                    labelRefs.current[index] = label\n                  }}\n                  initial={false}\n                  animate={\n                    current\n                      ? {\n                          width: labelWidths[index] || \"auto\",\n                          opacity: 1,\n                          marginLeft: size === \"sm\" ? 6 : 8,\n                        }\n                      : { width: 0, opacity: 0, marginLeft: 0 }\n                  }\n                  transition={transition}\n                  className=\"relative overflow-hidden whitespace-nowrap\"\n                >\n                  {item.label}\n                </motion.span>\n              )}\n            </>\n          )\n          const itemProps = {\n            \"data-slot\": \"floating-nav-item\",\n            \"data-current\": current ? \"\" : undefined,\n            // \"page\" only means something on a link; a button item marks\n            // the current one of a set.\n            \"aria-current\": current\n              ? item.href !== undefined\n                ? (\"page\" as const)\n                : (\"true\" as const)\n              : undefined,\n            onClick: handleClick,\n            className: cn(\n              floatingNavItemVariants({ variant, labels, size }),\n              item.icon == null && (size === \"sm\" ? \"px-3.5\" : \"px-4\")\n            ),\n          }\n          return (\n            <li key={item.value} className=\"flex\">\n              {item.href !== undefined ? (\n                <a href={item.href} {...itemProps}>\n                  {content}\n                </a>\n              ) : (\n                <button type=\"button\" {...itemProps}>\n                  {content}\n                </button>\n              )}\n            </li>\n          )\n        })}\n      </motion.ul>\n    </nav>\n  )\n}\n\nexport {\n  FloatingNav,\n  floatingNavItemVariants,\n  floatingNavVariants,\n  type FloatingNavItem,\n  type FloatingNavProps,\n}\n",
      "type": "registry:ui"
    }
  ],
  "cssVars": {
    "theme": {
      "color-popover-overlay": "var(--popover-overlay)",
      "color-glass": "var(--glass)",
      "color-glass-edge": "var(--glass-edge)",
      "color-glass-lens": "var(--glass-lens)",
      "color-ring-subtle": "var(--ring-subtle)"
    },
    "light": {
      "popover-overlay": "color-mix(in oklch, var(--color-white) 85%, transparent)",
      "glass": "color-mix(in oklch, var(--color-neutral-50) 20%, transparent)",
      "glass-edge": "color-mix(in oklch, var(--color-white) 85%, transparent)",
      "glass-lens": "color-mix(in oklch, var(--color-neutral-900) 10%, transparent)",
      "ring-subtle": "color-mix(in oklch, var(--color-neutral-400) 50%, transparent)"
    },
    "dark": {
      "popover-overlay": "color-mix(in oklch, var(--color-neutral-900) 85%, transparent)",
      "glass": "color-mix(in oklch, var(--color-neutral-950) 20%, transparent)",
      "glass-edge": "color-mix(in oklch, var(--color-white) 15%, transparent)",
      "glass-lens": "color-mix(in oklch, var(--color-white) 10%, transparent)",
      "ring-subtle": "color-mix(in oklch, var(--color-neutral-500) 50%, transparent)"
    }
  },
  "meta": {
    "tier": "special-components",
    "group": "Navigation",
    "docs": "https://fibo.toribryan.com/?path=/docs/special-components-floating-nav--docs"
  },
  "docs": "Docs and live examples: https://fibo.toribryan.com/?path=/docs/special-components-floating-nav--docs",
  "categories": [
    "special-components",
    "navigation"
  ],
  "type": "registry:ui"
}