Noctis

Menu

A dropdown menu: a trigger opens a floating list of actions — with groups, checkbox and radio items, nested submenus, and shortcut hints. Decoupled from any particular trigger — pass your own, commonly a Button rendered through the trigger.

Basic

A Menu.Trigger opens the Menu.Content. Compose the trigger from a Button via render so it inherits the button's look, hover, and focus; the content holds the Items.

With icons

Give items a leading icon, and divide groups with a Separator. Items support disabled like any Base UI menu item, and the menu is fully keyboard-operable — arrow keys, Home/End, and typeahead.

Groups

Wrap related items in a Menu.Group with a Menu.GroupLabel — the label is muted, non-interactive, and announced as the group's name. A destructive action takes tone="danger": a red label whose highlight tints toward danger instead of the neutral fill.

Checkbox & radio items

Menu.CheckboxItem toggles a setting; Menu.RadioGroup with Menu.RadioItems selects exactly one value. Both keep the menu open when activated, so several settings can change in one visit, and both reserve the leading column for their indicator — labels stay aligned with iconed items. Use inset on plain Items sharing a menu with them.

Nest a Menu.SubmenuRoot wrapping a Menu.SubmenuTrigger and its own Menu.Content. The submenu opens beside its trigger on the inline-end side, flipping to inline-start at the viewport edge, and the trailing chevron mirrors under RTL. It opens on hover after a short intent delay — with a grace period on leave, so diagonal pointer travel into the submenu doesn't snap it shut — and from the keyboard with Right arrow or Enter. Trailing secondary text (here, the resolved times) flows after the label.

Shortcuts

Menu.Shortcut renders a trailing keyboard hint through the Kbd primitive — pass keys in Kbd syntax (Mod resolves to ⌘ or Ctrl by platform). The hint is decorative: it is hidden from assistive tech so the item's accessible name stays clean. When the binding is actually wired up, set aria-keyshortcuts on the item to expose it to screen readers — with the platform's real modifier (Meta+C on macOS, Control+C elsewhere), since the attribute takes literal keys, not the Mod abstraction.

Positioning

The menu opens below the trigger, aligned to its start, by default. Steer it with side and align (and sideOffset / alignOffset) on Menu.Content — alignment is direction-aware, so start/end flip under RTL. collisionPadding (default 8) keeps the menu off the viewport edges, and a long menu caps at the available height and scrolls its list. Noctis menus sit flush against their anchor — there is no arrow/caret part (Base UI offers one; this system doesn't use it).

Keyboard

KeyAction
Enter / Space / On the trigger: open the menu and highlight the first item ( opens to the last).
/ Move the highlight to the next / previous item.
Home / EndFirst / last item.
Open the highlighted submenu and move into it ( under RTL).
Close the current submenu and return to its trigger ( under RTL).
Enter / SpaceActivate the highlighted item — run an Item, toggle a CheckboxItem, select a RadioItem, open a submenu.
EscClose the current menu level; on the root, return focus to the trigger.
TabClose the whole menu and move focus on.
CharactersTypeahead — jump to the item whose label matches what you type.

Disabled items stay reachable by the keyboard and are announced as disabled — they just can't be activated. That follows the ARIA menu pattern: removing them from navigation would hide why an action is unavailable from screen-reader users. Right-click context menus are out of scope for this primitive.

Anatomy

Compose a menu from its parts. Menu.Root owns the open state (it accepts every Base UI Menu.Root prop — open, defaultOpen, onOpenChange, modal).

  • Menu.Root — owns the open state; renders no element of its own. Modal by default (page scroll locked, outside content inert); pass modal={false} for a lighter popup.
  • Menu.Trigger — opens the menu. Style it directly or compose a Button through render.
  • Menu.Content — the floating, elevated, animated surface holding the items. Props: side (default bottom; inline-end inside a submenu), align (default start), sideOffset, alignOffset, collisionPadding.
  • Menu.Group + Menu.GroupLabel — a labelled section of related items.
  • Menu.Item — a menu action with an optional leading icon, an inset alignment helper, and a tone (default | danger).
  • Menu.LinkItem — a navigable row rendering an <a> (href, target, rel), with the same icon / inset / tone recipe as Item; closes the menu on click by default.
  • Menu.CheckboxItem — a toggling item with an animated check indicator.
  • Menu.RadioGroup + Menu.RadioItem — a single-select set with a dot indicator.
  • Menu.SubmenuRoot + Menu.SubmenuTrigger — a nested submenu; the trigger is an item-shaped row with a trailing, RTL-mirrored chevron.
  • Menu.Shortcut — a trailing, decorative keyboard hint composing Kbd.
  • Menu.Separator — a hairline between groups of items.

Every rendered part carries a data-slot (menu-trigger, menu-content, menu-viewport, menu-group, menu-group-label, menu-item, menu-link-item, menu-checkbox-item, menu-radio-group, menu-radio-item, menu-item-indicator, menu-submenu-trigger, menu-shortcut, menu-separator) for host-side styling — pair it with the Base UI state attributes (data-popup-open, data-highlighted, data-disabled, data-checked).

Design tokens

Generated from the component's declaration — the same graph that mints the CSS, so a variable name or its resolution default can't drift. The minted tokens are the public override seam: set one on any ancestor and every menu in that region retunes — e.g. .marketing { --noctis-menu-content-min-width: 18rem; } widens every popup opened beneath it. Knobs that aren't minted are reached through the part's data-slot. See Customization for the full override ladder and Tokens for the whole graph.

Token

API reference

Generated from the component's types — every prop, type, default, and description comes straight from the source. Each part gets its own table; parts that only forward to Base UI list just the props they pass through.

Menu.Root

No props of its own — forwards to the underlying Base UI part.

Menu.Trigger

Prop

Menu.Content

Prop

Menu.Group

Prop

Menu.GroupLabel

Prop

Menu.Item

Prop

Menu.LinkItem

Prop

Menu.CheckboxItem

Prop

Menu.RadioGroup

Prop

Menu.RadioItem

Prop

Menu.SubmenuRoot

No props of its own — forwards to the underlying Base UI part.

Menu.SubmenuTrigger

Prop

Menu.Shortcut

Prop

Menu.Separator

Prop

AttributeDescription
data-slotThe trigger element.
data-popup-openPresent on the trigger — and on a submenu trigger — while its menu is open.
data-variantThe item's visual variant, mirroring its `tone` prop (`default`/`danger`); the component layer keys the row's token defaults off it.
data-highlightedPresent on the menu item the pointer or keyboard is currently over.
data-disabledPresent on a disabled menu item.
data-checkedPresent on a ticked checkbox item or the selected radio item (and its indicator).
data-uncheckedPresent on an unticked checkbox item or an unselected radio item (and its indicator).
data-sideThe side of the anchor the content actually rendered on (`bottom`, `inline-end`, …).
data-starting-stylePresent on the content for the first frame after mount — the transition's start state.
data-ending-stylePresent on the content while it transitions out before unmounting.
data-instantPresent on the content when the change should not animate (keyboard open, dismissal).