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

indicators-submenus.mddocs/

Visual Indicators and Sub-menus

Indicator component for visual feedback and sub-menu support for nested navigation structures.

Capabilities

NavigationMenuIndicator

Visual indicator component that shows the active menu item with automatic positioning and animation support.

/**
 * Visual indicator that highlights the active menu item
 * Automatically positions itself relative to the active trigger
 * @param forceMount - Force mounting for animation control
 */
const NavigationMenuIndicator: React.ForwardRefExoticComponent<
  NavigationMenuIndicatorProps & React.RefAttributes<HTMLDivElement>
>;

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

Key Features:

  • Automatic positioning based on active trigger
  • Smooth transitions between different menu items
  • Portal rendering to indicator track
  • Orientation support (horizontal/vertical)
  • Animation support via forceMount
  • Resize observer integration for responsive positioning

Usage Examples:

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

// Basic indicator usage
function IndicatorExample() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Home</NavigationMenuTrigger>
          <NavigationMenuContent>Home content</NavigationMenuContent>
        </NavigationMenuItem>
        
        <NavigationMenuItem>
          <NavigationMenuTrigger>Products</NavigationMenuTrigger>
          <NavigationMenuContent>Products content</NavigationMenuContent>
        </NavigationMenuItem>
        
        <NavigationMenuItem>
          <NavigationMenuTrigger>Services</NavigationMenuTrigger>
          <NavigationMenuContent>Services content</NavigationMenuContent>
        </NavigationMenuItem>
        
        {/* Indicator automatically positions over active trigger */}
        <NavigationMenuIndicator className="nav-indicator" />
      </NavigationMenuList>
    </NavigationMenu>
  );
}

// Styled indicator with animations
function StyledIndicatorExample() {
  return (
    <NavigationMenu>
      <NavigationMenuList className="nav-list">
        <NavigationMenuItem>
          <NavigationMenuTrigger>Tab 1</NavigationMenuTrigger>
          <NavigationMenuContent>Content 1</NavigationMenuContent>
        </NavigationMenuItem>
        
        <NavigationMenuItem>
          <NavigationMenuTrigger>Tab 2</NavigationMenuTrigger>
          <NavigationMenuContent>Content 2</NavigationMenuContent>
        </NavigationMenuItem>
        
        <NavigationMenuIndicator 
          className="indicator"
          style={{
            bottom: 0,
            height: "2px",
            backgroundColor: "blue",
            transition: "transform 200ms ease",
          }}
        />
      </NavigationMenuList>
    </NavigationMenu>
  );
}

// Force mounted indicator for custom animations
function AnimatedIndicatorExample() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Item 1</NavigationMenuTrigger>
          <NavigationMenuContent>Content 1</NavigationMenuContent>
        </NavigationMenuItem>
        
        <NavigationMenuItem>
          <NavigationMenuTrigger>Item 2</NavigationMenuTrigger>
          <NavigationMenuContent>Content 2</NavigationMenuContent>
        </NavigationMenuItem>
        
        <NavigationMenuIndicator 
          forceMount
          className="custom-animated-indicator"
        />
      </NavigationMenuList>
    </NavigationMenu>
  );
}

Vertical Orientation Indicator

function VerticalIndicatorExample() {
  return (
    <NavigationMenu orientation="vertical">
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Vertical Item 1</NavigationMenuTrigger>
          <NavigationMenuContent>Content 1</NavigationMenuContent>
        </NavigationMenuItem>
        
        <NavigationMenuItem>
          <NavigationMenuTrigger>Vertical Item 2</NavigationMenuTrigger>
          <NavigationMenuContent>Content 2</NavigationMenuContent>
        </NavigationMenuItem>
        
        {/* Indicator adapts to vertical orientation */}
        <NavigationMenuIndicator 
          style={{
            left: 0,
            width: "3px",
            backgroundColor: "green",
            transition: "transform 200ms ease",
          }}
        />
      </NavigationMenuList>
    </NavigationMenu>
  );
}

Sub-menu Architecture

Sub-menus inherit from the same core architecture but operate within a nested context:

Basic Sub-menu Structure

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

function SubMenuExample() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Main Category</NavigationMenuTrigger>
          <NavigationMenuContent>
            {/* Sub-menu within content */}
            <NavigationMenuSub>
              <NavigationMenuList>
                <NavigationMenuItem>
                  <NavigationMenuTrigger>Subcategory 1</NavigationMenuTrigger>
                  <NavigationMenuContent>
                    Sub-content 1
                  </NavigationMenuContent>
                </NavigationMenuItem>
                
                <NavigationMenuItem>
                  <NavigationMenuTrigger>Subcategory 2</NavigationMenuTrigger>
                  <NavigationMenuContent>
                    Sub-content 2
                  </NavigationMenuContent>
                </NavigationMenuItem>
              </NavigationMenuList>
            </NavigationMenuSub>
          </NavigationMenuContent>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  );
}

Controlled Sub-menu

function ControlledSubMenuExample() {
  const [subValue, setSubValue] = React.useState("");
  
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Products</NavigationMenuTrigger>
          <NavigationMenuContent>
            <NavigationMenuSub
              value={subValue}
              onValueChange={setSubValue}
              orientation="vertical"
            >
              <NavigationMenuList>
                <NavigationMenuItem value="web">
                  <NavigationMenuTrigger>Web Products</NavigationMenuTrigger>
                  <NavigationMenuContent>
                    Web product details
                  </NavigationMenuContent>
                </NavigationMenuItem>
                
                <NavigationMenuItem value="mobile">
                  <NavigationMenuTrigger>Mobile Products</NavigationMenuTrigger>
                  <NavigationMenuContent>
                    Mobile product details
                  </NavigationMenuContent>
                </NavigationMenuItem>
              </NavigationMenuList>
            </NavigationMenuSub>
          </NavigationMenuContent>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  );
}

Complex Nested Structure

function ComplexNestedExample() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Solutions</NavigationMenuTrigger>
          <NavigationMenuContent>
            <div className="mega-menu">
              <div className="menu-column">
                <h3>By Industry</h3>
                <NavigationMenuSub>
                  <NavigationMenuList>
                    <NavigationMenuItem>
                      <NavigationMenuTrigger>Healthcare</NavigationMenuTrigger>
                      <NavigationMenuContent>
                        <NavigationMenuLink href="/healthcare/hospitals">Hospitals</NavigationMenuLink>
                        <NavigationMenuLink href="/healthcare/clinics">Clinics</NavigationMenuLink>
                      </NavigationMenuContent>
                    </NavigationMenuItem>
                    
                    <NavigationMenuItem>
                      <NavigationMenuTrigger>Finance</NavigationMenuTrigger>
                      <NavigationMenuContent>
                        <NavigationMenuLink href="/finance/banking">Banking</NavigationMenuLink>
                        <NavigationMenuLink href="/finance/insurance">Insurance</NavigationMenuLink>
                      </NavigationMenuContent>
                    </NavigationMenuItem>
                  </NavigationMenuList>
                </NavigationMenuSub>
              </div>
              
              <div className="menu-column">
                <h3>By Size</h3>
                <NavigationMenuSub>
                  <NavigationMenuList>
                    <NavigationMenuItem>
                      <NavigationMenuLink href="/enterprise">Enterprise</NavigationMenuLink>
                    </NavigationMenuItem>
                    
                    <NavigationMenuItem>
                      <NavigationMenuLink href="/small-business">Small Business</NavigationMenuLink>
                    </NavigationMenuItem>
                  </NavigationMenuList>
                </NavigationMenuSub>
              </div>
            </div>
          </NavigationMenuContent>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  );
}

Indicator Positioning

The indicator automatically handles positioning based on the active trigger:

Horizontal Layout

  • Uses transform: translateX() for positioning
  • Width matches the active trigger width
  • Positioned at bottom by default

Vertical Layout

  • Uses transform: translateY() for positioning
  • Height matches the active trigger height
  • Positioned at left by default

CSS Example

/* Horizontal indicator */
.nav-indicator[data-orientation="horizontal"] {
  bottom: 0;
  height: 2px;
  background: var(--accent-color);
  transition: transform 200ms ease;
}

/* Vertical indicator */
.nav-indicator[data-orientation="vertical"] {
  left: 0;
  width: 3px;
  background: var(--accent-color);
  transition: transform 200ms ease;
}

/* Indicator states */
.nav-indicator[data-state="visible"] {
  opacity: 1;
}

.nav-indicator[data-state="hidden"] {
  opacity: 0;
}

Data Attributes

Both indicators and sub-menus expose data attributes for styling:

NavigationMenuIndicator Data Attributes

[data-state="visible"] { /* Indicator is shown */ }
[data-state="hidden"] { /* Indicator is hidden */ }
[data-orientation="horizontal"] { /* Horizontal layout */ }
[data-orientation="vertical"] { /* Vertical layout */ }

Advanced Indicator Styling:

.nav-indicator {
  position: absolute;
  transition: all 200ms ease;
}

.nav-indicator[data-state="visible"] {
  opacity: 1;
  transform: scale(1);
}

.nav-indicator[data-state="hidden"] {
  opacity: 0;
  transform: scale(0.8);
}

.nav-indicator[data-orientation="horizontal"] {
  width: 100%;
  height: 2px;
  bottom: 0;
  left: 0;
}

.nav-indicator[data-orientation="vertical"] {
  width: 3px;
  height: 100%;
  left: 0;
  top: 0;
}

NavigationMenuSub Data Attributes

[data-orientation="horizontal"] { /* Sub-menu horizontal layout */ }
[data-orientation="vertical"] { /* Sub-menu vertical layout */ }

Sub-menu Styling:

.nav-sub[data-orientation="horizontal"] {
  display: flex;
  flex-direction: row;
}

.nav-sub[data-orientation="vertical"] {
  display: flex;
  flex-direction: column;
}

These attributes enable CSS-based styling and animations without JavaScript.

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