CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-radix-ui--react-navigation-menu

A collection of React components for building accessible, customizable navigation menus with keyboard support and flexible layout options

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

content-management.mddocs/

Content Management

Content and viewport components for displaying menu content with animation support and focus management.

Capabilities

NavigationMenuContent

Dismissable content panel that displays when a navigation menu item is triggered, with built-in focus management and animation support.

/**
 * Content panel for navigation menu items with dismissable layer integration
 * @param forceMount - Force mounting for animation control (useful with animation libraries)
 */
const NavigationMenuContent: React.ForwardRefExoticComponent<
  NavigationMenuContentProps & React.RefAttributes<HTMLDivElement>
>;

interface NavigationMenuContentProps 
  extends React.ComponentPropsWithoutRef<"div"> {
  forceMount?: true;
}

Key Features:

  • Dismissable layer integration (clicks outside, Escape key)
  • Focus management and tab order handling
  • Animation support via forceMount prop
  • Portal rendering (when used with viewport)
  • Keyboard navigation within content
  • Pointer interaction handling

Usage Examples:

import { 
  NavigationMenu,
  NavigationMenuList,
  NavigationMenuItem,
  NavigationMenuTrigger,
  NavigationMenuContent,
  NavigationMenuLink
} from "@radix-ui/react-navigation-menu";

// Basic content usage
function BasicContentExample() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Products</NavigationMenuTrigger>
          <NavigationMenuContent className="nav-content">
            <div className="content-grid">
              <NavigationMenuLink href="/web">Web Development</NavigationMenuLink>
              <NavigationMenuLink href="/mobile">Mobile Apps</NavigationMenuLink>
              <NavigationMenuLink href="/desktop">Desktop Software</NavigationMenuLink>
            </div>
          </NavigationMenuContent>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  );
}

// Content with force mount for animations
function AnimatedContentExample() {
  return (
    <NavigationMenuItem>
      <NavigationMenuTrigger>Services</NavigationMenuTrigger>
      <NavigationMenuContent 
        forceMount
        className="animated-content"
        onPointerEnter={() => console.log("Content entered")}
        onPointerLeave={() => console.log("Content left")}
      >
        <div className="service-list">
          <h3>Our Services</h3>
          <NavigationMenuLink href="/consulting">Consulting</NavigationMenuLink>
          <NavigationMenuLink href="/support">Support</NavigationMenuLink>
          <NavigationMenuLink href="/training">Training</NavigationMenuLink>
        </div>
      </NavigationMenuContent>
    </NavigationMenuItem>
  );
}

// Rich content with complex layout
function RichContentExample() {
  return (
    <NavigationMenuItem>
      <NavigationMenuTrigger>Resources</NavigationMenuTrigger>
      <NavigationMenuContent>
        <div className="resource-content">
          <div className="resource-section">
            <h4>Documentation</h4>
            <NavigationMenuLink href="/docs/getting-started">Getting Started</NavigationMenuLink>
            <NavigationMenuLink href="/docs/api">API Reference</NavigationMenuLink>
            <NavigationMenuLink href="/docs/examples">Examples</NavigationMenuLink>
          </div>
          
          <div className="resource-section">
            <h4>Community</h4>
            <NavigationMenuLink href="/forum">Forum</NavigationMenuLink>
            <NavigationMenuLink href="/discord">Discord</NavigationMenuLink>
            <NavigationMenuLink href="/github">GitHub</NavigationMenuLink>
          </div>
          
          <div className="resource-section">
            <h4>Latest</h4>
            <p>Check out our latest blog post about navigation patterns.</p>
            <NavigationMenuLink href="/blog/latest">Read More</NavigationMenuLink>
          </div>
        </div>
      </NavigationMenuContent>
    </NavigationMenuItem>
  );
}

NavigationMenuViewport

Centralized viewport container that manages and displays menu content with dynamic sizing and positioning.

/**
 * Centralized viewport for menu content with dynamic sizing
 * Provides a container that automatically sizes to match active content
 * @param forceMount - Force mounting for animation control
 */
const NavigationMenuViewport: React.ForwardRefExoticComponent<
  NavigationMenuViewportProps & React.RefAttributes<HTMLDivElement>
>;

interface NavigationMenuViewportProps 
  extends React.ComponentPropsWithoutRef<"div"> {
  forceMount?: true;
}

Key Features:

  • Dynamic sizing based on active content
  • CSS custom properties for dimensions
  • Portal rendering for content
  • Animation support with presence detection
  • Pointer event handling
  • Content lifecycle management

Usage Examples:

import { 
  NavigationMenu,
  NavigationMenuList,
  NavigationMenuItem,
  NavigationMenuTrigger,
  NavigationMenuContent,
  NavigationMenuViewport
} from "@radix-ui/react-navigation-menu";

// Basic viewport setup
function ViewportExample() {
  return (
    <div className="nav-container">
      <NavigationMenu>
        <NavigationMenuList>
          <NavigationMenuItem>
            <NavigationMenuTrigger>Products</NavigationMenuTrigger>
            <NavigationMenuContent>
              <div className="products-content">
                {/* Content will be rendered in viewport */}
                Product information here
              </div>
            </NavigationMenuContent>
          </NavigationMenuItem>
          
          <NavigationMenuItem>
            <NavigationMenuTrigger>Services</NavigationMenuTrigger>
            <NavigationMenuContent>
              <div className="services-content">
                {/* Different sized content */}
                Much larger service information content here
                with multiple sections and detailed descriptions
              </div>
            </NavigationMenuContent>
          </NavigationMenuItem>
        </NavigationMenuList>
        
        {/* Viewport renders all content and manages transitions */}
        <NavigationMenuViewport className="nav-viewport" />
      </NavigationMenu>
    </div>
  );
}

// Viewport with custom styling using CSS custom properties
function StyledViewportExample() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Large Content</NavigationMenuTrigger>
          <NavigationMenuContent>
            <div style={{ width: "400px", height: "300px" }}>
              Large content area
            </div>
          </NavigationMenuContent>
        </NavigationMenuItem>
      </NavigationMenuList>
      
      <NavigationMenuViewport 
        className="custom-viewport"
        style={{
          // CSS custom properties are automatically set:
          // --radix-navigation-menu-viewport-width
          // --radix-navigation-menu-viewport-height
          width: "var(--radix-navigation-menu-viewport-width)",
          height: "var(--radix-navigation-menu-viewport-height)",
          transition: "width 200ms, height 200ms",
        }}
      />
    </NavigationMenu>
  );
}

// Viewport with animation control
function AnimatedViewportExample() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Animated Content</NavigationMenuTrigger>
          <NavigationMenuContent forceMount>
            Animated content here
          </NavigationMenuContent>
        </NavigationMenuItem>
      </NavigationMenuList>
      
      <NavigationMenuViewport 
        forceMount
        onPointerEnter={() => console.log("Viewport entered")}
        onPointerLeave={() => console.log("Viewport left")}
      />
    </NavigationMenu>
  );
}

Content vs Viewport Mode

The navigation menu can operate in two content display modes:

Direct Content Mode (Default)

Content is rendered directly adjacent to triggers:

<NavigationMenuItem>
  <NavigationMenuTrigger>Trigger</NavigationMenuTrigger>
  <NavigationMenuContent>
    {/* Rendered directly in DOM tree */}
  </NavigationMenuContent>
</NavigationMenuItem>

Viewport Mode

Content is rendered centrally in a viewport:

<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Trigger</NavigationMenuTrigger>
      <NavigationMenuContent>
        {/* Rendered via portal into viewport */}
      </NavigationMenuContent>
    </NavigationMenuItem>
  </NavigationMenuList>
  
  <NavigationMenuViewport />
</NavigationMenu>

Benefits of Viewport Mode:

  • Consistent content positioning
  • Smooth size transitions between different content
  • Better animation control
  • Simplified layout management

Event Handling

Content Events

// Pointer events for hover behavior
onPointerEnter?: (event: React.PointerEvent) => void;
onPointerLeave?: (event: React.PointerEvent) => void;

// Focus events for accessibility
onFocusOutside?: (event: Event) => void;
onPointerDownOutside?: (event: Event) => void;

// Keyboard events
onKeyDown?: (event: React.KeyboardEvent) => void;
onEscapeKeyDown?: (event: KeyboardEvent) => void;

Viewport Events

// Pointer events
onPointerEnter?: (event: React.PointerEvent) => void;
onPointerLeave?: (event: React.PointerEvent) => void;

CSS Custom Properties

When using viewport mode, these CSS custom properties are automatically available:

.nav-viewport {
  width: var(--radix-navigation-menu-viewport-width);
  height: var(--radix-navigation-menu-viewport-height);
  transition: width 200ms, height 200ms;
}

Data Attributes

Content components expose data attributes for styling and animation:

NavigationMenuContent Data Attributes

[data-state="open"] { /* Content is visible */ }
[data-state="closed"] { /* Content is hidden */ }
[data-motion="from-start"] { /* Animating in from start */ }
[data-motion="from-end"] { /* Animating in from end */ }
[data-motion="to-start"] { /* Animating out to start */ }
[data-motion="to-end"] { /* Animating out to end */ }
[data-orientation="horizontal"|"vertical"] { /* Content orientation */ }

Animation Examples:

.nav-content[data-state="open"] {
  animation: slideIn 200ms ease-out;
}

.nav-content[data-state="closed"] {
  animation: slideOut 200ms ease-in;
}

.nav-content[data-motion="from-start"] {
  animation: slideFromStart 200ms ease-out;
}

.nav-content[data-motion="from-end"] {
  animation: slideFromEnd 200ms ease-out;
}

@keyframes slideIn {
  from { opacity: 0; transform: translateY(-10px); }
  to { opacity: 1; transform: translateY(0); }
}

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

NavigationMenuViewport Data Attributes

[data-state="open"] { /* Viewport contains content */ }
[data-state="closed"] { /* Viewport is empty */ }
[data-orientation="horizontal"|"vertical"] { /* Viewport orientation */ }

Viewport Styling:

.nav-viewport[data-state="open"] {
  pointer-events: auto;
  opacity: 1;
}

.nav-viewport[data-state="closed"] {
  pointer-events: none;
  opacity: 0;
}

.nav-viewport[data-orientation="horizontal"] {
  /* Styles for horizontal viewport */
}

.nav-viewport[data-orientation="vertical"] {
  /* Styles for vertical viewport */
}

Install with Tessl CLI

npx tessl i tessl/npm-radix-ui--react-navigation-menu

docs

content-management.md

core-components.md

index.md

indicators-submenus.md

interactive-elements.md

utility-functions.md

tile.json