{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "navigation-menu",
  "title": "Navigation Menu",
  "type": "registry:ui",
  "description": "Top-of-page horizontal navigation with hover/click triggered megamenus. Use for marketing sites and product navs that need rich content — featured links, mini-cards, and submenu columns.",
  "categories": [
    "navigation"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/navigation-menu/navigation-menu.ts",
      "content": "import { LitElement, css, html, isServer, nothing, type PropertyDeclarations } from 'lit'\nimport { ChevronDown } from 'lucide'\nimport { cn } from '../../lib/utils'\nimport { icon } from '../../lib/icon'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { navigationMenuTriggerStyle } from './navigation-menu.variants'\n\nexport { navigationMenuTriggerStyle }\n\n// React's class strings, verbatim (packages/registry-react/components/navigation-menu).\nconst rootClasses = 'group/navigation-menu relative flex max-w-max flex-1 items-center justify-center'\nconst listClasses = 'group flex flex-1 list-none items-center justify-center gap-1'\nconst itemClasses = 'relative'\nconst chevronClasses = 'relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180'\nconst contentClasses = cn(\n  'data-[motion^=from-]:motion-safe:animate-in data-[motion^=to-]:motion-safe:animate-out data-[motion^=from-]:motion-safe:fade-in data-[motion^=to-]:motion-safe:fade-out data-[motion=from-end]:motion-safe:slide-in-from-right-52 data-[motion=from-start]:motion-safe:slide-in-from-left-52 data-[motion=to-end]:motion-safe:slide-out-to-right-52 data-[motion=to-start]:motion-safe:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto',\n  'group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:motion-safe:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:motion-safe:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:motion-safe:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:motion-safe:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:motion-safe:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:motion-safe:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none',\n)\nconst linkClasses =\n  \"data-active:focus:bg-accent data-active:hover:bg-accent data-active:bg-accent/50 data-active:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4\"\nconst indicatorClasses =\n  'data-[state=visible]:motion-safe:animate-in data-[state=hidden]:motion-safe:animate-out data-[state=hidden]:motion-safe:fade-out data-[state=visible]:motion-safe:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden'\nconst viewportWrapperClasses = 'absolute top-full left-0 isolate z-50 flex justify-center'\nconst viewportClasses =\n  'origin-top-center bg-popover text-popover-foreground data-[state=open]:motion-safe:animate-in data-[state=closed]:motion-safe:animate-out data-[state=closed]:motion-safe:zoom-out-95 data-[state=open]:motion-safe:zoom-in-90 relative left-[var(--radix-navigation-menu-viewport-left)] mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]'\n\n/** Run `done` once `el`'s own animations finish (or 400ms, for hidden tabs). */\nfunction afterAnimations(el: Element | null | undefined, done: () => void) {\n  let fired = false\n  const fin = () => {\n    if (fired) return\n    fired = true\n    done()\n  }\n  setTimeout(fin, 400)\n  requestAnimationFrame(() => {\n    const anims = el?.getAnimations() ?? []\n    if (anims.length) Promise.all(anims.map((a) => a.finished)).then(fin, fin)\n    else fin()\n  })\n}\n\nconst booleanDefaultTrue = {\n  fromAttribute: (v: string | null) => v !== 'false',\n  toAttribute: (v: boolean) => (v ? '' : 'false'),\n}\n\nlet uid = 0\n\n/**\n * <uip-navigation-menu> — the registry NavigationMenu (Radix) as web components.\n *\n *   <uip-navigation-menu>                       ← NavigationMenu + List (+ Viewport)\n *     <uip-navigation-menu-item>                ← NavigationMenuItem\n *       <span slot=\"trigger\">Getting started</span>   ← NavigationMenuTrigger children\n *       <ul slot=\"content\" class=\"grid w-72 gap-2 p-4\">…</ul>  ← NavigationMenuContent children\n *     </uip-navigation-menu-item>\n *     <uip-navigation-menu-item>\n *       <uip-navigation-menu-link href=\"#\" trigger-style>Pricing</uip-navigation-menu-link>\n *     </uip-navigation-menu-item>\n *   </uip-navigation-menu>\n *\n * Each item renders its trigger, content and (with `viewport`, the default)\n * the viewport box in its OWN shadow root, so aria-controls/aria-labelledby\n * resolve; the viewport is absolutely positioned against this element's\n * `relative` root exactly like React's. Panel content stays light DOM (styled\n * by the page, links stay real links).\n *\n * Properties: `value` (open item's value, '' = closed), `viewport` (default\n * true; `viewport=\"false\"` renders content under its trigger), `indicator`\n * (renders NavigationMenuIndicator), `delayDuration` (200), `skipDelayDuration` (300).\n * Events: `value-change` { value }.\n */\nexport class UipNavigationMenu extends LitElement {\n  static styles = [tailwind, css`:host { display: contents; }`]\n\n  static properties: PropertyDeclarations = {\n    value: { reflect: true },\n    viewport: { type: Boolean, converter: booleanDefaultTrue, reflect: true },\n    indicator: { type: Boolean },\n    delayDuration: { type: Number, attribute: 'delay-duration' },\n    skipDelayDuration: { type: Number, attribute: 'skip-delay-duration' },\n    accessibleLabel: { attribute: 'aria-label' },\n    indicatorState: { state: true },\n  }\n\n  value = ''\n  viewport = true\n  indicator = false\n  delayDuration = 200\n  skipDelayDuration = 300\n  accessibleLabel?: string\n  private indicatorState: 'visible' | 'hidden' | 'gone' = 'gone'\n  private openTimer?: ReturnType<typeof setTimeout>\n  private closeTimer?: ReturnType<typeof setTimeout>\n  private lastClosedAt = 0\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'navigation-menu')\n  }\n\n  get items() {\n    if (isServer) return []\n    return [...this.children].filter((c): c is UipNavigationMenuItem => c instanceof UipNavigationMenuItem)\n  }\n\n  private itemFor(value: string) {\n    return this.items.find((i) => i.itemValue === value)\n  }\n\n  // --- open state -------------------------------------------------------------\n  setValue(value: string) {\n    if (value === this.value) return\n    this.value = value\n    this.dispatchEvent(new CustomEvent('value-change', { detail: { value }, bubbles: true, composed: true }))\n  }\n\n  protected willUpdate(changed: Map<string, unknown>) {\n    if (changed.has('value')) {\n      const prev = (changed.get('value') as string | undefined) ?? ''\n      const items = this.items\n      const from = items.findIndex((i) => i.itemValue === prev)\n      const to = items.findIndex((i) => i.itemValue === this.value)\n      if (!this.value) this.lastClosedAt = Date.now()\n      items.forEach((item, i) => {\n        const switching = from >= 0 && to >= 0\n        if (i === to) item.setExpanded(true, switching ? (to > from ? 'from-end' : 'from-start') : undefined)\n        else if (i === from) item.setExpanded(false, switching ? (to > from ? 'to-start' : 'to-end') : undefined)\n      })\n      if (this.value) this.indicatorState = 'visible'\n      else if (this.indicatorState === 'visible') {\n        this.indicatorState = 'hidden'\n        this.updateComplete.then(() =>\n          afterAnimations(this.renderRoot.querySelector('[data-slot=navigation-menu-indicator]'), () => {\n            if (!this.value) this.indicatorState = 'gone'\n          }),\n        )\n      }\n    }\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    removeEventListener('pointerdown', this.onOutsidePointer, true)\n  }\n\n  // Radix DismissableLayer: a pointerdown outside the menu closes it.\n  private onOutsidePointer = (e: PointerEvent) => {\n    if (!e.composedPath().includes(this)) this.setValue('')\n  }\n\n  protected updated(changed: Map<string, unknown>) {\n    if (changed.has('value')) {\n      if (this.value) addEventListener('pointerdown', this.onOutsidePointer, true)\n      else removeEventListener('pointerdown', this.onOutsidePointer, true)\n    }\n    if (changed.has('viewport')) this.items.forEach((i) => i.requestUpdate())\n    if (changed.has('value') && this.value) this.placeIndicator()\n  }\n\n  // Written straight to the element's style: it's measured after render, and a\n  // reactive property set in updated() would schedule a second update.\n  private placeIndicator() {\n    const trigger = this.itemFor(this.value)?.triggerEl\n    const nav = this.renderRoot.querySelector('nav')\n    const el = this.renderRoot.querySelector<HTMLElement>('[data-slot=navigation-menu-indicator]')\n    if (!trigger || !nav || !el) return\n    const t = trigger.getBoundingClientRect()\n    const n = nav.getBoundingClientRect()\n    // Radix Indicator: absolute, left 0, width = trigger, translateX(offset).\n    Object.assign(el.style, {\n      position: 'absolute',\n      left: '0px',\n      width: `${t.width}px`,\n      transform: `translateX(${t.left - n.left}px)`,\n    })\n  }\n\n  // --- hover intent (Radix delayDuration / skipDelayDuration) --------------------\n  onTriggerEnter(item: UipNavigationMenuItem) {\n    clearTimeout(this.openTimer)\n    clearTimeout(this.closeTimer)\n    const skip = !!this.value || Date.now() - this.lastClosedAt < this.skipDelayDuration\n    if (skip) this.setValue(item.itemValue)\n    else this.openTimer = setTimeout(() => this.setValue(item.itemValue), this.delayDuration)\n  }\n\n  startClose() {\n    clearTimeout(this.openTimer)\n    clearTimeout(this.closeTimer)\n    this.closeTimer = setTimeout(() => this.setValue(''), 150)\n  }\n\n  cancelClose() {\n    clearTimeout(this.closeTimer)\n  }\n\n  toggle(item: UipNavigationMenuItem) {\n    clearTimeout(this.openTimer)\n    clearTimeout(this.closeTimer)\n    this.setValue(this.value === item.itemValue ? '' : item.itemValue)\n  }\n\n  // --- keyboard: roving focus across the top-level triggers/links ----------------\n  private onKeyDown(e: KeyboardEvent) {\n    const items = this.items\n    const from = items.findIndex((i) => i.isTopLevelTarget(e.composedPath()))\n    if (from < 0) return\n    let to = -1\n    if (e.key === 'ArrowRight') to = Math.min(from + 1, items.length - 1)\n    else if (e.key === 'ArrowLeft') to = Math.max(from - 1, 0)\n    else if (e.key === 'Home') to = 0\n    else if (e.key === 'End') to = items.length - 1\n    else return\n    e.preventDefault()\n    items[to]?.focusTopLevel()\n  }\n\n  // Close when focus leaves the whole menu (Radix onFocusOutside).\n  private onFocusOut(e: FocusEvent) {\n    const next = e.relatedTarget as Node | null\n    if (!next || !this.value) return\n    if (this.contains(next) || this.renderRoot.contains(next)) return\n    // relatedTarget is retargeted to a host when focus moves into another\n    // shadow root; our items' shadow roots live under this element.\n    this.setValue('')\n  }\n\n  render() {\n    return html`<nav\n      aria-label=${this.accessibleLabel ?? 'Main'}\n      data-orientation=\"horizontal\"\n      data-viewport=${this.viewport ? 'true' : 'false'}\n      class=${rootClasses}\n      @keydown=${this.onKeyDown}\n      @focusout=${this.onFocusOut}\n    >\n      <ul data-uipkge=\"\" data-slot=\"navigation-menu-list\" data-orientation=\"horizontal\" class=${listClasses}>\n        <slot></slot>\n      </ul>\n      ${this.indicator && this.indicatorState !== 'gone'\n        ? html`<div\n            aria-hidden=\"true\"\n            data-uipkge=\"\"\n            data-slot=\"navigation-menu-indicator\"\n            data-orientation=\"horizontal\"\n            data-state=${this.indicatorState === 'visible' ? 'visible' : 'hidden'}\n            class=${indicatorClasses}\n          >\n            <div class=\"bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md\"></div>\n          </div>`\n        : nothing}\n    </nav>`\n  }\n}\n\n/**\n * <uip-navigation-menu-item> — one top-level entry. With a `trigger` slot it\n * renders NavigationMenuTrigger (button + chevron) and NavigationMenuContent\n * (the `content` slot); otherwise its default slot holds a link.\n * Property: `value` (defaults to an auto id).\n *\n * The host is the list item (`role=\"listitem\"` as a plain attribute, so the\n * root's <ul> > <slot> > host is a valid list for AT — ElementInternals'\n * `.role` does not surface for these hosts in Chromium, verified in the\n * accessibility tree). `part=\"base\"` is React's `<li class=\"relative\">` box:\n * style it with `class=\"[&::part(base)]:…\"`.\n */\nexport class UipNavigationMenuItem extends LitElement {\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties: PropertyDeclarations = {\n    value: {},\n    expanded: { state: true },\n    mounted: { state: true },\n    motion: { state: true },\n    hasTrigger: { state: true },\n  }\n\n  value?: string\n  private expanded = false\n  private mounted = false\n  private motion?: string\n  private hasTrigger = false\n  private readonly autoValue = `item-${++uid}`\n  private readonly idBase = `uip-nav-${uid}`\n  private ro?: ResizeObserver\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'navigation-menu-item')\n    // The host sits in the root's <ul>; it is the list item.\n    this.setAttribute('role', 'listitem')\n    if (isServer) return\n    const read = () => (this.hasTrigger = !!this.querySelector(':scope > [slot=\"trigger\"]'))\n    read()\n    // Frameworks may render the slotted children after the host connects.\n    this.childObserver = new MutationObserver(read)\n    this.childObserver.observe(this, { childList: true })\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    this.ro?.disconnect()\n    this.childObserver?.disconnect()\n  }\n\n  private childObserver?: MutationObserver\n\n  get itemValue() {\n    return this.value || this.autoValue\n  }\n\n  private get root() {\n    return isServer ? null : this.closest('uip-navigation-menu')\n  }\n\n  get triggerEl() {\n    return this.renderRoot?.querySelector<HTMLButtonElement>('[data-slot=navigation-menu-trigger]') ?? null\n  }\n\n  private get contentEl() {\n    return this.renderRoot?.querySelector<HTMLElement>('[data-slot=navigation-menu-content]') ?? null\n  }\n\n  private get defaultSlotted() {\n    const slot = this.renderRoot?.querySelector<HTMLSlotElement>('slot:not([name])')\n    return (slot?.assignedElements()[0] as HTMLElement | undefined) ?? null\n  }\n\n  isTopLevelTarget(path: EventTarget[]) {\n    const t = this.triggerEl ?? this.defaultSlotted\n    return !!t && path.includes(t)\n  }\n\n  focusTopLevel() {\n    ;(this.triggerEl ?? this.defaultSlotted)?.focus()\n  }\n\n  /** Called by the root when the open value changes. */\n  setExpanded(expanded: boolean, motion?: string) {\n    this.motion = motion\n    if (expanded) {\n      this.expanded = true\n      this.mounted = true\n      return\n    }\n    if (!this.expanded) return\n    this.expanded = false\n    this.updateComplete.then(() => {\n      const viewport = this.renderRoot.querySelector('[data-slot=navigation-menu-viewport]')\n      afterAnimations(motion ? this.contentEl : (viewport ?? this.contentEl), () => {\n        if (!this.expanded) this.mounted = false\n      })\n    })\n  }\n\n  protected updated(changed: Map<string, unknown>) {\n    if (changed.has('mounted') && !isServer) {\n      this.ro?.disconnect()\n      const content = this.contentEl\n      if (this.mounted && content) {\n        this.ro = new ResizeObserver(() => this.measure())\n        this.ro.observe(content)\n        this.measure()\n      }\n    }\n  }\n\n  // Written straight to the viewport's style: the size is measured after\n  // render, and a reactive property set in updated() would schedule a second\n  // update. (The viewport is only rendered while mounted, so the vars never go stale.)\n  private measure() {\n    const c = this.contentEl\n    const viewport = this.renderRoot.querySelector<HTMLElement>('[data-slot=navigation-menu-viewport]')\n    if (!c || !viewport) return\n    viewport.style.setProperty('--radix-navigation-menu-viewport-width', `${c.offsetWidth}px`)\n    viewport.style.setProperty('--radix-navigation-menu-viewport-height', `${c.offsetHeight}px`)\n  }\n\n  private onTriggerPointerEnter(e: PointerEvent) {\n    if (e.pointerType === 'mouse') this.root?.onTriggerEnter(this)\n  }\n\n  private onPointerLeave(e: PointerEvent) {\n    if (e.pointerType === 'mouse') this.root?.startClose()\n  }\n\n  private onContentPointerEnter(e: PointerEvent) {\n    if (e.pointerType === 'mouse') this.root?.cancelClose()\n  }\n\n  private onTriggerKeyDown(e: KeyboardEvent) {\n    if (e.key !== 'ArrowDown') return\n    e.preventDefault()\n    const focusFirst = () => {\n      const slot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"content\"]')\n      const first = slot\n        ?.assignedElements({ flatten: true })\n        .flatMap((el) => [el, ...el.querySelectorAll('*')])\n        .find((el): el is HTMLElement => el instanceof HTMLElement && el.matches('a[href], button, [tabindex]:not([tabindex=\"-1\"]), input, select, textarea, uip-navigation-menu-link'))\n      first?.focus()\n    }\n    const root = this.root\n    if (!this.expanded) root?.toggle(this)\n    // The root re-renders first (it calls setExpanded), then this item mounts the panel.\n    Promise.resolve(root?.updateComplete).then(() => this.updateComplete).then(focusFirst)\n  }\n\n  private onKeyDown(e: KeyboardEvent) {\n    if (e.key === 'Escape' && this.expanded) {\n      e.preventDefault()\n      this.root?.setValue('')\n      this.triggerEl?.focus()\n    }\n  }\n\n  // A click on a link inside the panel closes the menu (Radix onSelect).\n  private onContentClick(e: MouseEvent) {\n    if (e.composedPath().some((n) => n instanceof HTMLAnchorElement)) this.root?.setValue('')\n  }\n\n  private renderContent() {\n    const id = `${this.idBase}-content`\n    return html`<div\n      id=${id}\n      aria-labelledby=${`${this.idBase}-trigger`}\n      data-uipkge=\"\"\n      data-slot=\"navigation-menu-content\"\n      data-state=${this.expanded ? 'open' : 'closed'}\n      data-motion=${this.motion ?? nothing}\n      data-orientation=\"horizontal\"\n      class=${contentClasses}\n      @pointerenter=${this.onContentPointerEnter}\n      @pointerleave=${this.onPointerLeave}\n      @click=${this.onContentClick}\n    >\n      <slot name=\"content\"></slot>\n    </div>`\n  }\n\n  render() {\n    const viewport = this.root?.viewport ?? true\n    const state = this.expanded ? 'open' : 'closed'\n    const trigger = this.hasTrigger\n      ? html`<button\n          id=${`${this.idBase}-trigger`}\n          type=\"button\"\n          aria-expanded=${this.expanded ? 'true' : 'false'}\n          aria-controls=${`${this.idBase}-content`}\n          data-uipkge=\"\"\n          data-slot=\"navigation-menu-trigger\"\n          data-state=${state}\n          class=${cn(navigationMenuTriggerStyle(), 'group')}\n          @pointerenter=${this.onTriggerPointerEnter}\n          @pointerleave=${this.onPointerLeave}\n          @click=${() => this.root?.toggle(this)}\n          @keydown=${this.onTriggerKeyDown}\n        >\n          <slot name=\"trigger\"></slot>${icon(ChevronDown, 'chevron-down', chevronClasses)}\n        </button>`\n      : nothing\n    return html`<div class=\"group/navigation-menu contents\" data-viewport=${viewport ? 'true' : 'false'} @keydown=${this.onKeyDown}>\n      <div part=\"base\" class=${itemClasses}>\n        ${trigger}\n        <slot @slotchange=${() => this.requestUpdate()}></slot>\n        ${this.hasTrigger && !viewport && this.mounted ? this.renderContent() : nothing}\n      </div>\n      ${this.hasTrigger && viewport && this.mounted\n        ? html`<div class=${viewportWrapperClasses}>\n            <div\n              data-uipkge=\"\"\n              data-slot=\"navigation-menu-viewport\"\n              data-state=${state}\n              data-orientation=\"horizontal\"\n              class=${viewportClasses}\n              @pointerenter=${this.onContentPointerEnter}\n              @pointerleave=${this.onPointerLeave}\n            >\n              ${this.renderContent()}\n            </div>\n          </div>`\n        : nothing}\n    </div>`\n  }\n}\n\n/**\n * <uip-navigation-menu-link> — NavigationMenuLink: an <a> with React's link\n * classes. `trigger-style` merges `navigationMenuTriggerStyle()` (what the\n * React stories pass as className for top-level links); `active` sets\n * data-active. Emits `select` (cancelable) on click.\n */\nexport class UipNavigationMenuLink extends LitElement {\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  static styles = [tailwind, css`:host { display: contents; }`]\n\n  static properties: PropertyDeclarations = {\n    href: {},\n    target: {},\n    active: { type: Boolean, reflect: true },\n    triggerStyle: { type: Boolean, attribute: 'trigger-style' },\n  }\n\n  href?: string\n  target?: string\n  active = false\n  triggerStyle = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n  }\n\n  private onClick(e: MouseEvent) {\n    const ok = this.dispatchEvent(new CustomEvent('select', { bubbles: true, composed: true, cancelable: true }))\n    if (!ok) e.preventDefault()\n  }\n\n  render() {\n    return html`<a\n      href=${this.href ?? nothing}\n      target=${this.target ?? nothing}\n      aria-current=${this.active ? 'page' : nothing}\n      ?data-active=${this.active}\n      data-uipkge=\"\"\n      data-slot=\"navigation-menu-link\"\n      class=${cn(linkClasses, this.triggerStyle && navigationMenuTriggerStyle())}\n      @click=${this.onClick}\n      ><slot></slot\n    ></a>`\n  }\n}\n\ncustomElements.get('uip-navigation-menu') || customElements.define('uip-navigation-menu', UipNavigationMenu)\ncustomElements.get('uip-navigation-menu-item') || customElements.define('uip-navigation-menu-item', UipNavigationMenuItem)\ncustomElements.get('uip-navigation-menu-link') || customElements.define('uip-navigation-menu-link', UipNavigationMenuLink)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-navigation-menu': UipNavigationMenu\n    'uip-navigation-menu-item': UipNavigationMenuItem\n    'uip-navigation-menu-link': UipNavigationMenuLink\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/navigation-menu/navigation-menu.ts"
    },
    {
      "path": "packages/elements-lit/src/components/navigation-menu/navigation-menu.variants.ts",
      "content": "import { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts`) so consuming Vue SFCs can import without creating a circular\n * dependency through the index. See card.variants.ts for the canonical\n * example + the SSR symptom that motivated the split.\n */\n\nexport const navigationMenuTriggerStyle = cva(\n  'group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1',\n)\n",
      "type": "registry:file",
      "target": "src/uip/components/navigation-menu/navigation-menu.variants.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
