easeul

Base UITransitions

Navigation menu

Base UI does not reuse the data-motion name, so unlike Radix there is no collision to guard against.

Every value the manifest allows on this host, single presets and the pairs that compose. The first is what this component ships with. Both stages are the same component, so anything that changes between them is the attribute.

withoutno attribute
with easefuldata-motion="fade"
host writes

data-open / data-closed, plus data-ending-style

easeful matches

[data-motion~="fade"][data-ending-style]

what runs

transition: opacity 150ms

you write

<NavigationMenu.Content data-motion="fade" />

Where the attribute goes

The parts this demo composes, and the value each one carries. Everything inside an animated part is that part's content, so the tree stops there.

  1. NavigationMenu.Root
  2. NavigationMenu.List
  3. NavigationMenu.Item
  4. NavigationMenu.Trigger
  5. NavigationMenu.Content
  6. NavigationMenu.Link
  7. NavigationMenu.Portal
  8. NavigationMenu.Positioner
  9. NavigationMenu.Popupdata-motion="fade"

Source

"use client"

import { NavigationMenu } from "@base-ui/react/navigation-menu"
import type { DemoProps } from "@/components/demos/props"

/* Base UI splits the navigation menu differently from Radix: the shared popup
 * is positioned outside the list, and the Viewport inside it is what the active
 * Content renders into. The animated element is therefore the Popup, not the
 * Content. */
export function BaseUiNavigationMenu({ motion }: DemoProps) {
  return (
    <NavigationMenu.Root className="navmenu">
      <NavigationMenu.List className="navmenu__list">
        <NavigationMenu.Item>
          <NavigationMenu.Trigger className="tab">Products</NavigationMenu.Trigger>
          <NavigationMenu.Content className="navmenu__content">
            <NavigationMenu.Link className="navmenu__link" href="#products">
              Motion presets
            </NavigationMenu.Link>
            <NavigationMenu.Link className="navmenu__link" href="#products">
              Host adapters
            </NavigationMenu.Link>
          </NavigationMenu.Content>
        </NavigationMenu.Item>
        <NavigationMenu.Item>
          <NavigationMenu.Trigger className="tab">Company</NavigationMenu.Trigger>
          <NavigationMenu.Content className="navmenu__content">
            <NavigationMenu.Link className="navmenu__link" href="#company">
              About
            </NavigationMenu.Link>
            <NavigationMenu.Link className="navmenu__link" href="#company">
              Changelog
            </NavigationMenu.Link>
          </NavigationMenu.Content>
        </NavigationMenu.Item>
      </NavigationMenu.List>
      <NavigationMenu.Portal>
        <NavigationMenu.Positioner sideOffset={8} collisionPadding={16}>
          <NavigationMenu.Popup className="navmenu__popup" data-motion={motion}>
            <NavigationMenu.Viewport />
          </NavigationMenu.Popup>
        </NavigationMenu.Positioner>
      </NavigationMenu.Portal>
    </NavigationMenu.Root>
  )
}
The whole file, read from disk at build time. Both copies above render from it, and the motion prop is the only difference between them.