{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"navbar","type":"registry:ui","title":"Navbar","description":"A horizontal top-of-page bar with a brand mark, navigation links, and actions on the right.","author":"fancy-ui <https://fancy-ui.rama.app>","categories":["navigation"],"registryDependencies":["https://fancy-ui.rama.app/r/internals-dom.json","https://fancy-ui.rama.app/r/sound.json","https://fancy-ui.rama.app/r/utils.json"],"files":[{"path":"react/src/components/navbar/Navbar.tsx","content":"\"use client\";\n\nimport { forwardRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../../utils\";\nimport \"./navbar.css\";\n\nexport interface NavbarProps {\n\t/** Accessible name for the `<nav>` landmark. Defaults to `\"Main\"`. */\n\tlabel?: string;\n\t/** Pins the bar to the viewport top with `position: sticky` and a translucent, blurred background. */\n\tsticky?: boolean;\n\t/** Draws a 1px hairline along the bottom edge. Defaults to `true`. */\n\tbordered?: boolean;\n\t/** The brand mark / wordmark, on the left. */\n\tbrand?: ReactNode;\n\t/** The navigation links, between the brand and the actions. */\n\tchildren?: ReactNode;\n\t/** Actions on the right — search, sign-in, a theme switch. */\n\tactions?: ReactNode;\n\t/** Additional CSS classes */\n\tclassName?: string;\n}\n\n/**\n * Site-level navigation bar: brand on the left, links in the middle, actions\n * on the right.\n *\n * The root element arrives through the ref channel rather than a `ref` prop,\n * per PORTING.md — the Svelte source declares `ref = $bindable(null)`.\n */\nexport const Navbar = forwardRef<HTMLElement, NavbarProps>(\n\t({ label = \"Main\", sticky = false, bordered = true, brand, children, actions, className }, ref) => {\n\t\tconst classes = cn(\n\t\t\t\"ft-navbar flex h-[52px] w-full items-center gap-5 bg-background px-5\",\n\t\t\tbordered && \"border-border border-b\",\n\t\t\t// A translucent fill instead of the opaque default: content scrolling\n\t\t\t// underneath the pinned bar stays legible through the blur rather than\n\t\t\t// vanishing behind a flat panel.\n\t\t\tsticky && \"sticky top-0 z-40 bg-background/80 backdrop-blur-md\",\n\t\t\tclassName\n\t\t);\n\n\t\treturn (\n\t\t\t<nav ref={ref} aria-label={label} className={classes}>\n\t\t\t\t{brand ? (\n\t\t\t\t\t<div className=\"ft-navbar-brand flex shrink-0 items-center gap-2 text-sm font-bold\">\n\t\t\t\t\t\t{brand}\n\t\t\t\t\t</div>\n\t\t\t\t) : null}\n\t\t\t\t{children ? (\n\t\t\t\t\t<div className=\"flex min-w-0 items-center gap-5\">{children}</div>\n\t\t\t\t) : null}\n\t\t\t\t{actions ? (\n\t\t\t\t\t<div className=\"ft-navbar-actions ml-auto flex shrink-0 items-center gap-3\">\n\t\t\t\t\t\t{actions}\n\t\t\t\t\t</div>\n\t\t\t\t) : null}\n\t\t\t</nav>\n\t\t);\n\t}\n);\n\nNavbar.displayName = \"Navbar\";\n","type":"registry:ui","target":"@ui/fancy/components/navbar/Navbar.tsx"},{"path":"react/src/components/navbar/NavbarLink.tsx","content":"\"use client\";\n\nimport { forwardRef } from \"react\";\nimport type { AnchorHTMLAttributes, MouseEvent, ReactNode } from \"react\";\nimport { cn } from \"../../utils\";\nimport { useSoundCue } from \"../../sound/use-sound\";\nimport \"./navbar-link.css\";\n\nexport interface NavbarLinkProps extends Omit<\n\tAnchorHTMLAttributes<HTMLAnchorElement>,\n\t\"className\" | \"href\" | \"children\" | \"onClick\"\n> {\n\t/** Destination URL. */\n\thref: string;\n\t/**\n\t * Marks this as the page the visitor is already on: `aria-current=\"page\"`\n\t * plus a visible weight/colour change and an accent underline — current\n\t * is never conveyed by colour alone.\n\t */\n\tcurrent?: boolean;\n\t/**\n\t * Opens the link in a new tab with a safe `rel`, and appends a\n\t * visually-hidden \"(opens in a new tab)\" note to the accessible name.\n\t */\n\texternal?: boolean;\n\t/** Strips the link out of the click and keyboard-activation paths. */\n\tdisabled?: boolean;\n\t/** Native click handler. Never called while `disabled`. */\n\tonClick?: (event: MouseEvent<HTMLAnchorElement>) => void;\n\t/** The link's label. */\n\tchildren?: ReactNode;\n\t/** Additional CSS classes */\n\tclassName?: string;\n\t/**\n\t * Plays the select cue through the sound controller. Off by default;\n\t * only audible once the user has enabled sound.\n\t */\n\tsound?: boolean;\n}\n\n/**\n * A single navigation link, standalone enough to render anywhere — a mobile\n * menu, a footer — not only as a `Navbar` descendant.\n *\n * The root element arrives through the ref channel rather than a `ref` prop,\n * per PORTING.md — the Svelte source declares `ref = $bindable(null)`.\n */\nexport const NavbarLink = forwardRef<HTMLAnchorElement, NavbarLinkProps>(\n\t(\n\t\t{\n\t\t\thref,\n\t\t\tcurrent = false,\n\t\t\texternal = false,\n\t\t\tdisabled = false,\n\t\t\tonClick,\n\t\t\tchildren,\n\t\t\tclassName,\n\t\t\tsound = false,\n\t\t\t...rest\n\t\t},\n\t\tref\n\t) => {\n\t\tconst playCue = useSoundCue(sound);\n\t\tconst classes = cn(\n\t\t\t\"ft-navbar-link inline-flex shrink-0 items-center rounded-[4px] px-0.5 pb-[3px] text-[13px] whitespace-nowrap transition-colors\",\n\t\t\t\"focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:outline-none\",\n\t\t\tcurrent ? \"text-foreground font-medium\" : \"text-muted-foreground hover:text-foreground\",\n\t\t\tdisabled && \"pointer-events-none opacity-50\",\n\t\t\tclassName\n\t\t);\n\n\t\t// An <a> has no native `disabled` state at all, and a synthetic click (a\n\t\t// test's `fireEvent.click`, some assistive tech) reaches this handler\n\t\t// without going through any browser gate — `aria-disabled` and the\n\t\t// stripped `href` below are the visual/semantic story, this guard is what\n\t\t// actually stops the callback.\n\t\tfunction handleClick(event: MouseEvent<HTMLAnchorElement>) {\n\t\t\tif (disabled) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!current) playCue(\"select\");\n\t\t\tonClick?.(event);\n\t\t}\n\n\t\treturn (\n\t\t\t<a\n\t\t\t\t{...rest}\n\t\t\t\tref={ref}\n\t\t\t\thref={disabled ? undefined : href}\n\t\t\t\ttarget={external ? \"_blank\" : rest.target}\n\t\t\t\trel={external ? \"noopener noreferrer\" : rest.rel}\n\t\t\t\taria-current={current ? \"page\" : rest[\"aria-current\"]}\n\t\t\t\taria-disabled={disabled ? \"true\" : rest[\"aria-disabled\"]}\n\t\t\t\ttabIndex={disabled ? -1 : rest.tabIndex}\n\t\t\t\tclassName={classes}\n\t\t\t\tonClick={handleClick}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t\t{external ? <span className=\"sr-only\"> (opens in a new tab)</span> : null}\n\t\t\t</a>\n\t\t);\n\t}\n);\n\nNavbarLink.displayName = \"NavbarLink\";\n","type":"registry:ui","target":"@ui/fancy/components/navbar/NavbarLink.tsx"},{"path":"react/src/components/navbar/index.ts","content":"\"use client\";\n\nexport { Navbar } from \"./Navbar\";\nexport type { NavbarProps } from \"./Navbar\";\nexport { NavbarLink } from \"./NavbarLink\";\nexport type { NavbarLinkProps } from \"./NavbarLink\";\n","type":"registry:ui","target":"@ui/fancy/components/navbar/index.ts"},{"path":"react/src/components/navbar/navbar-link.css","content":"/*\n * Declared here too, not just on Navbar's root: `NavbarLink` is a fully\n * standalone component (\"keep them independent so a consumer can put a\n * NavbarLink anywhere\" — a mobile menu, a footer), so relying on\n * inheritance alone would leave the underline missing wherever it\n * doesn't happen to render as a Navbar descendant. Same shape as\n * `Autocomplete`/`Combobox`/`TimePicker`'s `--ft-field-accent` and\n * `Button`'s `--ft-btn-accent` — every one of those declares its own\n * copy of the same formula rather than trusting an ancestor. This reads\n * `--ft-accent`, never redeclares it, so a consumer retinting the whole\n * tree from higher up still reaches this.\n */\n.ft-navbar-link {\n\t--ft-nav-accent: var(\n\t\t--ft-accent,\n\t\tlight-dark(oklch(0.5432 0.2528 300.22), oklch(0.604 0.2606 301.75))\n\t);\n\t/* The underline below is an absolutely-positioned pseudo, which needs a\n\t   containing block. An inline-flex anchor taking `position: relative`\n\t   changes no layout. */\n\tposition: relative;\n}\n\n/*\n * The accent underline, character-for-character the declaration that used to\n * sit on the anchor itself — same inset shadow, same inherited radius, so\n * the resting look is unchanged. It lives one layer down for two reasons.\n * It can now be transitioned without transitioning the focus ring, which\n * shares the `box-shadow` property and must never animate. And it frees the\n * anchor's own `box-shadow`, which Tailwind's `focus-visible:ring-2`\n * compiles to: unlayered scoped CSS beats `@layer utilities` regardless of\n * specificity, so until now the current link's own underline was\n * suppressing its focus ring entirely.\n */\n.ft-navbar-link::before {\n\tcontent: \"\";\n\tposition: absolute;\n\tinset: 0;\n\tborder-radius: inherit;\n\tpointer-events: none;\n\tbox-shadow: inset 0 -2px 0 var(--ft-nav-accent);\n\topacity: 0;\n}\n\n.ft-navbar-link[aria-current=\"page\"]::before {\n\topacity: 1;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n\t/* `transform-origin: center` rather than a leading edge is deliberate:\n\t   unlike a sliding tab indicator this bar never travels between\n\t   siblings, so there is no leading edge to grow from, and `center`\n\t   reads the same in both writing directions for free.\n\t   150ms = tokens.DURATIONS.fast, cubic-bezier(0.4, 0, 0.2, 1) = tokens.EASINGS.inout */\n\t.ft-navbar-link::before {\n\t\ttransition:\n\t\t\topacity var(--ft-navbar-signal-duration, var(--ft-duration-fast, 150ms))\n\t\t\t\tvar(--ft-ease-inout, cubic-bezier(0.4, 0, 0.2, 1)),\n\t\t\ttransform var(--ft-navbar-signal-duration, var(--ft-duration-fast, 150ms))\n\t\t\t\tvar(--ft-ease-inout, cubic-bezier(0.4, 0, 0.2, 1));\n\t\ttransform: scaleX(0.4);\n\t\ttransform-origin: center;\n\t}\n\n\t.ft-navbar-link[aria-current=\"page\"]::before {\n\t\ttransform: scaleX(1);\n\t}\n}\n","type":"registry:ui","target":"@ui/fancy/components/navbar/navbar-link.css"},{"path":"react/src/components/navbar/navbar.css","content":"/*\n * `NavbarLink` declares this same fallback again, locally on itself —\n * deliberately, not a drift: it is a fully standalone component (\"keep\n * them independent so a consumer can put a NavbarLink anywhere\" — a\n * mobile menu, a footer), so this declaration here only reaches it when\n * it happens to render as a DOM descendant of a Navbar. `Autocomplete`/\n * `Combobox`/`TimePicker` and its portalled `TimePickerPanel` are the\n * precedent — a component that can render somewhere other than as a\n * literal descendant needs its own copy, not just an ancestor's. Neither\n * declaration redeclares `--ft-accent` itself, so a consumer retinting\n * the whole tree via `--ft-accent` still reaches both.\n */\n.ft-navbar {\n\t--ft-nav-accent: var(\n\t\t--ft-accent,\n\t\tlight-dark(oklch(0.5432 0.2528 300.22), oklch(0.604 0.2606 301.75))\n\t);\n}\n","type":"registry:ui","target":"@ui/fancy/components/navbar/navbar.css"}],"meta":{"license":"MIT","package":"fancy-ui-react","version":"0.7.0","source":"https://fancy-ui.rama.app/docs/components/navbar?framework=react"}}