Skip to content
Yuzur1ha Open

Component referenceVersion Draft

Menu

Menu uses native navigation links and action buttons rather than emulating an application menu.

Design notes

Active appearance can be subtle or solid. ResponsiveMenu reuses the same content across inline, Popover and Drawer layouts and closes after selection by default.

MenuDisclosure and MenuPopover have separate props contracts. contentClassName belongs to the disclosure; triggerAppearance and onCloseAutoFocus belong to the popover. MenuItem closes on ordinary link activation even when app routing prevents default. MenuAction stays open when its click handler prevents default.

Playground

Playgrounddemo.tsx
Menu layout
Active style

Overview

content/components/menu/demo.tsx

import { useState } from "react"
import {
  Menu,
  MenuGroup,
  MenuDisclosure,
  MenuPopover,
  MenuItem,
  MenuAction,
  ResponsiveMenu,
  Segmented,
  Button,
} from "@yuzur1ha/ui"
import { DemoResult } from "../../examples/result"
export function MenuDemo() {
  const [activeAppearance, setActiveAppearance] = useState<
    "subtle" | "solid" | "emphasis"
  >("subtle")
  const [active, setActive] = useState("Overview")
  const [layout, setLayout] = useState<
    "responsive" | "inline" | "popover" | "drawer"
  >("responsive")
  return (
    <>
      <Segmented
        label="Menu layout"
        value={layout}
        onChange={setLayout}
        options={[
          { value: "responsive", label: "Auto" },
          { value: "inline", label: "Inline" },
          { value: "popover", label: "Popover" },
          { value: "drawer", label: "Drawer" },
        ]}
      />
      <Segmented
        label="Active style"
        value={activeAppearance}
        onChange={setActiveAppearance}
        options={[
          { value: "subtle", label: "Subtle" },
          { value: "solid", label: "Solid" },
          { value: "emphasis", label: "Emphasis" },
        ]}
      />
      <ResponsiveMenu
        label="Workspace navigation"
        triggerLabel="Browse workspace"
        closeLabel="Close navigation"
        layout={layout}
      >
        <Menu label="Workspace" activeAppearance={activeAppearance}>
          <Button onClick={() => setActive("New workspace")}>
            New workspace
          </Button>
          <MenuGroup label="Workspace">
            {["Overview", "Activity", "Members"].map((label, index) => (
              <MenuItem
                key={label}
                href={`#${label.toLowerCase()}`}
                active={active === label}
                trailing={String(index + 1).padStart(2, "0")}
                onClick={(event) => {
                  event.preventDefault()
                  setActive(label)
                }}
              >
                {label}
              </MenuItem>
            ))}
            <MenuItem href="#billing" disabled>
              Billing
            </MenuItem>
          </MenuGroup>
          <MenuGroup label="Preferences">
            <MenuAction data-menu-dismiss onClick={() => setActive("Settings")}>
              Settings
            </MenuAction>
          </MenuGroup>
        </Menu>
      </ResponsiveMenu>
      <MenuDisclosure label="Browse guides">
        <Menu label="Guides" appearance="subtle" size="small">
          <MenuItem
            href="#introduction"
            active
            trailing="03"
            dismissOnSelect={false}
            onClick={(event) => {
              event.preventDefault()
              setActive("Introduction · keep browsing")
            }}
          >
            Introduction
          </MenuItem>
          <MenuItem href="#installation">Installation</MenuItem>
        </Menu>
      </MenuDisclosure>
      <MenuPopover label="On this page">
        <Menu label="Page sections" appearance="subtle" size="small">
          <MenuAction onClick={() => setActive("Introduction")}>
            Introduction
          </MenuAction>
          <MenuAction onClick={() => setActive("Installation")}>
            Installation
          </MenuAction>
        </Menu>
      </MenuPopover>
      <DemoResult>{active}</DemoResult>
    </>
  )
}

API

MenuDisclosure

An inline disclosure trigger and scrollable menu panel. Escape closes the panel and restores trigger focus; item selection closes it by default.

MenuDisclosureProps
PropertyTypeRequiredDefaultDescription
labelstringYes—Text and accessible name of the built-in disclosure trigger.
openbooleanNo—Controlled open state. Pair with onOpenChange.
defaultOpenbooleanNofalseInitial open state when open is omitted.
onOpenChange((open: boolean) => void)No—Called when opening or closing is requested.
sizeComponentSizeNo"small"Size of the built-in trigger.
childrenReactNodeYes—Content rendered inside the component.
contentClassNamestringNo""Classes on the expanded content wrapper.
classNamestringNo""Additional classes applied to the component’s documented root or control.
scrollContentClassNamestringNo""Class for the scrollable content wrapper inside the viewport.

No custom imperative methods. Event callbacks are listed as properties.

MenuPopover

A popover menu with a built-in button trigger. Item selection closes it by default; onCloseAutoFocus can preserve navigation-driven focus.

MenuPopoverProps
PropertyTypeRequiredDefaultDescription
labelstringYes—Text of the built-in trigger and accessible name of the popover.
openbooleanNo—Controlled open state. Pair with onOpenChange.
defaultOpenbooleanNofalseInitial open state when open is omitted.
onOpenChange((open: boolean) => void)No—Called when opening or closing is requested.
sizeComponentSizeNo"small"Size of the built-in trigger.
triggerAppearance"text" | "primary" | "secondary"No"text"Appearance of the built-in trigger button.
onCloseAutoFocus((event: Event) => void)No—Called before focus returns after closing; prevent default when navigation manages focus.
childrenReactNodeYes—Content rendered inside the component.
classNamestringNo""Additional classes applied to the component’s documented root or control.

No custom imperative methods. Event callbacks are listed as properties.

ResponsiveMenu

One menu subtree presented inline at ≥1024px, in a Popover at 640–1023px and in a left Drawer below 640px. Crossing layouts remounts the subtree and closes open surfaces.

ResponsiveMenuProps
PropertyTypeRequiredDefaultDescription
labelstringYes—Accessible surface title for the popover or drawer.
triggerLabelstringYes—Text or accessible label of the narrow-layout navigation trigger.
closeLabelstringYes—Accessible name of the drawer close action.
layout"popover" | "inline" | "responsive" | "drawer"No"responsive"Responsive by default: desktop inline, tablet popover, phone drawer. Explicit values force one presentation.
triggerIconOnlybooleanNofalseVisually hide trigger text while retaining triggerLabel as its accessible name.
triggerAppearance"text" | "primary" | "secondary"No"secondary"Appearance of the popover/drawer trigger button.
triggerSizeComponentSizeNo"medium"Size of the popover/drawer trigger button.
drawerPropsPick<DrawerProps, "className" | "headerActions" | "headerContent" | "bodyClassName">No—Header actions/content and body/root classes forwarded only to the drawer layout.
childrenReactNodeYes—Content rendered inside the component.
classNamestringNo""Additional classes applied to the component’s documented root or control.

No custom imperative methods. Event callbacks are listed as properties.

Menu

A native nav container providing size, orientation and active-style context to menu items. Native nav attributes and ref are forwarded.

MenuProps
PropertyTypeRequiredDefaultDescription
labelstringYes—Accessible name for the navigation landmark.
appearance"default" | "subtle"No"default"Container/item treatment: default or subtle.
activeAppearance"subtle" | "solid" | "emphasis"No"subtle"Default active-item style: subtle marker, solid contrast, or emphasis with text scale and ink color.
orientation"vertical" | "horizontal"No"vertical"Vertical navigation list or horizontal navigation row.
sizeComponentSizeNo"medium"Visual size: small, medium or large.
groupGapnumberNo—Gap in pixels between groups; omitted uses the size-dependent spacing token.
childrenReactNodeNo—Content rendered inside the component.
classNamestringNo""Additional classes applied to the component’s documented root or control.
idstringNo—DOM identifier for the rendered element.
refRef<HTMLElement>No—Ref to the component’s documented DOM element.

No custom imperative methods. Event callbacks are listed as properties.

MenuGroup

A labelled group of menu items. Native div attributes and ref apply to the group wrapper.

MenuGroupProps
PropertyTypeRequiredDefaultDescription
labelstringYes—Visible group heading and accessible group name.
childrenReactNodeNo—Content rendered inside the component.
classNamestringNo""Additional classes applied to the component’s documented root or control.
idstringNo—DOM identifier for the rendered element.
refRef<HTMLDivElement>No—Ref to the component’s documented DOM element.

No custom imperative methods. Event callbacks are listed as properties.

MenuItem

A native navigation link with active and disabled states. Disabled items lose href and keyboard tab access. Native anchor attributes are forwarded.

MenuItemProps
PropertyTypeRequiredDefaultDescription
hrefstringYes—Navigation destination.
activebooleanNo—Mark the item current. Defaults aria-current to page; an explicit aria-current can select location.
activeAppearance"subtle" | "solid" | "emphasis"NoInherits MenuOverride the active style inherited from Menu: subtle, solid, or emphasis.
disabledbooleanNo—Remove href and tab access and prevent activation.
dismissOnSelectbooleanNotrueClose the enclosing menu on unmodified primary activation, even when onClick prevents navigation.
iconReactNodeNo—Decorative leading icon.
trailingReactNodeNo—Trailing content, such as a count or shortcut.
targetHTMLAttributeAnchorTargetNo—Browsing context for the linked destination, such as _self or _blank.
relstringNo—Link relationship tokens. New-window links also receive noopener noreferrer.
aria-currentboolean | "time" | "true" | "false" | "step" | "page" | "location" | "date"No—Current navigation position. Used only when active is true; defaults to page.
onClickMouseEventHandler<HTMLAnchorElement>No—Native link activation handler; prevent default to handle navigation in the app.
childrenReactNodeNo—Content rendered inside the component.
classNamestringNo""Additional classes applied to the component’s documented root or control.
idstringNo—DOM identifier for the rendered element.
refRef<HTMLAnchorElement>No—Ref to the component’s documented DOM element.

No custom imperative methods. Event callbacks are listed as properties.

MenuAction

A native button for menu actions. Prevent default in onClick to keep the enclosing menu open. Native button attributes are forwarded.

MenuActionProps
PropertyTypeRequiredDefaultDescription
dismissOnSelectbooleanNotrueClose the enclosing menu after activation unless onClick prevents default.
disabledbooleanNo—Disables the native control.
type"button" | "submit" | "reset"No"button"Native control type.
onClickMouseEventHandler<HTMLButtonElement>No—Native activation handler.
namestringNo—Native form submission field name.
valuestring | number | readonly string[]No—Current native control value. Pair with onChange for controlled input.
childrenReactNodeNo—Content rendered inside the component.
classNamestringNo""Additional classes applied to the component’s documented root or control.
idstringNo—DOM identifier for the rendered element.
refRef<HTMLButtonElement>No—Ref to the component’s documented DOM element.

No custom imperative methods. Event callbacks are listed as properties.