Navigation Menu

PohonGitHub
A list of links that can be displayed horizontally or vertically.
<script setup lang="ts">
import {
  ANavigationMenuContent,
  ANavigationMenuIndicator,
  ANavigationMenuItem,
  ANavigationMenuLink,
  ANavigationMenuList,
  ANavigationMenuRoot,
  ANavigationMenuTrigger,
  ANavigationMenuViewport,
} from 'akar';
import { ref } from 'vue';

const currentTrigger = ref('');

const items = ref<Array<any>>([
  {
    label: 'Guide',
    icon: 'i-lucide:book-open',
    to: '/docs/akar/overview/getting-started',
    children: [
      {
        label: 'Introduction',
        description: 'Fully styled and customizable components for Nuxt.',
        icon: 'i-lucide:house',
      },
      {
        label: 'Installation',
        description: 'Learn how to install and configure Pohon UI in your application.',
        icon: 'i-lucide:cloud-download',
      },
      {
        label: 'Icons',
        icon: 'i-lucide:smile',
        description: 'You have nothing to do, @nuxt/icon will handle it automatically.',
      },
      {
        label: 'Colors',
        icon: 'i-lucide:swatch-book',
        description: 'Choose a primary and a neutral color from your UnoCSS theme.',
      },
      {
        label: 'Theme',
        icon: 'i-lucide:cog',
        description: 'You can customize components by using the `class` / `pohon` props or in your app.config.ts.',
      },
    ],
  },
  {
    label: 'Composables',
    icon: 'i-lucide:database',
    to: '/docs/pohon/composables',
    children: [
      {
        label: 'defineShortcuts',
        icon: 'i-lucide:file-text',
        description: 'Define shortcuts for your application.',
        to: '/docs/pohon/composables/define-shortcuts',
      },
      { label: 'useOverlay', icon: 'i-lucide:file-text', description: 'Display a modal/slideover within your application.', to: '/docs/pohon/composables/use-overlay' },
      { label: 'useToast', icon: 'i-lucide:file-text', description: 'Display a toast within your application.', to: '/docs/pohon/composables/use-toast' },
    ],
  },
  {
    label: 'Components',
    icon: 'i-lucide:box',
    to: '/docs/pohon/components',
    active: true,
    children: [
      {
        label: 'Link',
        icon: 'i-lucide:file-text',
        description: 'Use NuxtLink with superpowers.',
        to: '/docs/pohon/components/link',
      },
      {
        label: 'Modal',
        icon: 'i-lucide:file-text',
        description: 'Display a modal within your application.',
        to: '/docs/pohon/components/dialog',
      },
      {
        label: 'NavigationMenu',
        icon: 'i-lucide:file-text',
        description: 'Display a list of links.',
        to: '/docs/pohon/components/navigation-menu',
      },
      {
        label: 'Pagination',
        icon: 'i-lucide:file-text',
        description: 'Display a list of pages.',
        to: '/docs/pohon/components/pagination',
      },
      {
        label: 'Popover',
        icon: 'i-lucide:file-text',
        description: 'Display a non-modal dialog that floats around a trigger element.',
        to: '/docs/pohon/components/popover',
      },
      {
        label: 'Progress',
        icon: 'i-lucide:file-text',
        description: 'Show a horizontal bar to indicate task progression.',
        to: '/docs/pohon/components/progress',
      },
    ],
  },
  {
    label: 'GitHub',
    icon: 'i-simple-icons:github',
    badge: '3.8k',
    to: 'https://github.com/nuxt/ui',
    target: '_blank',
  },
  {
    label: 'Help',
    icon: 'i-lucide:circle-help',
    disabled: true,
  },
]);
</script>

<template>
  <ANavigationMenuRoot
    v-model="currentTrigger"
    class="bg-background flex gap-1.5 w-full items-center justify-center relative [&>div]:min-w-0"
  >
    <ANavigationMenuList class="flex min-w-0 items-center isolate">
      <ANavigationMenuItem
        v-for="item in items"
        :key="item.label"
        class="py-2 min-w-0"
      >
        <ANavigationMenuLink
          v-if="!item.children"
          class="group text-sm color-text-muted font-medium px-2.5 py-1.5 gap-1.5 grid grid-cols-[min-content_1fr_min-content] w-full cursor-pointer transition-colors-280 items-center relative data-[state=open]:color-text-highlighted hover:color-text-highlighted focus-visible:(outline-none before:ring-2 before:ring-primary before:ring-inset) focus:outline-none before:(rounded-md content-empty transition-colors-280 inset-x-px inset-y-0 absolute -z-1) dark:focus-visible:outline-none data-[state=open]:before:bg-background-elevated/50 hover:before:bg-background-elevated/50"
        >
          <span
            class="color-text-dimmed shrink-0 size-5 transition-colors-280 group-data-[state=open]:color-text group-hover:color-text"
            :class="item.icon"
          />

          {{ item.label }}
        </ANavigationMenuLink>
        <ANavigationMenuTrigger
          v-else
          class="group text-sm color-text-muted font-medium px-2.5 py-1.5 gap-1.5 grid grid-cols-[min-content_1fr_min-content] w-full transition-colors-280 items-center relative data-[state=open]:color-text-highlighted hover:color-text-highlighted focus-visible:(outline-none before:ring-2 before:ring-primary before:ring-inset) focus:outline-none before:(rounded-md content-empty transition-colors-280 inset-x-px inset-y-0 absolute -z-1) dark:focus-visible:outline-none data-[state=open]:before:bg-background-elevated/50 hover:before:bg-background-elevated/50"
        >
          <span
            class="color-text-dimmed shrink-0 size-5 transition-colors-280 group-data-[state=open]:color-text group-hover:color-text"
            :class="item.icon"
          />

          {{ item.label }}

          <span class="group ms-auto inline-flex gap-1.5 items-center">
            <span
              class="i-lucide:chevron-down shrink-0 size-5 transform transition-transform-280 group-data-[state=open]:rotate-180"
            />
          </span>
        </ANavigationMenuTrigger>

        <ANavigationMenuContent
          v-if="!!item.children"
          class="max-h-[70vh] w-full left-0 top-0 absolute overflow-y-auto data-[motion^=from-]:(animate-in fade-in) data-[motion^=to-]:(animate-out fade-out) data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52"
        >
          <ul class="p-2 gap-2 grid grid-cols-2 isolate">
            <li
              v-for="child in item.children"
              :key="child.label"
            >
              <ANavigationMenuLink as-child>
                <a
                  class="group text-sm color-text px-3 py-2 text-start flex gap-2 size-full transition-colors-280 items-start relative hover:color-text-highlighted focus-visible:outline-none focus:outline-none before:rounded-md before:content-empty before:transition-colors-280 before:inset-x-px before:inset-y-0 before:absolute dark:focus-visible:outline-none hover:before:bg-background-elevated/50 focus-visible:before:ring-2 focus-visible:before:ring-primary focus-visible:before:ring-inset before:-z-1"
                  :href="child.to"
                >
                  <span
                    class="color-text-dimmed shrink-0 size-5 transition-colors-280 group-hover:color-text"
                    :class="child.icon"
                  />
                  <div class="min-w-0">
                    <p class="font-medium truncate">
                      {{ child.label }}
                    </p>
                    <p class="color-text-muted">
                      {{ child.description }}
                    </p>
                  </div>
                </a>
              </ANavigationMenuLink>
            </li>
          </ul>
        </ANavigationMenuContent>
      </ANavigationMenuItem>
    </ANavigationMenuList>

    <div class="flex w-full left-0 top-full absolute">
      <ANavigationMenuIndicator
        class="flex h-2.5 w-$akar-navigation-menu-indicator-size translate-x-$akar-navigation-menu-indicator-position transition-[transform,width]-280 items-end bottom-0 justify-center absolute z-2 overflow-hidden data-[state=hidden]:(animate-out fade-out) data-[state=visible]:(animate-in fade-in)"
      >
        <div class="border border-border rounded-xs bg-background size-2.5 rotate-45 top-[50%] relative z-1" />
      </ANavigationMenuIndicator>

      <ANavigationMenuViewport
        class="rounded-md bg-background h-$akar-navigation-menu-viewport-height w-full ring ring-ring shadow-lg origin-[top_center] transition-[width,height,left]-280 relative z-1 overflow-hidden data-[state=closed]:(animate-out fade-out-0 zoom-out-95) data-[state=open]:(animate-in zoom-in-90)"
      />
    </div>
  </ANavigationMenuRoot>
</template>

Features

  • Can be controlled or uncontrolled.
  • Flexible layout structure with managed tab focus.
  • Supports submenus.
  • Optional active item indicator.
  • Full keyboard navigation.
  • Exposes CSS variables for advanced animation.
  • Supports custom timings.

Anatomy

Import all parts and piece them together.

<script setup lang="ts">
import {
  ANavigationMenuContent,
  ANavigationMenuIndicator,
  ANavigationMenuItem,
  ANavigationMenuLink,
  ANavigationMenuList,
  ANavigationMenuRoot,
  ANavigationMenuSub,
  ANavigationMenuTrigger,
  ANavigationMenuViewport,
} from 'akar';
</script>

<template>
  <ANavigationMenuRoot>
    <ANavigationMenuList>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger />
        <ANavigationMenuContent>
          <ANavigationMenuLink />
        </ANavigationMenuContent>
      </ANavigationMenuItem>

      <ANavigationMenuItem>
        <ANavigationMenuLink />
      </ANavigationMenuItem>

      <ANavigationMenuItem>
        <ANavigationMenuTrigger />
        <ANavigationMenuContent>
          <ANavigationMenuSub>
            <ANavigationMenuList />
            <ANavigationMenuViewport />
          </ANavigationMenuSub>
        </ANavigationMenuContent>
      </ANavigationMenuItem>

      <ANavigationMenuIndicator />
    </ANavigationMenuList>

    <ANavigationMenuViewport />
  </ANavigationMenuRoot>
</template>

Pohon

One benefit of using Akar is its flexibility and low-level control over the components. However, this also means that you may need to manually construct more complex UI elements by combining multiple Akar components together.

If you feel there's a lot of elements that needs to be constructed manually using Akar, consider using Pohon UI instead. It provides a higher-level abstraction over Akar components with pre-defined styles and behaviors that can help you build UIs faster.

API Reference

Root

Contains all the parts of a navigation menu.

Props

Prop Default Type
as'nav'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

defaultValuestring

The value of the menu item that should be active when initially rendered.

Use when you do not need to control the value state.

delayDuration200number

The duration from when the pointer enters the trigger until the tooltip gets opened.

dir'ltr' | 'rtl'

The reading direction of the combobox when applicable.

If omitted, inherits globally from ConfigProvider or assumes LTR (left-to-right) reading mode.

disableClickTriggerfalseboolean

If true, menu cannot be open by click on trigger

disableHoverTriggerfalseboolean

If true, menu cannot be open by hover on trigger

disablePointerLeaveCloseboolean

If true, menu will not close during pointer leave event

modelValuestring
orientation'horizontal''horizontal' | 'vertical'

The orientation of the menu.

skipDelayDuration300number

How much time a user has to enter another trigger without incurring a delay again.

unmountOnHidetrueboolean

When true, the element will be unmounted on closed state.

Emits

Event Type
update:modelValue[value: string]

Event handler called when the value changes.

Slots

Slot Type
modelValuestring

The controlled value of the menu item to activate. Can be used as v-model.

Data Attributes

Attribute Value

Sub

Signifies a submenu. Use it in place of the root part when nested to create a submenu.

Props

Prop Default Type
as'div'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

defaultValuestring

The value of the menu item that should be active when initially rendered.

Use when you do not need to control the value state.

modelValuestring

The controlled value of the menu item to activate. Can be used as v-model.

orientation'horizontal''horizontal' | 'vertical'

The orientation of the menu.

Emits

Event Type
update:modelValue[value: string]

Event handler called when the value changes.

Slots

Slot Type
modelValuestring

The controlled value of the menu item to activate. Can be used as v-model.

Data Attributes

Attribute Value

List

Contains the top level menu items.

Props

Prop Default Type
as'ul'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Data Attributes

Attribute Value

Item

A top level menu item, contains a link or trigger with content combination.

Props

Prop Default Type
as'li'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

valuestring

A unique value that associates the item with an active value when the navigation menu is controlled.

This prop is managed automatically when uncontrolled.

Trigger

The button that toggles the content.

Props

Prop Default Type
as'button'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

disabledboolean

When true, prevents the user from interacting with item

Data Attributes

Attribute Value

Content

Contains the content associated with each trigger.

Built with Presence component - supports any animation techniques while maintaining access to presence emitted events.

Props

Prop Default Type
as'div'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

disableOutsidePointerEventsboolean

When true, hover/focus/click interactions will be disabled on elements outside the DismissableLayer. Users will need to click twice on outside elements to interact with them: once to close the DismissableLayer, and again to trigger the element.

forceMountboolean

Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries.

Emits

Event Type
escapeKeyDown[event: KeyboardEvent]
focusOutside[event: FocusOutsideEvent]
interactOutside[event: PointerDownOutsideEvent | FocusOutsideEvent]
pointerDownOutside[event: PointerDownOutsideEvent]

Data Attributes

Attribute Value

A navigational link.

Props

Prop Default Type
as'a'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

activeboolean

Used to identify the link as the currently active page.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Emits

Event Type
select[payload: CustomEvent<{ originalEvent: Event; }>]

Event handler called when the user selects a link (via mouse or keyboard).

Calling event.preventDefault in this handler will prevent the navigation menu from closing when selecting that link.

Data Attributes

Attribute Value

Indicator

An optional indicator element that renders below the list, is used to highlight the currently active trigger.

Built with Presence component - supports any animation techniques while maintaining access to presence emitted events.

Props

Prop Default Type
as'div'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

forceMountboolean

Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries.

Data Attributes

Attribute Value

CSS Variables

Variable Description
--akar-navigation-menu-indicator-size

The size of the indicator.

--akar-navigation-menu-indicator-position

The position of the indicator

Viewport

An optional viewport element that is used to render active content outside of the list.

Built with Presence component - supports any animation techniques while maintaining access to presence emitted events.

Props

Prop Default Type
as'div'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

align'center''start' | 'center' | 'end'

Placement of the viewport for css variables (--akar-navigation-menu-viewport-left, --akar-navigation-menu-viewport-top).

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

forceMountboolean

Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries.

Data Attributes

Attribute Value

CSS Variables

Variable Description
--akar-navigation-menu-viewport-width

The width of the viewport when visible/hidden, computed from the active content

--akar-navigation-menu-viewport-height

The height of the viewport when visible/hidden, computed from the active content

Examples

Vertical

You can create a vertical menu by using the orientation prop.

<script setup lang="ts">
import {
  ANavigationMenuContent,
  ANavigationMenuItem,
  ANavigationMenuList,
  ANavigationMenuRoot,
  ANavigationMenuTrigger,
} from 'akar';
</script>

<template>
  <ANavigationMenuRoot orientation="vertical">
    <ANavigationMenuList>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item one</ANavigationMenuTrigger>
        <ANavigationMenuContent>Item one content</ANavigationMenuContent>
      </ANavigationMenuItem>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item two</ANavigationMenuTrigger>
        <ANavigationMenuContent>Item Two content</ANavigationMenuContent>
      </ANavigationMenuItem>
    </ANavigationMenuList>
  </ANavigationMenuRoot>
</template>

Flexible layouts

Use the Viewport part when you need extra control over where Content is rendered. This can be helpful when your design requires an adjusted DOM structure or if you need flexibility to achieve advanced animation. Tab focus will be maintained automatically.

<script setup lang="ts">
import {
  ANavigationMenuContent,
  ANavigationMenuItem,
  ANavigationMenuList,
  ANavigationMenuRoot,
  ANavigationMenuTrigger,
  ANavigationMenuViewport,
} from 'akar';
</script>

<template>
  <ANavigationMenuRoot>
    <ANavigationMenuList>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item one</ANavigationMenuTrigger>
        <ANavigationMenuContent>Item one content</ANavigationMenuContent>
      </ANavigationMenuItem>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item two</ANavigationMenuTrigger>
        <ANavigationMenuContent>Item two content</ANavigationMenuContent>
      </ANavigationMenuItem>
    </ANavigationMenuList>

    <!-- ANavigationMenuContent will be rendered here when active  -->
    <ANavigationMenuViewport />
  </ANavigationMenuRoot>
</template>

With indicator

You can use the optional Indicator part to highlight the currently active Trigger, this is useful when you want to provide an animated visual cue such as an arrow or highlight to accompany the Viewport.

<script setup lang="ts">
import {
  ANavigationMenuContent,
  ANavigationMenuItem,
  ANavigationMenuList,
  ANavigationMenuRoot,
  ANavigationMenuTrigger,
  ANavigationMenuViewport,
} from 'akar';
</script>

<template>
  <ANavigationMenuRoot>
    <ANavigationMenuList>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item one</ANavigationMenuTrigger>
        <ANavigationMenuContent>Item one content</ANavigationMenuContent>
      </ANavigationMenuItem>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item two</ANavigationMenuTrigger>
        <ANavigationMenuContent>Item two content</ANavigationMenuContent>
      </ANavigationMenuItem>

      <ANavigationMenuIndicator class="ANavigationMenuIndicator" />
    </ANavigationMenuList>

    <ANavigationMenuViewport />
  </ANavigationMenuRoot>
</template>
/* styles.css */
.ANavigationMenuIndicator {
  background-color: grey;
  position: absolute;
  transition:
    width,
    transform,
    250ms ease;
}

.ANavigationMenuIndicator[data-orientation='horizontal'] {
  left: 0;
  height: 3px;
  transform: translateX(var(--akar-navigation-menu-indicator-position));
  width: var(--akar-navigation-menu-indicator-size);
}

With submenus

Create a submenu by nesting your ANavigationMenu and using the Sub part in place of its Root. Submenus work differently to Root navigation menus and are similar to Tabs in that one item should always be active, so be sure to assign and set a defaultValue.

<script setup lang="ts">
import {
  ANavigationMenuContent,
  ANavigationMenuItem,
  ANavigationMenuList,
  ANavigationMenuRoot,
  ANavigationMenuSub,
  ANavigationMenuTrigger,
} from 'akar';
</script>

<template>
  <ANavigationMenuRoot>
    <ANavigationMenuList>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item one</ANavigationMenuTrigger>
        <ANavigationMenuContent>Item one content</ANavigationMenuContent>
      </ANavigationMenuItem>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item two</ANavigationMenuTrigger>
        <ANavigationMenuContent>
          <ANavigationMenuSub default-value="sub1">
            <ANavigationMenuList>
              <ANavigationMenuItem value="sub1">
                <ANavigationMenuTrigger>Sub item one</ANavigationMenuTrigger>
                <ANavigationMenuContent> Sub item one content </ANavigationMenuContent>
              </ANavigationMenuItem>
              <ANavigationMenuItem value="sub2">
                <ANavigationMenuTrigger>Sub item two</ANavigationMenuTrigger>
                <ANavigationMenuContent> Sub item two content </ANavigationMenuContent>
              </ANavigationMenuItem>
            </ANavigationMenuList>
          </ANavigationMenuSub>
        </ANavigationMenuContent>
      </ANavigationMenuItem>
    </ANavigationMenuList>
  </ANavigationMenuRoot>
</template>

With client side routing

If you need to use the RouterLink component provided by your routing package then we recommend adding asChild="true" to ANavigationMenuLink, or setting as="RouterLink". This will ensure accessibility and consistent keyboard control is maintained:

<script setup lang="ts">
import { ANavigationMenuItem, ANavigationMenuList, ANavigationMenuRoot } from 'akar';

// RouterLink should be injected by default if using `vue-router`
</script>

<template>
  <ANavigationMenuRoot>
    <ANavigationMenuList>
      <ANavigationMenuItem>
        <ANavigationMenuLink as-child>
          <RouterLink to="/">
            Home
          </RouterLink>
          <ANavigationMenuLink />
        </ANavigationMenuLink>
      </ANavigationMenuItem>
      <ANavigationMenuItem>
        <ANavigationMenuLink
          :as="RouterLink"
          to="/about"
        >
          About
        </ANavigationMenuLink>
      </ANavigationMenuItem>
    </ANavigationMenuList>
  </ANavigationMenuRoot>
</template>

Advanced animation

We expose --akar-navigation-menu-viewport-[width|height] and data-motion['from-start'|'to-start'|'from-end'|'to-end'] attributes to allow you to animate Viewport size and Content position based on the enter/exit direction.

Combining these with position: absolute; allows you to create smooth overlapping animation effects when moving between items.

<script setup lang="ts">
import {
  ANavigationMenuContent,
  ANavigationMenuItem,
  ANavigationMenuList,
  ANavigationMenuRoot,
  ANavigationMenuTrigger,
  ANavigationMenuViewport,
} from 'akar';
</script>

<template>
  <ANavigationMenuRoot>
    <ANavigationMenuList>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item one</ANavigationMenuTrigger>
        <ANavigationMenuContent class="ANavigationMenuContent">
          Item one content
        </ANavigationMenuContent>
      </ANavigationMenuItem>
      <ANavigationMenuItem>
        <ANavigationMenuTrigger>Item two</ANavigationMenuTrigger>
        <ANavigationMenuContent class="ANavigationMenuContent">
          Item two content
        </ANavigationMenuContent>
      </ANavigationMenuItem>
    </ANavigationMenuList>

    <ANavigationMenuViewport class="ANavigationMenuViewport" />
  </ANavigationMenuRoot>
</template>
/* styles.css */
.ANavigationMenuContent {
  position: absolute;
  top: 0;
  left: 0;
  animation-duration: 250ms;
  animation-timing-function: ease;
}
.ANavigationMenuContent[data-motion='from-start'] {
  animation-name: enterFromLeft;
}
.ANavigationMenuContent[data-motion='from-end'] {
  animation-name: enterFromRight;
}
.ANavigationMenuContent[data-motion='to-start'] {
  animation-name: exitToLeft;
}
.ANavigationMenuContent[data-motion='to-end'] {
  animation-name: exitToRight;
}

.ANavigationMenuViewport {
  position: relative;
  width: var(--akar-navigation-menu-viewport-width);
  height: var(--akar-navigation-menu-viewport-height);
  transition:
    width,
    height,
    250ms ease;
}

@keyframes enterFromRight {
  from {
    opacity: 0;
    transform: translateX(200px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes enterFromLeft {
  from {
    opacity: 0;
    transform: translateX(-200px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes exitToRight {
  from {
    opacity: 1;
    transform: translateX(0);
  }
  to {
    opacity: 0;
    transform: translateX(200px);
  }
}

@keyframes exitToLeft {
  from {
    opacity: 1;
    transform: translateX(0);
  }
  to {
    opacity: 0;
    transform: translateX(-200px);
  }
}

Accessibility

Adheres to the navigation role requirements.

Differences to menubar

ANavigationMenu should not be confused with menubar, although this primitive shares the name menu in the colloquial sense to refer to a set of navigation links, it does not use the WAI-ARIA menu role. This is because menu and menubars behave like native operating system menus most commonly found in desktop application windows, as such they feature complex functionality like composite focus management and first-character navigation.

These features are often considered unnecessary for website navigation and at worst can confuse users who are familiar with established website patterns.

See the W3C Disclosure Navigation Menu example for more information.

It's important to use ANavigationMenuLink for all navigational links within a menu, this not only applies to the main list but also within any content rendered via ANavigationMenuContent. This will ensure consistent keyboard interactions and accessibility while also giving access to the active prop for setting aria-current and the active styles. See this example for more information on usage with third party routing components.

Keyboard Interactions

Key Description
SpaceEnter

When focus is on ANavigationMenuTrigger, opens the content.

Tab

Moves focus to the next focusable element.

ArrowDown

When horizontal and focus is on an open ANavigationMenuTrigger, moves focus into ANavigationMenuContent.
Moves focus to the next ANavigationMenuTrigger or ANavigationMenuLink.

ArrowUp

Moves focus to the previous ANavigationMenuTrigger or ANavigationMenuLink.

ArrowRightArrowLeft

When vertical and focus is on an open ANavigationMenuTrigger, moves focus into its ANavigationMenuContent.
Moves focus to the next / previous ANavigationMenuTrigger or ANavigationMenuLink.

HomeEnd

Moves focus to the first/last ANavigationMenu.Trigger or ANavigationMenu.Link.

Esc

Closes open ANavigationMenu.Content and moves focus to itsANavigationMenu.Trigger.