Ir al contenido
Navegación

Menubar

La barra de menús de una app: Archivo, Editar, Ver. Un DropdownMenu por título, coordinados.

import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, … } from "sebs7n-ui/menubar"

Ejemplos

El editor de informes

Tab entra una sola vez a la barra; ← → cambian de título y, con un menú abierto, pasar al de al lado lo abre solo. Los atajos de la derecha son el motivo de que el menubar exista: se abre una vez para descubrirlos.

import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger } from "sebs7n-ui/menubar"
import { useState } from "react"

function Editor() {
  const [tema, setTema] = useState("sistema")
  return (
    <Menubar>
      <MenubarMenu>
        <MenubarTrigger>Archivo</MenubarTrigger>
        <MenubarContent>
          <MenubarItem>
            Nuevo informe
            <MenubarShortcut>⌘N</MenubarShortcut>
          </MenubarItem>
          <MenubarItem>
            Abrir…
            <MenubarShortcut>⌘O</MenubarShortcut>
          </MenubarItem>
          <MenubarItem>
            Guardar
            <MenubarShortcut>⌘S</MenubarShortcut>
          </MenubarItem>
          <MenubarSub>
            <MenubarSubTrigger>Exportar</MenubarSubTrigger>
            <MenubarSubContent>
              <MenubarItem>PDF</MenubarItem>
              <MenubarItem>CSV</MenubarItem>
              <MenubarItem>Planilla de cálculo</MenubarItem>
            </MenubarSubContent>
          </MenubarSub>
          <MenubarSeparator />
          <MenubarItem variant="destructive">
            Descartar borrador…
            <MenubarShortcut>⌘⌫</MenubarShortcut>
          </MenubarItem>
        </MenubarContent>
      </MenubarMenu>

      <MenubarMenu>
        <MenubarTrigger>Editar</MenubarTrigger>
        <MenubarContent>
          <MenubarItem>
            Deshacer
            <MenubarShortcut>⌘Z</MenubarShortcut>
          </MenubarItem>
          <MenubarItem>
            Rehacer
            <MenubarShortcut>⌘⇧Z</MenubarShortcut>
          </MenubarItem>
          <MenubarSeparator />
          <MenubarItem>
            Buscar y reemplazar
            <MenubarShortcut>⌘F</MenubarShortcut>
          </MenubarItem>
        </MenubarContent>
      </MenubarMenu>

      <MenubarMenu>
        <MenubarTrigger>Ver</MenubarTrigger>
        <MenubarContent>
          <MenubarGroup>
            <MenubarLabel>Paneles</MenubarLabel>
            <MenubarCheckboxItem defaultChecked>
              Barra lateral
              <MenubarShortcut>⌘B</MenubarShortcut>
            </MenubarCheckboxItem>
            <MenubarCheckboxItem>Notas al pie</MenubarCheckboxItem>
          </MenubarGroup>
          <MenubarSeparator />
          <MenubarGroup>
            <MenubarLabel>Tema</MenubarLabel>
            <MenubarRadioGroup onValueChange={setTema} value={tema}>
              <MenubarRadioItem value="claro">Claro</MenubarRadioItem>
              <MenubarRadioItem value="oscuro">Oscuro</MenubarRadioItem>
              <MenubarRadioItem value="sistema">Sistema</MenubarRadioItem>
            </MenubarRadioGroup>
          </MenubarGroup>
        </MenubarContent>
      </MenubarMenu>
    </Menubar>
  )
}

Un título deshabilitado

Cuando un menú entero no aplica al documento abierto, se apaga el título en vez de sacarlo: una barra que cambia de ancho según el archivo obliga a buscar de nuevo cada vez.

import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarTrigger } from "sebs7n-ui/menubar"

function Deshabilitado() {
  return (
    <Menubar>
      <MenubarMenu>
        <MenubarTrigger>Archivo</MenubarTrigger>
        <MenubarContent>
          <MenubarItem>Nuevo informe</MenubarItem>
          <MenubarItem>Abrir…</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
      <MenubarMenu disabled>
        <MenubarTrigger>Tabla</MenubarTrigger>
        <MenubarContent>
          <MenubarItem>Insertar fila</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
      <MenubarMenu>
        <MenubarTrigger>Ayuda</MenubarTrigger>
        <MenubarContent>
          <MenubarItem>Atajos de teclado</MenubarItem>
          <MenubarItem>Documentación</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
    </Menubar>
  )
}

Props

Generadas del TypeScript del paquete. Las propias del componente, más las heredadas del primitivo que tienen algo que explicar —marcadas «heredada de Base UI»—. El resto está en la línea «hereda de».

Menubar

Hereda las props de Menubar.

PropTipoPor defectoDescripción
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.
loopFocusboolean—Heredada de Base UI. Si al pasar del último título se vuelve al primero.
modalboolean—Heredada de Base UI. Con true (el default), mientras hay un menú abierto el resto de la página no recibe clicks.

MenubarCheckboxItem

Hereda las props de Menu.CheckboxItem.

PropTipoPor defectoDescripción
insetboolean—Corre el texto a la canaleta del tilde (pl-7) para que alinee con los CheckboxItem y RadioItem del mismo menú. En un menú sin tildes no hace falta.
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

MenubarContent

Hereda las props de Menu.Popup y Menu.Positioner.

PropTipoPor defectoDescripción
align"center" | "end" | "start""start"Cómo se alinea el panel sobre el eje transversal.
alignOffsetnumber | OffsetFunction0Corrimiento en píxeles sobre el eje de alineación.
side"left" | "right" | "top" | "bottom" | "inline-end" | "inline-start""bottom"De qué lado del ancla se abre el panel.
sideOffsetnumber | OffsetFunction6Distancia en píxeles entre el ancla y el panel.
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

MenubarGroup

Hereda las props de Menu.Group.

Sin props propias: pasa todo al primitivo.

MenubarItem

Hereda las props de Menu.Item.

PropTipoPor defectoDescripción
externalboolean—Lleva a otro sitio: texto en el acento y ↗ al final.
insetboolean—Alinea el texto con el de los ítems que llevan ícono.
variant"default" | "destructive""default"destructive: texto e ícono en rojo, para la acción que borra.
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

MenubarLabel

Hereda las props de Menu.GroupLabel.

PropTipoPor defectoDescripción
insetboolean—Corre el texto a la canaleta del tilde (pl-7) para que alinee con los CheckboxItem y RadioItem del mismo menú. En un menú sin tildes no hace falta.
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

MenubarMenu

Hereda las props de Menu.Root.

PropTipoPor defectoDescripción
actionsRefReact.RefObject<MenuRoot.Actions | null>—Heredada de Base UI. Ref con las acciones imperativas de Base UI (unmount()), para desmontarlo sin esperar la animación de salida.
defaultOpenboolean—Heredada de Base UI. Si arranca abierto. Es la versión no controlada de open.
disabledboolean—Heredada de Base UI. Apaga la interacción y lo marca con data-disabled, que es el atributo del que cuelgan los estilos de apagado.
loopFocusboolean—Heredada de Base UI. Si al pasar del último elemento el foco vuelve al primero.
onOpenChange((open: boolean, eventDetails: MenuRoot.ChangeEventDetails) => void)—Heredada de Base UI. Se llama con el estado nuevo cada vez que se abre o se cierra.
openboolean—Heredada de Base UI. Si está abierto. Pasarla lo vuelve controlado: sin onOpenChange ya no se cierra solo.

MenubarRadioGroup

Hereda las props de Menu.RadioGroup.

Sin props propias: pasa todo al primitivo.

MenubarRadioItem

Hereda las props de Menu.RadioItem.

PropTipoPor defectoDescripción
insetboolean—Corre el texto a la canaleta del tilde (pl-7) para que alinee con los CheckboxItem y RadioItem del mismo menú. En un menú sin tildes no hace falta.
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

MenubarSeparator

Hereda las props de Menu.Separator.

PropTipoPor defectoDescripción
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

MenubarShortcut

Hereda las props de <span>.

PropTipoPor defectoDescripción
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

MenubarSub

Hereda las props de Menu.SubmenuRoot.

Sin props propias: pasa todo al primitivo.

MenubarSubContent

Hereda las props de Menu.Popup y Menu.Positioner.

PropTipoPor defectoDescripción
align"center" | "end" | "start""start"Cómo se alinea el panel sobre el eje transversal.
alignOffsetnumber | OffsetFunction-5Corrimiento en píxeles sobre el eje de alineación.
side"left" | "right" | "top" | "bottom" | "inline-end" | "inline-start""inline-end"De qué lado del ancla se abre el panel.
sideOffsetnumber | OffsetFunction2Distancia en píxeles entre el ancla y el panel.
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

MenubarSubTrigger

Hereda las props de Menu.SubmenuTrigger.

PropTipoPor defectoDescripción
insetboolean—Corre el texto a la canaleta del tilde (pl-7) para que alinee con los CheckboxItem y RadioItem del mismo menú. En un menú sin tildes no hace falta.
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

MenubarTrigger

Hereda las props de Menu.Trigger.

PropTipoPor defectoDescripción
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

Teclado

Tab
Entra a la barra y sale. Los títulos son una sola parada de tabulación.
← →
Cambia de título. Con un menú abierto, abre el del título al que llegás.
Home · End
Primer y último título.
Enter · Espacio · ↓
Abre el menú del título enfocado.
↑ ↓
Recorre los ítems del menú abierto.
→ ←
Entra y sale de un submenú.
Escribir
Salta al ítem que empieza con esas letras.
Enter
Ejecuta y cierra.
Escape
Cierra el menú y devuelve el foco a su título.
Pasar el mouse
Con un menú abierto, pasar por otro título lo abre sin click.

Accesibilidad

  • Base UI emite role="menubar" con aria-orientation, y cada menú es el role="menu" del sistema: el lector anuncia «barra de menús, N elementos».
  • El recorrido es roving tabindex: la barra entera ocupa una parada de Tab, no una por título.
  • Un MenubarMenu disabled deja el título en la barra y apagado, en vez de sacarlo: una barra que cambia de ancho según el documento obliga a buscar de nuevo cada vez.
  • MenubarShortcut es decorativo: el keydown lo registra la app. En un check, el atajo va antes del tilde, que ocupa la columna de la derecha.

Reglas de uso

  • Casi ninguna web necesita un menubar. Es para apps con decenas de comandos y ninguna otra superficie donde meterlos: un editor, una planilla, una herramienta de diseño.
  • Si los ítems son secciones del sitio, lo que querés es `NavigationMenu`. Un menubar usado para navegación esconde atrás de tres menús lo que una barra de links mostraba de entrada.
  • Si es un solo grupo de acciones, es un `DropdownMenu` suelto. Una barra de un título no es una barra.
  • Sin fondo ni borde propios: vive dentro del header de la app y hereda su superficie.
  • Los atajos son la mitad del trabajo: el menú se abre una vez para descubrir el comando y después se usa el teclado para siempre. Un menubar sin MenubarShortcut desperdicia el componente.

Relacionados