Saltar al contenido
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
Primeros pasosWebReactSwiftUIJetpack ComposeFlutterReact NativePreajustesTu propio sistema de diseñoUsar Tanrei con agentes de IAMarcaTokens de diseñoSistemas de diseño y personalizaciónTheme StudioColorTipografíaIconosFormaElevaciónMovimientoEstados de interacciónComportamientoDiseño de páginaAccesibilidadTextosTokens de colorTokens de la escala tipográficaTokens de formaTokens de elevaciónTokens de movimientoTokens de estadoTokens de espacio y de medidaComponentesBotónBotón de acción principalBotón segmentadoGrupo de botonesBotón con menúBarra de herramientasBotón de copiarCasilla de verificaciónBotón de opciónInterruptorChipMenúControl deslizanteCampo de control deslizanteCalendarioDateTimePickerGrupo de camposGrupo de botones de opciónGrupo de casillas de verificaciónSelectCampo de selecciónBarra de menúsSubida de archivosSelector de archivos AvanzadoZona de soltar archivosEntrada de texto AvanzadoCampo de textoCampo de chipsCampoCuadro combinado AvanzadoCampo de cuadro combinadoEntrada numérica AvanzadoCampo numéricoEntrada de color AvanzadoCampo de colorEntrada de código de un solo uso AvanzadoCampo de código de un solo usoEntrada de texto incrustada AvanzadoCuadro de redacción de chatTarjetaDivisorCapa modal AvanzadoDiálogoListaFila de listaHoja inferiorHoja lateralCarruselBloque de códigoTablaPopoverPasosAcordeónLista de descripcionesFiguraEstructura de la aplicaciónCabecera de páginaVista de árbolLínea de tiempoVista previa de imágenesTirador de tamañoMarco redimensionableÁrea de desplazamientoInsigniaIndicador de progresoMedidorContador animadoIndicador de cargaSnackbarTooltipAtajo de tecladoTagCódigo en líneaCalloutAvatarEstado vacíoGrupo de tagsEsqueletoElemento de archivoMuestraTexto en marquesinaPestañasVista de pestañasBarra de la aplicaciónBarra de navegaciónPanel de navegaciónBúsquedaEnlacePaginaciónEnlaces anterior y siguienteIndicador de pasosRuta de navegaciónTabla de contenidoTarjeta de enlaceMenú FABEstadoCómo se construye TanreiCómo contribuirLicencia y atribución
InicioComponentesMenú

Menú

Un conjunto corto de acciones u opciones en una superficie temporal que se abre desde el elemento que lo pidió.

content_copyVer en Markdown
Categoría
Selección
Tokens
comp.menu
Nivel
Base

Descripción general

Web
tanrei-menu
schedule
SwiftUI
TanreiMenu
schedule
Jetpack Compose
TanreiMenu
schedule
Flutter
TanreiMenuAnchor
schedule
React Native
Menu
schedule

Generado a partir de docs/status/status.json, specs/menu.spec.json

Acciones, un elemento de casilla de verificación, un grupo de opciones y una acción destructiva

Un submenú: un elemento que abre otro nivel a su lado

Un menú de enlaces: cada fila lleva a algún sitio, una es la página en la que estás, y una abre una pestaña nueva

Uso

El ancla es tu propio botón, botón de icono o campo. El menú no la dibuja: lee la posición del ancla, coloca su superficie junto a ella y le devuelve el foco. Tú abres el menú desde el ancla y lo cierras cuando el menú lo pide.

Un menú tiene cinco tipos de elemento: item (una acción), checkbox-item, radio-item, submenu (un elemento que abre otro nivel del menú a su lado) y group, que contiene elementos de los otros tipos. Cada plataforma los recibe como recibe cualquier conjunto de hijos repetidos: elementos hijos en la web, vistas en un view builder en SwiftUI, llamadas en una lambda de contenido en Compose, widgets en menuChildren en Flutter y, en React Native, un array items de objetos planos que se distinguen por kind. El checked de un elemento de casilla de verificación es suyo. Cuál de los elementos de opción está marcado pertenece a su grupo, por valor.

El atajo de teclado de un elemento es su propiedad shortcut, escrita con los nombres de tecla neutros del atajo de teclado (mod+c). El menú lo dibuja al final de la fila con el componente de atajo de teclado, como ⌘C en los teclados de Apple y Ctrl+C en los demás, y lo expone como el atajo de teclado del elemento. No hace que las teclas funcionen: las vincula tu producto. trailingText es para todo lo que no sea un atajo, como un recuento o «Beta». La web dibuja todo esto, y el aspecto actual. SwiftUI, Compose, Flutter y React Native van rezagados: hasta que se pongan al día, no tienen la propiedad shortcut y escriben el atajo en trailingText, como hacen sus ejemplos de abajo. Tampoco tienen submenús, separation, iconSpace ni markIcon, y todavía dibujan la marca de verificación como una marca propia en un recuadro de 16, no rellenan ninguna fila marcada y empiezan el título de un grupo donde empiezan las etiquetas.

Un elemento con un href es un elemento de enlace: el enlace propio de la plataforma, que se dibuja exactamente igual que una acción y al que se llega con las mismas teclas. Una activación corriente emite el click del elemento, cierra el menú y después abre el destino. target y targetLabel son los del botón. current marca el elemento de enlace de la página en la que está la persona: se dibuja con un relleno propio, sin marca de verificación. La web incluye los elementos de enlace; SwiftUI, Compose, Flutter y React Native todavía no los tienen.

Un menú también puede ser el menú contextual de un área: pon trigger en context y el ancla es esa área, como un lienzo, una lista o una fila. La persona pide el menú en un punto de ella con un clic derecho, una pulsación larga, o Mayús+F10 o la tecla de menú contextual sobre lo que tiene el foco. El menú emite contextRequest con el punto, y tú lo abres allí. La web tiene el activador contextual; SwiftUI, Compose, Flutter y React Native todavía no.

<tanrei-button variant="text" id="more" label="Más"><span slot="icon">more_vert</span></tanrei-button>

<!-- anchor es el id del elemento desde el que se abre el menú. -->
<tanrei-menu anchor="more">
  <!-- shortcut se escribe con nombres de tecla neutros: se dibuja como ⌘C o Ctrl+C según el teclado de la persona. -->
  <tanrei-menu-item label="Copiar" leading-icon="content_copy" shortcut="mod+c"></tanrei-menu-item>
  <!-- El título de un grupo es su atributo label, como en <optgroup label>. -->
  <tanrei-menu-group label="Vista">
    <tanrei-menu-checkbox-item label="Mostrar cuadrícula" checked></tanrei-menu-checkbox-item>
  </tanrei-menu-group>
  <!-- mark-icon="check" en un grupo, o en un elemento, marca los que están marcados con una simple marca de verificación. -->
  <tanrei-menu-group label="Ordenar por" value="name" mark-icon="check">
    <tanrei-menu-radio-item label="Nombre" value="name"></tanrei-menu-radio-item>
    <tanrei-menu-radio-item label="Fecha de modificación" value="date"></tanrei-menu-radio-item>
  </tanrei-menu-group>
  <!-- Un submenú: sus hijos son el nivel siguiente. Emite toggle, nunca close. -->
  <tanrei-submenu label="Abrir recientes" leading-icon="history">
    <tanrei-menu-item label="Report.pdf"></tanrei-menu-item>
    <tanrei-menu-item label="Notes.txt"></tanrei-menu-item>
  </tanrei-submenu>
  <tanrei-menu-item label="Eliminar" leading-icon="delete" destructive></tanrei-menu-item>
  <!-- Con href, un elemento es un enlace: primero su click, después el cierre y, por último, el navegador lo sigue. -->
  <tanrei-menu-item label="Atajos de teclado" href="/help/shortcuts/"></tanrei-menu-item>
  <tanrei-menu-item label="Código fuente" href="https://example.com/tanrei" target="new" target-label="se abre en una pestaña nueva"></tanrei-menu-item>
</tanrei-menu>

<!-- separation="gap" separa los grupos con espacio vacío en este menú y en sus submenús, elija lo que elija el sistema de diseño. -->
<tanrei-menu anchor="more" separation="gap">…</tanrei-menu>

<!-- trigger="context": el menú contextual del área cuyo id es anchor. Dale nombre con aria-label. -->
<div id="canvas">…</div>
<tanrei-menu id="canvas-menu" anchor="canvas" trigger="context" aria-label="Lienzo">…</tanrei-menu>

<script type="module">
  import '@tanrei/web/components/menu';
  const menu = document.querySelector('tanrei-menu');
  // Una pulsación en el ancla es cosa del ancla: no cierra el menú, así que ábrelo o ciérralo aquí.
  document.querySelector('#more').addEventListener('click', () => (menu.open = !menu.open));
  // close lleva su motivo: escape, outside, tab, focus-out o item.
  menu.addEventListener('close', () => (menu.open = false));
  // click, change y toggle se emiten en el elemento o en el grupo y se propagan, así que un solo listener en el menú puede recibirlos todos.
  menu.querySelector('[label="Copiar"]').addEventListener('click', copy);
  menu.querySelector('[label="Mostrar cuadrícula"]').addEventListener('change', (e) => setGrid(e.detail.checked));
  menu.querySelector('[label="Ordenar por"]').addEventListener('change', (e) => sortBy(e.detail.value));

  // Un clic derecho, una pulsación larga, Mayús+F10 o la tecla de menú contextual en el área piden el menú contextual.
  // detail lleva el punto, el elemento por el que se pidió y cómo. Ábrelo, o cancela el evento para
  // dejar la petición al menú propio del navegador.
  const canvasMenu = document.querySelector('#canvas-menu');
  canvasMenu.addEventListener('contextrequest', () => (canvasMenu.open = true));
  canvasMenu.addEventListener('close', () => (canvasMenu.open = false));
</script>
import { Button, Menu, MenuCheckboxItem, MenuGroup, MenuItem, MenuRadioItem, Submenu } from '@tanrei/react';

<Button variant="text" id="more" label="Más" onClick={() => setOpen(!open)}><span slot="icon">more_vert</span></Button>
<Menu anchor="more" open={open} onClose={() => setOpen(false)}>
  <MenuItem label="Copiar" leadingIcon="content_copy" shortcut={['mod+c']} onClick={copy} />
  <MenuGroup label="Vista">
    <MenuCheckboxItem label="Mostrar cuadrícula" checked={grid} onChange={(e) => setGrid(e.detail.checked)} />
  </MenuGroup>
  <MenuGroup label="Ordenar por" value={sort} onChange={(e) => setSort(e.detail.value)}>
    <MenuRadioItem label="Nombre" value="name" />
    <MenuRadioItem label="Fecha de modificación" value="date" />
  </MenuGroup>
  <Submenu label="Abrir recientes" leadingIcon="history">
    <MenuItem label="Report.pdf" onClick={() => openFile('Report.pdf')} />
    <MenuItem label="Notes.txt" onClick={() => openFile('Notes.txt')} />
  </Submenu>
  <MenuItem label="Eliminar" leadingIcon="delete" destructive onClick={remove} />
  {/* Un elemento de enlace. Un enrutador cancela el click y va él mismo al destino; el menú se cierra igualmente. */}
  <MenuItem label="Atajos de teclado" href="/help/shortcuts/" onClick={(e) => { e.preventDefault(); navigate('/help/shortcuts/'); }} />
</Menu>

{/* El menú contextual del área cuyo id es canvas: una petición lo abre. */}
<Menu anchor="canvas" trigger="context" aria-label="Lienzo" open={contextOpen} onContextRequest={() => setContextOpen(true)} onClose={() => setContextOpen(false)}>
  <MenuItem label="Pegar" onClick={paste} />
</Menu>
import Tanrei

// Un modificador sobre el ancla, como .popover. Al cerrarse escribe false en isPresented.
TanreiIconButton("Más", action: { showMenu = true }) { Image(systemName: "ellipsis") }
    .tanreiMenu(isPresented: $showMenu, placement: .bottomStart) {
        TanreiMenuItem("Copiar", action: copy)
        TanreiMenuSection("Vista") {
            TanreiMenuToggle("Mostrar cuadrícula", isOn: $showGrid)
        }
        TanreiMenuSection("Ordenar por", selection: $sort) {
            // Aplica .tag(_:) al propio TanreiMenuOption.
            TanreiMenuOption("Nombre").tag("name")
            TanreiMenuOption("Fecha de modificación").tag("date")
        }
        TanreiMenuItem("Eliminar", role: .destructive, action: delete)
    }
import io.github.chnotchy.tanrei.components.TanreiCheckboxMenuItem
import io.github.chnotchy.tanrei.components.TanreiIconButton
import io.github.chnotchy.tanrei.components.TanreiMenu
import io.github.chnotchy.tanrei.components.TanreiMenuGroup
import io.github.chnotchy.tanrei.components.TanreiMenuItem
import io.github.chnotchy.tanrei.components.TanreiRadioMenuItem
import io.github.chnotchy.tanrei.primitives.TanreiText

// Como DropdownMenu: llama al menú dentro del Box del ancla. Lo cierras tú: onDismissRequest lleva el motivo.
Box {
    TanreiIconButton(onClick = { expanded = true }, contentDescription = "Más") { TanreiText("more_vert") }
    TanreiMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
        TanreiMenuItem(text = "Copiar", onClick = ::copy, trailingText = "⌘C", leadingIcon = { TanreiText("content_copy") })
        TanreiMenuGroup(title = "Vista") {
            TanreiCheckboxMenuItem(text = "Mostrar cuadrícula", checked = showGrid, onCheckedChange = { showGrid = it })
        }
        TanreiMenuGroup(title = "Ordenar por", selectedValue = sort, onSelectedValueChange = { sort = it }) {
            TanreiRadioMenuItem(text = "Nombre", value = "name")
            TanreiRadioMenuItem(text = "Fecha de modificación", value = "date")
        }
        TanreiMenuItem(text = "Eliminar", onClick = ::delete, destructive = true, leadingIcon = { TanreiText("delete") })
    }
}
import 'package:tanrei/tanrei.dart';

// Como MenuAnchor: lo abre un controlador. Cuando el menú pide cerrarse, llama a onClose con el motivo y se cierra solo.
final TanreiMenuController menu = TanreiMenuController();

TanreiMenuAnchor(
  controller: menu,
  onClose: (TanreiMenuCloseReason reason) {},
  menuChildren: <Widget>[
    TanreiMenuItem(
      onPressed: copy,
      leadingIcon: const Icon(AppSymbols.contentCopy),
      trailingText: '⌘C',
      child: const Text('Copiar'),
    ),
    TanreiMenuGroup<String>(
      title: 'Vista',
      children: <Widget>[
        TanreiCheckboxMenuItem(
          value: showGrid,
          onChanged: (bool value) => setState(() => showGrid = value),
          child: const Text('Mostrar cuadrícula'),
        ),
      ],
    ),
    TanreiMenuGroup<String>(
      title: 'Ordenar por',
      groupValue: sort,
      onChanged: (String value) => setState(() => sort = value),
      children: const <Widget>[
        TanreiRadioMenuItem<String>(value: 'name', child: Text('Nombre')),
        TanreiRadioMenuItem<String>(value: 'date', child: Text('Fecha de modificación')),
      ],
    ),
    // Un onPressed nulo deshabilita un elemento.
    TanreiMenuItem(onPressed: delete, destructive: true, child: const Text('Eliminar')),
  ],
  builder: (BuildContext context, TanreiMenuController controller, Widget? child) =>
      TanreiIconButton(onPressed: controller.open, icon: const Icon(AppSymbols.moreVert), semanticLabel: 'Más'),
);
import { useRef, useState } from 'react';
import type { View } from 'react-native';
import { IconButton, Menu } from '@tanrei/react-native';

const more = useRef<View>(null);
const [open, setOpen] = useState(false);

// Indica a la tecnología de apoyo si el menú del ancla está abierto: aquí el menú no puede definirlo.
<IconButton ref={more} accessibilityLabel="Más" accessibilityState={{ expanded: open }} onPress={() => setOpen(true)}>
  more_vert
</IconButton>
// Controlado: define visible como false desde onDismiss, que recibe el motivo.
<Menu
  visible={open}
  anchorRef={more}
  accessibilityLabel="Más"
  onDismiss={() => setOpen(false)}
  items={[
    { label: 'Copiar', leadingIcon: 'content_copy', trailingText: '⌘C', onPress: copy },
    { kind: 'group', title: 'Vista', items: [{ kind: 'checkbox-item', label: 'Mostrar cuadrícula', checked: grid, onCheckedChange: setGrid }] },
    { kind: 'group', title: 'Ordenar por', value: sort, onValueChange: setSort, items: [
      { kind: 'radio-item', label: 'Nombre', value: 'name' },
      { kind: 'radio-item', label: 'Fecha de modificación', value: 'date' },
    ] },
    { label: 'Eliminar', leadingIcon: 'delete', destructive: true, onPress: remove },
  ]}
/>

Especificaciones

Texto de apoyo, icono final y elementos deshabilitados

Títulos de grupo sobre las columnas de la marca y del icono

Columnas de marca e icono: iconSpace auto, el valor predeterminado, y never

Marcas: el recuadro y el anillo del sistema predeterminado, y una simple marca de verificación indicada con mark-icon

Separación: un divisor entre grupos y una separación de espacio vacío

En el sistema predeterminado, un elemento de casilla de verificación se marca como una casilla de verificación y un elemento de opción como un botón de opción, que se dibujan tanto si el elemento está marcado como si no: un recuadro vacío y un recuadro relleno con una marca de verificación, un anillo fino y un anillo grueso con su relleno. Así, una opción que está desactivada se sigue viendo como una opción. Son dibujos en la columna de la marca, no controles: la fila es lo único que se pulsa y que recibe el foco. La marca de verificación del recuadro se dibuja con el peso mayor de la marca propia de un control, como la de la casilla de verificación.

markIcon, en un elemento o en un grupo para todos sus elementos, nombra un glifo del conjunto de iconos con el que marcar en su lugar un elemento marcado. check dibuja una simple marca de verificación del tamaño del icono inicial que tiene al lado (24 en el sistema predeterminado), y nada mientras el elemento no está marcado. Un sistema de diseño elige sus propias marcas: m3-like y cupertino usan la marca de verificación simple. La marca de verificación no se refleja en un diseño de derecha a izquierda. Cuando el estado marcado de un elemento cambia mientras el menú está abierto, la marca cambia sin moverse, y de inmediato donde el movimiento está reducido.

Una fila marcada se reconoce por su marca. En el sistema predeterminado no se rellena con el contraste estándar, de modo que la única fila rellena de un menú en reposo es la de la página en la que está la persona. Con contraste medio y alto, cuando la persona ha pedido señales más fuertes, la fila marcada también se rellena: una capa del propio color del contenido de la fila sobre la superficie del menú, un paso más intensa que la de una fila con el cursor encima, tanto en el tema claro como en el oscuro, de modo que una fila marcada, una fila con el cursor encima y una fila en reposo se distinguen sobre una superficie elevada. El menú lee el relleno de sus tokens en el esquema en el que se dibuja; es el sistema de diseño quien lo decide para cada esquema de contraste. El elemento cuyo submenú está abierto conserva el relleno del paso del cursor mientras su submenú está abierto, lo que señala el camino hasta el nivel en uso, y nunca parece una fila rellena.

El título de un grupo empieza donde empieza el contenido de las filas: sobre la marca o el icono inicial cuando el menú dibuja esas columnas, y sobre las etiquetas cuando no dibuja ninguna de las dos. Termina a la misma distancia del otro borde, salta de línea, crece con un texto más grande y se refleja en un diseño de derecha a izquierda.

Anatomía

Anatomía

Dibujado sobre el fixture actions, tal como lo dibuja el paquete web.
Parte Descripción
1 surface The floating sheet that holds the items: its color, corners, elevation and padding. It is placed against the anchor, or at the point a context menu was asked for, and drawn above everything else.
2 container One item's row. It carries hover, focus and press, the fill of a checked item, and the fill of a submenu item whose submenu is open.
3 mark · opcional What shows whether a checkbox or radio item is chosen, in the mark column at the start of the row. By default it is the look of the matching control, drawn as a mark: a box for a checkbox item (empty, or filled with the icon set's check) and a ring for a radio item (thin, or heavy with its fill), in both conditions. Where the design system chooses the check (comp.menu.checkbox-item.mark, comp.menu.radio-item.mark) or the item or its group names a glyph (markIcon), it is that glyph of the system's icon set while the item is checked and nothing while it is not. It is never a control of its own: it takes no focus and no press, and the item stays a menu item checkbox or a menu item radio. Its column is kept, empty, in the other rows of its section while iconSpace is auto, in every row while it is always, and in its own row only while it is never (MNU-59).
4 leading-icon · opcional An icon before the label. Its column is kept, empty, in the other rows of its section while iconSpace is auto, in every row while it is always, and in its own row only while it is never (MNU-59).
5 label The item's text and its accessible name.
6 supporting-text · opcional A line below the label that says more about the item.
7 trailing-text · opcional Text at the end of the row that is not a keyboard shortcut, such as a count or "Beta". It shares the trailing column with the shortcut.
8 shortcut · opcional The item's keyboard shortcut at the end of the row, in the trailing column: the keyboard shortcut component at its small size, in the form comp.menu.shortcut.variant names, its legends in the trailing text's style and color (MNU-31).
9 trailing-icon · opcional An icon at the end of the row. A submenu item draws its submenu indicator in this column instead, and a link item that opens a new context draws its mark there, the glyph open_in_new, while the product names no glyph for it (MNU-55).
10 submenu-indicator · opcional The glyph chevron_right of the system's icon set at the end of a submenu item's row, in the trailing icon column: it says the item opens another level, and points to the side that level opens on. The icon set marks it as mirrored, so the icon helper turns it in a right-to-left layout. The menu draws no chevron of its own.
11 submenu · opcional The surface a submenu item opens: a menu of its own beside the surface that holds the item, drawn with the surface's tokens and holding items of every kind, further submenus included.
12 group · opcional A run of related items (a group item), optionally under a title. The menu separates a group from what is next to it by a divider, or under gap by drawing it as a tile of its own, as its separation names, and a group of radio items is one set of choices.
13 group-title · opcional The heading drawn above a group's items when the group has a title. It starts where the rows' content starts, not after the check and icon columns. It names the group and takes no focus.
14 divider · opcional The hairline the menu draws between two groups while its separation is divider. Under gap there is none: each group is a tile of its own, and the space between the tiles parts them (MNU-36).
15 focus-ring Gap-then-ring indicator shown for keyboard focus, drawn inside the item's row.

Medidas

Medidas

Longitud Token Valor (px)
surface padding-inline tanrei.comp.menu.surface.padding-inline 4
Dibujado sobre el fixture actions, tal como lo dibuja el paquete web.

Propiedades

Propiedad Tipo Valor predeterminado Descripción
open boolean false Whether the menu is shown. The product opens it from its anchor (a button, an icon-only Button, a field), or on contextRequest while trigger is context. Where open is a plain value (web, Compose, React Native) the product closes the menu by setting it to false when the menu emits close; where it is a binding or a controller (SwiftUI, Flutter) the menu closes itself through it (MNU-5).
placement bottom-start | bottom-end | top-start | top-end "bottom-start" Where the surface goes against the anchor: below or above it, with its start or end edge aligned to the anchor's. The menu flips to the other side when this side has no room. For a context menu opened at a point it is where the surface goes from the point: bottom-start puts its block-start, inline-start corner there (MNU-61).
trigger anchor | context "anchor" What the menu opens from. anchor: the product opens it from its anchor — a button, an icon-only Button, a field — and the surface is placed against the anchor's edges. context: the menu is the context menu of an area, and the anchor is that area (a canvas, a list, a row, a document's body of text); a person asks for the menu at a point in it — a secondary click, a long press of a touch or a pen, Shift+F10 or the ContextMenu key on the element of the area that has focus — and the menu emits contextRequest, the product sets open, and the surface opens at that point, or against that element for the keyboard. The items, the keys, the submenus and the close reasons are the same in both (MNU-60 to MNU-63).
separation auto | divider | gap "auto" How a group is told from what is next to it. divider: a line between them. gap: empty space and no line. Auto takes comp.menu.separation, the design system's answer (divider in the default system). It applies to the menu and to every submenu of it.
iconSpace auto | always | never "auto" Whether a row keeps the room of the mark column and of the leading icon column where it has nothing to draw there, so that the labels of a section start on one line — a section being a group, or a run of adjacent items outside any group. auto: a row keeps a column's room while another item of its section draws in that column (a checkbox or radio item its mark, an item its leading icon), so that a section without marks or icons draws its labels where the row's content starts. always: every row keeps both. never: only a row that draws in a column has it. The tree view's and the navigation drawer's iconSpace, with the same values and default. It applies to the menu and to every submenu of it (MNU-59).
density regular | compact "regular" How much room a row takes: its least height, its paddings, the gap between its columns and the size of its glyphs (the mark, the leading and the trailing icon), and never its text style. regular (a 48 row with 24 glyphs in the default system) is the menu of a product used by touch as well as by pointer. compact (a 32 row with 20 glyphs, the tree view's compact row) is for a tool used with a pointer, whose menus are long and opened often. For touch a row is never lower than the touch target in either (MNU-12). It applies to the menu and to every submenu of it (MNU-64). A design system may add values.

Eventos

Evento Descripción
close The menu asks to be closed, every open submenu with it: Escape or the platform's back while no submenu is open, a press outside, Tab, focus leaving it, an item's activation at any level, or its anchor no longer drawn (MNU-65). Carries which of these it was, named escape, back, outside, tab, focus-out, item or anchor-hidden (in the platform's casing). Closing a submenu alone is the submenu item's toggle, not this event.
contextRequest While trigger is context: the person asked for the context menu in the anchor's area. Carries the point in the viewport's coordinates, the element it was asked about (the one under the pointer, or the one with focus) and how it was asked: pointer (a secondary click, or the context-menu command of assistive technology), long-press (a touch or a pen held still) or keyboard (Shift+F10, the ContextMenu key). Set open to true to show the menu there. Cancelling it leaves the request to the platform: its own context menu answers it (MNU-60).

Elementos

Los tipos de elemento que el componente repite. Un fixture enumera sus elementos en orden; cada plataforma nombra cada tipo según su propio estilo.

item

An action: it does something and closes the menu. With an href it is a link: it goes somewhere, and the menu closes.

Propiedad Tipo Valor predeterminado Descripción
label string "" The item's text and its accessible name. Required.
supportingText string "" A line below the label that says more about the item.
leadingIcon string "" The name of an icon (a glyph of the system's icon set) before the label.
trailingIcon string "" The name of an icon at the end of the row.
trailingText string "" Text at the end of the row that is not a keyboard shortcut, such as a count or "Beta". An item's shortcut goes in shortcut; an item that has both draws the shortcut.
shortcut strings The item's keyboard shortcut, in the keyboard shortcut component's key names: one chord per string, pressed in order (["mod+c"]). The menu draws it at the end of the row for the keyboard the person uses and exposes it as the item's keyboard shortcut (MNU-24, MNU-31). It does not make the keys work; the product binds them.
destructive boolean false Marks an action that deletes or discards something; it is drawn in the error color.
disabled boolean false Prevents activation. The item stays reachable with the arrow keys.
href string "" The destination: an address as the platform's links take one (a URL; on the web a path or a fragment as well). Not empty, it makes the item a link item (a control with a destination is a link): the platform's own link, exposed as a menu item, drawn exactly as an action is and moved to with the same keys; a plain activation emits click, closes the menu and opens the destination (MNU-53, MNU-54). Empty, the item is an action and the product acts on click. Only the item kind has one: a checkbox item, a radio item and a submenu item take no destination (MNU-57).
target same | new "same" Where the destination opens, as the Button's target. same: where the person is. new: a new context — a new tab or window on the web, outside the current screen elsewhere — which the item marks with the glyph open_in_new in its trailing icon column, unless trailingIcon names one, and says through targetLabel (MNU-55). Ignored without href.
targetLabel string "" What assistive technology reads after the link item's name while target is new ("opens in a new tab"), as the Button's targetLabel: Tanrei ships no words of its own. Required while target is new; ignored otherwise.
current boolean false Marks a link item as the destination the person is at now (the current page): exposed as the current item and drawn with the fill of the row the person is on, comp.menu.current.container.color, in every contrast scheme, with no mark and no change of weight; it stays a link (MNU-58). The product sets it; the menu never does. Ignored without href.
Evento Descripción
click The item was activated by pointer, touch, keyboard or assistive technology. A link item emits it before the menu closes and before its destination opens; a product that takes the navigation itself (a client-side router) cancels it, and the menu still closes (MNU-54).
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento tanrei-menu-item TanreiMenuItem TanreiMenuItem TanreiMenuItem MenuItem
label · Propiedad label title text child label
supportingText · Propiedad supportingText supportingText supportingText supportingText supportingText
leadingIcon · Propiedad leadingIcon leadingIcon leadingIcon leadingIcon leadingIcon
trailingIcon · Propiedad trailingIcon trailingIcon trailingIcon trailingIcon trailingIcon
trailingText · Propiedad trailingText trailingText trailingText trailingText trailingText
shortcut · Propiedad shortcut shortcut shortcut shortcut shortcut
destructive · Propiedad destructive role destructive destructive destructive
disabled · Propiedad disabled disabled enabled onPressed disabled
href · Propiedad href destination href href href
target · Propiedad target target target target target
targetLabel · Propiedad targetLabel targetLabel targetLabel targetSemanticLabel targetLabel
current · Propiedad current isCurrent current isCurrent current
click · Evento click action onClick onPressed onPress
Notas With href the item's shadow root holds an <a role="menuitem" tabindex="-1"> in place of its row element, drawn through the shared link primitive (packages/web/src/primitives/link.ts) as <tanrei-list-item>'s is, with Space left among the keys that press it (MNU-54). target is the target attribute with the values same and new (the HTML spellings _self and _blank are read as those two), targetLabel the target-label attribute, and rel (to which new adds noopener) and download are web-only and forwarded to the anchor. click is the anchor's own click: cancelling it (preventDefault()) cancels the navigation and not the menu's close. The menu fires close from that click only when event.button is 0 and no modifier key is down; Enter with a modifier key is passed to the anchor, and Space calls the anchor's click(). A disabled link item is the anchor without href, with aria-disabled="true". An href attribute on tanrei-menu-checkbox-item, tanrei-menu-radio-item or tanrei-submenu is ignored and reported (MNU-57). TanreiMenuItem(_ title:, role:, action:), as Button(role:); role: .destructive is destructive. destination: URL? = nil makes it a SwiftUI Link (the link trait in place of the button's), opened by the environment's openURL after action and after isPresented is written, which an app that routes addresses itself answers with its own OpenURLAction; target: TanreiLinkTarget = .same and targetLabel (the accessibility hint), as the Button's. href: String? = null makes it a link: opened through LocalUriHandler.current after onClick and onDismissRequest (an app that routes addresses itself provides its own UriHandler); Compose has no link role for a clickable element, so the node drops Role.Button and carries no role; target: TanreiLinkTarget = TanreiLinkTarget.Same, and targetLabel is appended to the state description, as the Button's. As MenuItemButton: a null onPressed disables it. href: Uri? = null makes it a link (Semantics(link: true) with the address as its linkUrl), opened by the nearest TanreiLinkOpener after onPressed and after the menu has closed; target is a TanreiLinkTarget and targetSemanticLabel the semantics hint, as the Button's. href?: string on an entry whose kind is 'item' makes it a link (accessibilityRole="link"): onPress receives an event whose preventDefault() keeps the menu from calling Linking.openURL(href); target is "same" | "new" and targetLabel the accessibilityHint, as the Button's. An href on an entry of another kind is ignored and reported (MNU-57).

checkbox-item

An option that is switched on or off on its own, such as "Show grid". It is marked as a checkbox unless the design system, its group or the item says otherwise.

Propiedad Tipo Valor predeterminado Descripción
label string "" The item's text and its accessible name. Required.
supportingText string "" A line below the label that says more about the item.
leadingIcon string "" The name of an icon (a glyph of the system's icon set) before the label.
trailingIcon string "" The name of an icon at the end of the row.
trailingText string "" Text at the end of the row that is not a keyboard shortcut, such as a count or "Beta". An item's shortcut goes in shortcut; an item that has both draws the shortcut.
shortcut strings The item's keyboard shortcut, in the keyboard shortcut component's key names: one chord per string, pressed in order (["mod+c"]). The menu draws it at the end of the row for the keyboard the person uses and exposes it as the item's keyboard shortcut (MNU-24, MNU-31). It does not make the keys work; the product binds them.
checked boolean false Whether the option is on. It is the item's own: the menu switches it when the item is activated and emits the item's change.
markIcon string "" The name of a glyph of the system's icon set to mark this item with while it is checked, instead of the mark the design system chooses ("check" draws a plain check). Empty takes the group's markIcon, and then the design system's choice (MNU-38).
disabled boolean false Prevents activation. The item stays reachable with the arrow keys.
Evento Descripción
change The item was activated; carries its new checked.
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento tanrei-menu-checkbox-item TanreiMenuToggle TanreiCheckboxMenuItem TanreiCheckboxMenuItem MenuCheckboxItem
label · Propiedad label title text child label
supportingText · Propiedad supportingText supportingText supportingText supportingText supportingText
leadingIcon · Propiedad leadingIcon leadingIcon leadingIcon leadingIcon leadingIcon
trailingIcon · Propiedad trailingIcon trailingIcon trailingIcon trailingIcon trailingIcon
trailingText · Propiedad trailingText trailingText trailingText trailingText trailingText
shortcut · Propiedad shortcut shortcut shortcut shortcut shortcut
checked · Propiedad checked isOn checked value checked
markIcon · Propiedad markIcon markIcon markIcon markIcon markIcon
disabled · Propiedad disabled disabled enabled onChanged disabled
change · Evento change isOn onCheckedChange onChanged onCheckedChange
Notas TanreiMenuToggle(_ title:, isOn:), as Toggle in a menu; writing the binding is change. As CheckboxMenuButton: value is checked, a null onChanged disables it.

radio-item

One choice of several in its group, such as "Sort by name"; its group holds which one is checked. It is marked as a radio button unless the design system, its group or the item says otherwise.

Propiedad Tipo Valor predeterminado Descripción
label string "" The item's text and its accessible name. Required.
supportingText string "" A line below the label that says more about the item.
leadingIcon string "" The name of an icon (a glyph of the system's icon set) before the label.
trailingIcon string "" The name of an icon at the end of the row.
trailingText string "" Text at the end of the row that is not a keyboard shortcut, such as a count or "Beta". An item's shortcut goes in shortcut; an item that has both draws the shortcut.
shortcut strings The item's keyboard shortcut, in the keyboard shortcut component's key names: one chord per string, pressed in order (["mod+c"]). The menu draws it at the end of the row for the keyboard the person uses and exposes it as the item's keyboard shortcut (MNU-24, MNU-31). It does not make the keys work; the product binds them.
value string "" Identifies the choice: the item is checked while its group's value equals it. Unique within its group; required.
markIcon string "" The name of a glyph of the system's icon set to mark this item with while it is checked, instead of the mark the design system chooses ("check" draws a plain check). Empty takes the group's markIcon, and then the design system's choice (MNU-38).
disabled boolean false Prevents activation. The item stays reachable with the arrow keys.
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento tanrei-menu-radio-item TanreiMenuOption TanreiRadioMenuItem TanreiRadioMenuItem MenuRadioItem
label · Propiedad label title text child label
supportingText · Propiedad supportingText supportingText supportingText supportingText supportingText
leadingIcon · Propiedad leadingIcon leadingIcon leadingIcon leadingIcon leadingIcon
trailingIcon · Propiedad trailingIcon trailingIcon trailingIcon trailingIcon trailingIcon
trailingText · Propiedad trailingText trailingText trailingText trailingText trailingText
shortcut · Propiedad shortcut shortcut shortcut shortcut shortcut
value · Propiedad value tag value value value
markIcon · Propiedad markIcon markIcon markIcon markIcon markIcon
disabled · Propiedad disabled disabled enabled enabled disabled
Notas TanreiMenuOption(_ title:).tag(value) inside a section with a selection, as a Picker's tagged options. As RadioMenuButton under a RadioGroup: the item takes its value and enabled, the group the selection.

submenu · grupo: contiene elementos

An item that opens another level of the menu beside it: it holds that level's items, of any kind, further submenus included. It is not an action and has no shortcut.

Propiedad Tipo Valor predeterminado Descripción
label string "" The item's text and its accessible name. Required.
supportingText string "" A line below the label that says more about the item.
leadingIcon string "" The name of an icon (a glyph of the system's icon set) before the label.
trailingText string "" Text at the end of the row, before the submenu indicator: the current value of what the submenu sets ("English"), or a count.
open boolean false Whether the item's submenu is shown. It is the item's own: the menu sets it when the submenu is opened or closed and emits the item's toggle; the product may also set it. It shows nothing while the surface that holds the item is not shown.
disabled boolean false Prevents the submenu from opening. The item stays reachable with the arrow keys.
Evento Descripción
toggle The item's submenu was opened or closed by the person; carries its new open.
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento tanrei-submenu TanreiSubmenu TanreiSubmenu TanreiSubmenu MenuSubmenu
label · Propiedad label title text child label
supportingText · Propiedad supportingText supportingText supportingText supportingText supportingText
leadingIcon · Propiedad leadingIcon leadingIcon leadingIcon leadingIcon leadingIcon
trailingText · Propiedad trailingText trailingText trailingText trailingText trailingText
open · Propiedad open isExpanded expanded controller open
disabled · Propiedad disabled disabled enabled enabled disabled
toggle · Evento toggle isExpanded onExpandedChange onOpenChanged onOpenChange
Notas <tanrei-submenu label="Open recent"> holds the submenu's items as its children, as tanrei-menu holds its own; it draws the row in its parent's surface and its own surface as a manual popover in the top layer. open reflects, and toggle fires on it and bubbles. TanreiSubmenu(_ title:, isExpanded:) { … }, as a Menu nested in a Menu; the binding is optional, and writing it is toggle. disabled follows .disabled(_:). TanreiSubmenu(text, expanded, onExpandedChange) { … }: the caller keeps expanded (state hoisting), and an overload without the pair keeps it itself. enabled is the inverse flag. As SubmenuButton: TanreiSubmenu(child:, menuChildren: […], controller:, onOpenChanged:); the optional MenuController opens and closes it, as the anchor's does the menu. { kind: 'submenu', label, items: […], open?, onOpenChange? }: uncontrolled unless open is given.

group · grupo: contiene elementos

A run of related items, optionally titled. The menu separates a group from what is next to it, by a divider or a gap, and a group of radio items is one set of choices.

Propiedad Tipo Valor predeterminado Descripción
title string "" A short heading drawn above the group's items, which also names the group; empty draws none and leaves the group unnamed.
value string null The value of the group's checked radio item; null checks none. The group sets it when a radio item is chosen and emits its change; the product may also set it. Unused by a group without radio items.
markIcon string "" The name of a glyph of the system's icon set that marks the group's checked checkbox and radio items, for those that name none of their own ("check" draws a plain check). Empty takes the design system's choice (MNU-38).
Evento Descripción
change A radio item of the group was chosen; carries the group's new value.
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento tanrei-menu-group TanreiMenuSection TanreiMenuGroup TanreiMenuGroup MenuGroup
title · Propiedad label title title title title
value · Propiedad value selection selectedValue groupValue value
markIcon · Propiedad markIcon markIcon markIcon markIcon markIcon
change · Evento change selection onSelectedValueChange onChanged onValueChange
Notas The title is the label attribute, as <optgroup label>: a title attribute would also draw the browser's tooltip. The group sets its own value before firing change. TanreiMenuSection(_ title:) { … } as Section; TanreiMenuSection(_ title:, selection:) { TanreiMenuOption … } for radio items, as an inline Picker. Writing the binding is change. TanreiMenuGroup(title, selectedValue, onSelectedValueChange) { TanreiRadioMenuItem(text, value) }; the caller keeps selectedValue. TanreiMenuGroup<T>(title:, groupValue:, onChanged:, children:), which is also the RadioGroup of its radio items. { kind: 'group', title?, value?, onValueChange?, items: (MenuItem | MenuCheckboxItem | MenuRadioItem)[] }.

Estados

Comportamiento

Requisitos en formato EARS. Cada implementación de plataforma se prueba contra estos identificadores. El texto de los requisitos se muestra tal como está en la especificación, en inglés.

ID Requisito
MNU-1 WHEN open becomes true THE menu SHALL present its surface in the platform's overlay layer (the web's top layer through the popover API, SwiftUI's popover, Compose's Popup, Flutter's Overlay, React Native's overlay layer that TanreiProvider renders), above other content at comp.menu.surface.elevation, and SHALL move focus to its first enabled item, wherever that item stands in the surface: inside a group as well, a menu whose every item is in a group included, and a menu the product mounts with open true included; WHEN open becomes false THE menu SHALL remove the surface and, if focus was inside it, return focus to the anchor (a context menu: to the element MNU-62 names). A menu is an operated overlay, so it takes focus and returns it although it is not modal.
MNU-2 WHILE trigger is anchor THE menu SHALL place its surface comp.menu.surface.offset from the anchor on the side and edge that placement names (bottom-start: below the anchor with the start edges aligned; bottom-end: below with the end edges aligned; top-start and top-end: above); WHERE that side has less room than the surface needs within comp.menu.surface.viewport-margin of the edge of the visible part of the window, above the on-screen keyboard while one is shown, and the other side has more THE menu SHALL flip to the other side; it SHALL shift along the anchor to stay within that margin, SHALL limit its height to the least of the height its items need, the most height the product's own style gives the surface where it gives one (on the web, max-block-size on the surface part), and the room left, place the surface by that height and scroll its items beyond it, and SHALL keep following the anchor while it moves. WHEN the on-screen keyboard shows or hides, or the visible part of the window otherwise moves or changes size, THE menu SHALL place its surface again inside what is visible. WHEN the layout direction is right-to-left start and end SHALL mirror.
MNU-3 THE menu SHALL make its surface at least comp.menu.surface.min-width and at most comp.menu.surface.max-width wide — as wide as its widest item within those bounds — inset its items by comp.menu.surface.padding-block and comp.menu.surface.padding-inline, fill it with comp.menu.surface.color, round it by comp.menu.surface.shape eased by sys.shape.corner-smoothing, and clip its items to that shape.
MNU-4 WHEN the menu opens THE menu SHALL fade its surface in and grow it from the edge that faces the anchor over comp.menu.motion.open-duration with comp.menu.motion.open-easing, and WHEN it closes fade it out over comp.menu.motion.close-duration with comp.menu.motion.close-easing; WHERE motion is reduced it SHALL appear and disappear at once. WHEN it is mounted with open true THE menu SHALL draw its surface, and the surface of every submenu open in it (MNU-50), without that motion while the document has not yet been first painted, and with it when it is mounted after that.
MNU-5 WHEN the user presses Escape or the platform's back while no submenu is open, presses outside every surface of the menu and the anchor (the area of a context menu is outside, MNU-62), presses Tab or Shift+Tab at any level, or moves focus out of the menu and all of its submenus THE menu SHALL emit close once with that reason and SHALL be closed, every open submenu with it (MNU-49): through its binding or controller where open is one (SwiftUI, Flutter), and by the product setting open to false where open is a plain value (web, Compose, React Native); Tab and Shift+Tab SHALL then move focus on from the anchor as they would have from it (a context menu: from the element MNU-62 names). Whether a press outside also reaches the element under it SHALL follow the platform and be stated in its README.
MNU-6 WHEN the user activates an enabled item (an action) at any level by pointer, touch, Enter or Space THE menu SHALL emit the item's click once, then emit close and be closed as MNU-5 says, every open submenu with it, an item that is a link (MNU-53) opening its destination after that, and only for an activation that MNU-54 counts; WHEN the user activates an enabled checkbox item THE menu SHALL switch its checked and emit the item's change once with the new checked; WHEN the user activates an enabled radio item whose value is not its group's value THE menu SHALL set the group's value to the radio item's value, so that the group's other radio items become unchecked, and emit the group's change once; activating a radio item that is already checked SHALL emit no change. After a checkbox or radio item, a checked radio item included, the menu SHALL close as for an action when the activation came from pointer, touch or Enter, and SHALL stay open, at every level, when it came from Space. Activating a submenu item opens its submenu and closes nothing (MNU-43).
MNU-7 WHILE the menu is open THE menu SHALL move focus among the items of the surface that holds focus — the menu's own or one submenu's, never across levels — to the next and previous item with Down Arrow and Up Arrow, wrapping at the ends and passing through groups as if their items were the surface's, to the first and last item with Home and End, and to the next item of that surface whose label begins with the typed characters when printable characters are typed within one second of each other; disabled items SHALL be reachable but SHALL NOT activate, a submenu item SHALL take focus as any other item, and dividers, groups and group titles SHALL NOT take focus.
MNU-8 WHEN a pointer moves onto an item THE menu SHALL move focus to that item without showing the focus ring and render it hovered, so that the arrow keys continue from it, except while the pointer is on its way to an open submenu (MNU-45); WHEN the pointer leaves the surface focus SHALL stay on that item.
MNU-9 WHILE an item's disabled is true THE menu SHALL NOT emit its click or change, nor its group's change, for it and SHALL expose the disabled state to assistive technology.
MNU-10 WHILE an item is hovered, keyboard-focused or pressed THE menu SHALL render its container, label, supporting text, trailing text, icons, mark (a glyph's mark color, and a control look's control and control-mark colors) and opacity from the component tokens of its condition in the highest-precedence active state, with precedence pressed over focused over hovered; focus gained by pointer or touch SHALL NOT select the focused state.
MNU-11 WHEN an item receives keyboard focus THE menu SHALL show the focus ring inside the item's container, which the surface would otherwise clip, without animation; focus gained by pointer or touch SHALL NOT show it.
MNU-12 WHERE an item is shorter than the minimum target THE menu SHALL make its container at least sys.target.touch tall for touch input, in both densities, because stacked items cannot extend a hit area over each other, and for fine pointers at least sys.target.pointer tall WHILE density is regular and as tall as its density gives WHILE density is compact (MNU-64), the one place where a row of the menu is lower than the pointer target; a platform that cannot tell the input type SHALL use sys.target.touch. The whole row SHALL be the item's hit area.
MNU-13 WHILE an item's disabled is true THE menu SHALL render its container, label, supporting text, trailing text, icons, mark (a glyph's mark color, and a control look's control and control-mark colors) and opacity from the disabled component tokens of its condition.
MNU-14 WHEN placement names a value the menu does not define THE menu SHALL render with the default value, and in debug builds report the unknown value.
MNU-15 WHEN the menu item is pressed by pointer, touch, Enter or Space THE menu item SHALL show the pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner.
MNU-16 WHILE pressed THE menu item SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole control and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; label, supporting text, trailing text, icons, mark, opacity and shape still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.menu.motion.feedback-duration with comp.menu.motion.state-easing, fades over comp.menu.motion.state-duration with comp.menu.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced.
MNU-17 THE menu item SHALL ease its container corners by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0) and show its keyboard focus as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README.
MNU-18 THE menu SHALL draw an item from the condition it is in: destructive for an item (an action) whose destructive is true, selected for a checkbox item whose checked is true and for a radio item whose value equals its group's value, current for a link item whose current is true (MNU-58), expanded for a submenu item whose submenu is open, otherwise unselected.
MNU-19 THE menu SHALL lay out an item as one row: comp.menu.container.padding-inline.regular from each edge, the mark column and the leading icon column, each where MNU-59 keeps it, the label with the supporting text below it taking the remaining width, then the trailing column, which holds the shortcut or the trailing text, and the trailing icon column, which holds the trailing icon or, in a submenu item, the submenu indicator (MNU-42) or, in a link item that opens a new context and names no trailing icon, its mark (MNU-55), each comp.menu.container.gap.regular from the next; an empty trailing icon column SHALL keep its width in every item of the same surface so that shortcuts, trailing text and trailing icons align, each surface — the menu's and each submenu's — reserving its own, and WHEN the layout direction is right-to-left THE menu SHALL mirror the row.
MNU-20 THE menu SHALL make an item at least comp.menu.container.height.regular tall, pad its content by comp.menu.container.padding-block.regular above and below, draw the label in comp.menu.label.typescale, the supporting text in comp.menu.supporting-text.typescale and the trailing text and the shortcut's legends in comp.menu.trailing-text.typescale, and grow with its content and with enlarged text rather than clip or truncate it.
MNU-21 THE menu SHALL draw leadingIcon at comp.menu.leading-icon.size.regular and trailingIcon at comp.menu.trailing-icon.size.regular through the icon helper, with the family and fill of the item's condition (selected for a checked item and for a current link item, otherwise unselected), sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max; an empty name SHALL draw nothing.
MNU-22 WHILE a checkbox or radio item is checked and its mark is a glyph (MNU-38) THE menu SHALL draw that glyph of the system's icon set — check where the design system's choice is check, the named glyph where markIcon names one — in its mark column, centered in a box of side comp.menu.mark.size.regular, in the mark color of its condition, with sys.icon.family.selected, sys.icon.fill.selected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, and SHALL draw nothing in that column while the item is not checked; it SHALL NOT mirror the check when the layout direction is right-to-left, a glyph that markIcon names following MNU-33; THE menu SHALL draw no mark of its own (no path, shape or character of its own for the check). WHEN checked changes while the menu is open THE menu SHALL fade the glyph in or out over comp.menu.motion.state-duration with comp.menu.motion.state-easing without moving or scaling it, and at once where motion is reduced.
MNU-23 WHILE the menu's separation is divider (MNU-35) THE menu SHALL draw a line of comp.menu.divider.thickness in comp.menu.divider.color across the surface's inner width, inside comp.menu.surface.padding-inline on each side (the same inset as the rows, not the rows' own padding) between a group and an adjacent group or an adjacent item outside any group, with comp.menu.divider.space above and below it, and SHALL expose it as a separator that takes no focus; it SHALL draw no line before the first or after the last element of the surface.
MNU-24 THE menu SHALL expose the menu role, named by its anchor (a context menu as MNU-63 says); each item as a menu item, a link item included (MNU-53), each checkbox item as a menu item checkbox and each radio item as a menu item radio with its checked state, whatever its mark looks like, each with its position and the size of the surface it is in, named by label and described by supporting text, a link item that opens a new context also by its targetLabel (MNU-55), and a link item that is current with the current state (MNU-58); each submenu item as a menu item that opens a menu, with whether that menu is open (aria-haspopup with the value menu, and aria-expanded, on the web), described also by its trailing text, and each submenu's surface as a menu of its own named by its submenu item's label; shortcut SHALL be exposed as the item's keyboard shortcut in the form KBS-12 gives (aria-keyshortcuts on the web) where the platform has that property, and otherwise the text KBS-10 gives as the item's description; trailing text SHALL be exposed as the item's description only, never as a keyboard shortcut, whatever it holds; icons, the mark and the submenu indicator SHALL be hidden; each group SHALL be exposed as a group, named by its title where it has one. WHILE trigger is anchor THE menu SHALL also expose on its anchor that it opens a menu and whether the menu is open, where the platform has that property.
MNU-25 WHILE the menu is open THE menu SHALL NOT trap focus, SHALL NOT make the rest of the screen inert and SHALL draw no scrim: it is an operated overlay that is not modal.
MNU-26 WHILE an item's label is empty THE menu SHALL still render the item, and in debug builds report that it has no accessible name.
MNU-27 WHEN the colors of an item change between states THE menu SHALL animate them over comp.menu.motion.state-duration with comp.menu.motion.state-easing, and SHALL change them at once where motion is reduced.
MNU-28 WHILE a group's title is not empty THE menu SHALL draw it above the group's items in comp.menu.group-title.typescale and comp.menu.group-title.color, starting comp.menu.group-title.padding-inline.regular from the rows' start edge and ending comp.menu.group-title.padding-inline.regular from their end edge, whether or not the menu draws the mark and leading icon columns (so that, at the default values, the title starts where each row's first column starts: the mark, the leading icon or the label), with comp.menu.container.padding-block.regular above and below, wrapping and growing with enlarged text rather than clipping; WHEN the layout direction is right-to-left start and end SHALL mirror. The title SHALL take no focus and SHALL NOT be read as an item.
MNU-29 WHEN a radio item is not inside a group, or a group's value names none of its radio items THE menu SHALL draw every such radio item unchecked; WHEN two radio items of one group share a value THE menu SHALL check both while the group's value names it; WHEN a group holds another group THE menu SHALL draw the inner group's items as the outer group's, with no title or divider of the inner group's own; in debug builds it SHALL report each of these.
MNU-30 IF the icon set cannot draw a glyph the menu draws yet — the check or the named glyph of a mark, the check inside a checked box, the submenu indicator — because its font is still loading or failed to load THE menu SHALL draw nothing in the glyph's place, never the glyph's name as text, SHALL keep its column's width, SHALL still draw the box of a checkbox look and the ring of a radio look, SHALL still expose the item's checked state and whether its submenu is open, and SHALL show the glyph once the icon set can draw it, without the fade of MNU-22.
MNU-31 WHILE an item's shortcut is not empty THE menu SHALL draw it in the item's trailing column with the keyboard shortcut component, hosted as KBS-13 says: at size small, in the form comp.menu.shortcut.variant names, with convention auto, and disabled while the item is disabled; its legends and separators SHALL take comp.menu.trailing-text.typescale and the trailing-text color of the item's condition and state, and its caps the keyboard shortcut's own tokens. IF an item has both a shortcut and trailing text THE menu SHALL draw the shortcut, not the trailing text, and in debug builds report the item.
MNU-32 THE menu SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the menu is shown THE menu SHALL draw itself for the new direction at once, without a motion of its own, keeping its open state and its focus, the surface placed again by the new direction (MNU-2).
MNU-33 WHERE the layout direction is right-to-left THE menu SHALL draw an item's leading icon and trailing icon, the glyph a markIcon names and the submenu indicator mirrored left to right exactly when the system's icon set marks that glyph's name as mirrored or the product forces mirroring for that use, and unmirrored when the product forbids it: the icon helper mirrors the glyph inside its own box, about the box's vertical centre line, changing neither its place, size, color and fill nor the order MNU-19 gives. THE menu SHALL NOT mirror a glyph itself, and SHALL NOT mirror content the product gives as an image or a view of its own rather than by a glyph's name. The check — the check of a mark and the check inside a checked box — is never mirrored (MNU-22, MNU-39).
MNU-34 THE menu SHALL lay out an item's label, supporting text and trailing text and a group's title as text of its own direction, isolated from the text around it: each takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right word, a number with its sign or unit, a telephone number and an address keep their own order in a right-to-left layout and reorder nothing beside them; THE menu SHALL still place each and align its lines by the layout direction, not by the text's own.
MNU-35 THE menu SHALL separate a group from an adjacent group or an adjacent item outside any group as its separation names: WHILE separation is auto, as comp.menu.separation names, which follows sys.behavior.separation unless a design system sets it apart; WHILE separation is divider or gap, as that value names, whatever the tokens say; divider draws the line of MNU-23 and gap leaves the space of MNU-36. Every submenu SHALL separate its groups as its menu does. WHEN separation, or the token it follows, changes THE menu SHALL draw the other form at once, without animation, keeping its open state and its focus; WHEN separation names a value the menu does not define THE menu SHALL treat it as auto and in debug builds report the unknown value. A platform that cannot draw a choice SHALL fall back to the first value, divider, and state that in its README.
MNU-36 WHILE the menu's separation is gap (MNU-35) THE menu SHALL draw each group, and each run of adjacent items outside any group, as a tile of its own: a sheet of comp.menu.surface.color with the corners comp.menu.surface.shape eased by sys.shape.corner-smoothing and the elevation comp.menu.surface.elevation, its ring drawn inside the tile's edge, holding its rows inside comp.menu.surface.padding-block and comp.menu.surface.padding-inline as the surface holds them; it SHALL leave comp.menu.group.gap of empty space between two tiles, through which what is under the menu shows, and SHALL draw no sheet, edge or elevation of its own around the tiles. It SHALL draw no line between tiles and SHALL expose no separator, the groups staying exposed as groups (MNU-24) and a tile being nothing to assistive technology; it SHALL leave no space before the first or after the last tile, and a group's title SHALL keep its own padding inside its tile (MNU-28). The tiles of a menu taller than the room it has SHALL scroll together inside the menu's bounds (MNU-2), and every submenu SHALL draw its tiles the same way: the gap tells the parts of a menu apart by cutting its sheet, as the gap of a button group or of an accordion makes separate shapes, never by empty space on one sheet.
MNU-37 WHILE a checkbox or radio item is checked THE menu SHALL fill its row, within the corners comp.menu.container.shape gives, with the container color of the selected condition in its state (comp.menu.selected.container.color at rest), SHALL draw that fill over its surface (comp.menu.surface.color) and under the row's content, so that a container color that is not opaque lets the surface show through it and nothing else, and SHALL draw its label, supporting text, trailing text and icons in that condition's colors; checked rows next to each other SHALL each keep their own corners and SHALL NOT be joined into one shape. THE menu SHALL NOT show a choice by the fill alone: a checked item always draws its mark (MNU-38), and a design system whose selected container color is none still marks it. The mark is what tells a checked item; the fill is a second cue a design system may give or withhold, through comp.menu.selected.container.color for each contrast scheme, and the menu SHALL read that token in the scheme it is drawn in and SHALL NOT decide the fill by the scheme itself. The default system gives it at medium and high contrast, where a person has asked for stronger cues, and withholds it at standard contrast, so that there the one filled row of a menu is the row the person is on (MNU-58).
MNU-38 THE menu SHALL draw the mark of a checkbox or radio item as the first of these that applies: the glyph the item's markIcon names; the glyph its group's markIcon names; otherwise what the design system chooses for the item's kind — comp.menu.checkbox-item.mark for a checkbox item (checkbox: the checkbox look of MNU-39; check: the icon set's check, as MNU-22 draws it) and comp.menu.radio-item.mark for a radio item (radio: the radio look of MNU-40; check: the same check). Whatever the mark is THE menu SHALL place it in the mark column, the first column of the row (MNU-19), before the leading icon, centered in a box of side comp.menu.mark.size.regular, SHALL hide it from assistive technology, SHALL NOT make it a control of its own (it takes no focus and no press and has no role apart from its item's), and SHALL keep exposing the item as a menu item checkbox or a menu item radio with its checked state (MNU-24). A platform that cannot draw a choice SHALL fall back to the first value and state that in its README.
MNU-39 WHILE a checkbox item's mark is the checkbox look (MNU-38) THE menu SHALL draw, whether the item is checked or not, a box of side comp.menu.control.box.size with corners comp.menu.control.box.shape eased by sys.shape.corner-smoothing: WHILE the item is not checked the box SHALL be empty, with an outline of comp.menu.control.outline.width inside its edge in the control color of its condition and state (comp.menu.unselected.control.color at rest); WHILE it is checked the box SHALL be filled in the control color (comp.menu.selected.control.color at rest), outline and fill being one shape with no seam between them, with the glyph check of the system's icon set centered on it at comp.menu.control.mark.size in the control-mark color of its condition and state, drawn with sys.icon.family.selected, sys.icon.fill.selected, the weight comp.menu.control.mark.weight (not sys.icon.weight: the check in the box is a control's own mark, as the checkbox's is, CHK-12) and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, not clipped by the box and never mirrored; the weight SHALL change the stroke of the glyph only. THE menu SHALL draw no round state layer behind the box: the row is the item's container.
MNU-40 WHILE a radio item's mark is the radio look (MNU-38) THE menu SHALL draw, whether the item is checked or not, a ring of outer diameter comp.menu.control.ring.size in the control color of its condition and state, the outer diameter the same in both conditions: WHILE the item is not checked the ring SHALL be stroked at comp.menu.control.ring.width inside that diameter; WHILE it is checked it SHALL be stroked at comp.menu.control.ring.selected-width-share of the ring's radius, or at comp.menu.control.ring.width where that is wider, the stroke growing inward, and the area inside the ring's inner edge, less comp.menu.control.fill.inset all round, SHALL be filled in the same control color at comp.menu.control.fill.opacity, ring and fill being one shape where they touch; a fill whose diameter would be zero or less SHALL NOT be drawn. THE menu SHALL draw no dot and no round state layer of its own.
MNU-41 WHEN checked changes while the menu is open and the item's mark is a control look THE menu SHALL move the box's outline and fill, the check inside it, and the ring's stroke and fill from the old condition to the new over comp.menu.motion.state-duration with comp.menu.motion.state-easing — the check fading, the ring's stroke growing or thinning inward — without moving or resizing the box or the ring's outer diameter, and the row's fill SHALL change with it (MNU-27); WHERE motion is reduced it SHALL draw the new condition whole at once.
MNU-42 THE menu SHALL draw a submenu item as an item's row (MNU-19, MNU-20) with the submenu indicator in its trailing icon column: the glyph chevron_right of the system's icon set at comp.menu.trailing-icon.size.regular in the trailing-icon color of the item's condition and state, drawn through the icon helper with sys.icon.family.unselected, sys.icon.fill.unselected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max; its trailing text, where it has one, SHALL be drawn in the trailing column before the indicator, and a submenu item SHALL have no shortcut, no trailing icon of the product's and no mark. THE menu SHALL draw no chevron of its own (no path, shape or character of its own).
MNU-43 WHEN the user presses Enter or Space on an enabled submenu item, presses the arrow key that points to the inline end on it (Right Arrow in a left-to-right layout, Left Arrow in a right-to-left one), presses it with a pointer or taps it THE menu SHALL open that item's submenu without delay, close any other open submenu of the same surface, move focus to the submenu's first enabled item (its first item where none is enabled), showing the focus ring only when the input was a key, set the item's open to true and emit the item's toggle once with true; it SHALL NOT emit close and SHALL close nothing else. WHEN the submenu is already open the same input SHALL move focus to its first enabled item and emit nothing. WHEN the product sets a submenu item's open to true while the surface that holds the item is shown THE menu SHALL open the submenu without moving focus and without emitting toggle.
MNU-44 WHEN a pointer that can hover rests on an enabled submenu item for comp.menu.motion.submenu-delay THE menu SHALL open that item's submenu, closing any other open submenu of the same surface, and SHALL leave focus on the submenu item (MNU-8) until the pointer enters the submenu or a key moves it; a pointer that leaves the item sooner SHALL open nothing. Touch has no hover: a tap opens the submenu (MNU-43). WHERE motion is reduced the delay SHALL still apply: it reads the pointer's intent and is not an animation.
MNU-45 WHILE a submenu is open and the pointer moves from its submenu item towards it THE menu SHALL keep the submenu open and SHALL NOT move hover or focus to the items the pointer crosses on the way (MNU-8): the pointer is on its way while it keeps moving inside the triangle whose corners are the pointer's last position on the submenu item and the two corners of the submenu's surface on the side that faces the item. WHEN the pointer leaves that triangle without entering the submenu, or rests inside it for comp.menu.motion.submenu-delay THE menu SHALL treat the item under the pointer as MNU-8 says, which closes the submenu when that item is another one (MNU-48). The triangle SHALL be taken anew each time the pointer leaves the submenu item and SHALL follow the side the submenu is placed on (MNU-46), so that it mirrors in a right-to-left layout and when the submenu is placed on the other side.
MNU-46 THE menu SHALL place a submenu's surface beside the surface that holds its submenu item, comp.menu.submenu.offset from that surface's inline-end edge (to the right in a left-to-right layout, to the left in a right-to-left one), with the block-start edge of its first row level with the block-start edge of the submenu item's row, and SHALL draw it above the surface it opened from; WHERE that side has less room than the surface needs within comp.menu.surface.viewport-margin of the edge of the visible part of the window, above the on-screen keyboard while one is shown, and the inline-start side has more THE menu SHALL place it on the inline-start side instead, the same distance from that edge, and a submenu opened from a surface placed that way SHALL try that side first; it SHALL shift the surface along the block axis to stay within that margin, SHALL limit its height to the least of the height its items need, the most the product's own style gives the submenu's surface (MNU-2) and the room left and scroll its items beyond it, and SHALL keep it in place against its submenu item while that item moves. WHEN the submenu item is scrolled out of the visible part of its surface THE menu SHALL close the submenu (MNU-48).
MNU-47 WHERE neither side of the surface that holds a submenu item has room for the submenu's surface at comp.menu.surface.min-width within comp.menu.surface.viewport-margin THE menu SHALL still present the submenu as a surface of its own, laid over the surface it opened from, on the side with more room and as far towards that side as the margin allows, narrowed down to comp.menu.surface.min-width before it covers more, and SHALL leave at least sys.target.touch of the covered surface's inline size uncovered on its other side wherever the visible part of the window is wide enough for both; THE menu SHALL NOT replace the items of a surface with its submenu's at any window size. WHILE a submenu covers part of the surface it opened from, a press on the uncovered part of that surface SHALL close the submenu as MNU-48 says and SHALL NOT activate the item under the press, so that a person without a keyboard or a back button can go back one level; keys and focus SHALL behave as in a wide window.
MNU-48 WHEN focus is in a submenu and the user presses the arrow key that points to the inline start (Left Arrow in a left-to-right layout, Right Arrow in a right-to-left one), Escape or the platform's back THE menu SHALL close that submenu and every submenu opened from it, and nothing else, return focus to its submenu item, showing the focus ring only when the input was a key, set the item's open to false and emit the item's toggle once with false, without emitting close. WHEN focus or hover moves to another item of the surface that holds the submenu item (MNU-7, MNU-8), WHEN the submenu item becomes disabled or is removed, or WHEN the product sets its open to false THE menu SHALL close the submenu the same way — the product's own change emitting no toggle — and focus that was inside it SHALL move to the submenu item, or to the item that took focus. Escape and the platform's back SHALL close one level for each press, and the menu itself only when no submenu is open (MNU-5). THE menu SHALL keep at most one submenu open in each surface.
MNU-49 WHEN the menu is closed — for a reason MNU-5 or MNU-6 names, at whatever level it arose, or because the product sets open to false — THE menu SHALL close every open submenu with it at once, emit close once and never once for each level, set each closed submenu item's open to false without emitting its toggle, and return focus to the anchor as MNU-1 says (a context menu: to the element MNU-62 names), not to a submenu item; a press inside any surface of the menu SHALL NOT count as a press outside, and focus moving between the menu's surfaces SHALL NOT count as focus leaving it. The surfaces SHALL fade out together as MNU-4 says.
MNU-50 WHILE a submenu is open THE menu SHALL draw its submenu item in the expanded condition (MNU-18) in whatever interaction state it is in, so that the path from the menu to the surface in use stays marked while the pointer and focus are elsewhere, drawing that condition's container color over its surface as MNU-37 draws a checked row's, and SHALL draw the submenu's surface as the menu's own (MNU-3) from the same tokens, its items following every behaviour of the menu's items, with the open and close motion of MNU-4 growing from the edge that faces its submenu item.
MNU-51 WHEN the user presses the arrow key that points to the inline end on an item that is not a submenu item, or the arrow key that points to the inline start in the menu's own surface THE menu SHALL do nothing and SHALL NOT let the key scroll or act on what is behind the menu. WHILE a submenu item's disabled is true THE menu SHALL NOT open its submenu by any input, SHALL close it if it is open (MNU-48), and SHALL still expose that the item opens a menu, with the disabled state (MNU-9).
MNU-52 THE menu SHALL let a submenu hold items of every kind, groups and further submenus included, to any depth, each level behaving as MNU-42 to MNU-51 say, and SHALL keep the radio items of a submenu apart from those of every other surface: a group's value reaches only the radio items that are its own. WHEN a submenu item lies more than two levels of submenu below the menu, or WHEN a submenu holds no item THE menu SHALL still render what it can — a submenu with no item draws its row and its indicator, takes focus and opens nothing — and in debug builds report each. WHILE open is true on a submenu item whose own surface is not shown THE menu SHALL show nothing for it until that surface is shown.
MNU-53 WHILE an item's href is not empty THE menu SHALL make the item a link item: the platform's own link to href (the web's anchor element; SwiftUI's Link; on Compose, Flutter and React Native an element with the platform's link semantics whose address is opened by the platform's opener), as BTN-22 says for a link button, so that whatever the platform gives a link — on the web its context menu, opening in a new tab or window, copying and dragging its address, and the address shown before it is followed — is the platform's, and not the menu's to draw or to take over. WHERE the platform has a menu item role (the web) THE menu SHALL expose a link item with that role, on the link itself, and not with the link role: a menu's own role, an item's position in it and the keys of MNU-7 hold only while everything the menu holds is a menu item, and a menu of links is operated as a menu; WHERE the platform's menu items are buttons and it has no such role THE link item SHALL carry the platform's link role or trait in place of the button's. Everything this spec says of an item (an action) other than what MNU-54 to MNU-57 say — its row and columns, its conditions, destructive included, its states, focus ring, minimum height, press answer, corners and motion, its place in the order of the arrow keys, Home, End and typed characters, and focus following the pointer (MNU-7, MNU-8, MNU-10 to MNU-21, MNU-27, MNU-31) — SHALL hold for a link item from the same tokens, with no look of its own for a link and none for a destination already visited; a link item SHALL NOT be a Tab stop, as no item of a menu is.
MNU-54 WHEN the user activates an enabled link item at any level as an item of a menu is activated — by a click with the primary button and no modifier key, a touch, Enter or Space with no modifier key, or the default action of assistive technology — THE menu SHALL emit the item's click exactly once, then emit close with the reason item and be closed as MNU-6 says for an action, and then open href where target names, unless the product cancelled that click, in which case it SHALL still close and SHALL open nothing: the product's own router has taken the navigation. It SHALL show the pressed state as MNU-15 and MNU-16 say and SHALL NOT hold the closing or the navigation back for sys.motion.duration.press-hold. Space SHALL activate a link item exactly as Enter does and SHALL scroll nothing: a link item is exposed and operated as a menu item (MNU-53), nothing in its look tells it from the action in the next row, Space activates every other row of the menu, and behind an open menu Space has no page to scroll (MNU-51). For every other activation the platform gives a link — on the web a click or Enter with a modifier key, a middle click and the link's context menu, on Apple platforms a long press that previews — THE menu SHALL leave the link to the platform, SHALL prevent, replace or imitate none of it, SHALL NOT emit close and SHALL stay open at every level with focus where it was, so that a person can open several of a menu's links in other tabs one after another; what MNU-5 says of a press outside and of focus leaving the menu still holds.
MNU-55 WHILE an item is a link item and its target is new THE menu SHALL open href in a new context — on the web a new tab or window that is given no handle to the page that opened it; on the other platforms outside the screen the person is on, as the platform's opener decides — and, WHILE the item's trailingIcon is empty, SHALL draw the glyph open_in_new of the system's icon set in the item's trailing icon column, as MNU-21 draws trailingIcon: at comp.menu.trailing-icon.size.regular, in the trailing-icon color of the item's condition and state (comp.menu.unselected.trailing-icon.color at rest, comp.menu.destructive.trailing-icon.color for a destructive item), through the icon helper, which mirrors it in a right-to-left layout because the icon set marks its name (MNU-33), hidden from assistive technology, and held to MNU-30 while the icon set cannot draw it. The mark SHALL reserve the trailing icon column in its surface as a trailing icon does (MNU-19), and the item's shortcut or trailing text SHALL keep its own column before it: the mark stands where a glyph of the product's would, not where the keys are written. WHILE trailingIcon names a glyph THE menu SHALL draw that glyph and no mark of its own: the product has chosen what that place shows. Whether a mark is drawn or not THE menu SHALL expose targetLabel as the end of the item's description, after its supporting text and whatever else MNU-24 puts there, and WHILE targetLabel is empty SHALL still render and in debug builds report that a link that opens a new context does not say so. WHILE target is same THE menu SHALL draw no mark and SHALL ignore targetLabel.
MNU-56 WHILE a link item's disabled is true THE menu SHALL NOT make it a link that can be followed: it SHALL render it as MNU-13 says, the mark of MNU-55 with it, SHALL expose it as MNU-53 exposes a link item, with the disabled state and no destination, SHALL keep it reachable by the arrow keys as MNU-7 keeps any disabled item, SHALL NOT emit its click or close for it, and SHALL open nothing by any means the platform gives a link (on the web the anchor carries no href, so there is no context menu entry, no drag and no new tab for it).
MNU-57 THE menu SHALL give a destination to the item kind alone. A checkbox item and a radio item hold a state and a submenu item opens another level, so none of them has href, target or targetLabel: a control that holds a state, or a second action, is not a destination; WHERE a platform lets one be written on them (an attribute of the web's element, a field of React Native's item object) THE menu SHALL ignore it, keep the item what it is, and in debug builds report it. WHEN a link item's target names a value the menu does not define THE menu SHALL treat it as same, and in debug builds report the unknown value. IF an item's href is only white space, or names a script to run rather than a place to go (the web's javascript: scheme), or cannot be read as an address by the platform, THE menu SHALL make the item an action without a destination, and in the last two cases report it in debug builds, as BTN-26 says for a button. WHILE an item's href is empty THE menu SHALL ignore its target and targetLabel.
MNU-58 WHILE a link item's current is true THE menu SHALL expose it as the current item of its set (on the web aria-current with the value page, on the item's link; on a platform without that state, the selected state) and SHALL draw its row from the current condition: a fill as MNU-37 lays one, in comp.menu.current.container.color at rest, and that condition's colors for its label, supporting text, trailing text and icons, its icons in the selected family and fill, in every state and in every contrast scheme; it SHALL draw no mark in the mark column, keep its label's own weight, and SHALL keep it a link item in every other respect (MNU-53 to MNU-56). The current destination is where the person is, not a choice the menu holds: it carries no check, and it is told from its neighbours by the fill the menu draws for the row the person is on, as a navigation drawer marks its current destination, while assistive technology is told by the state. Its fill is its own and not a checked row's: a checked row has its mark and may go without a fill (MNU-37), the current row has only its fill, so a design system that withholds the one keeps the other. WHERE the platform draws with a person's own forced colors, which remove the fill, THE menu SHALL draw a line of sys.border.thick inside the current item's row in the platform's link color. THE menu SHALL NOT set current itself on any activation: where the product is, is the product's to say. WHILE an item's href is empty THE menu SHALL ignore its current and in debug builds report it.
MNU-59 THE menu SHALL decide the mark column and the leading icon column for each section of a surface — a group, with the items of a group inside it (MNU-29), or a run of adjacent items outside any group: the parts MNU-36 draws as tiles — and each of the two columns by itself. WHILE iconSpace is auto a row SHALL keep a column, empty where it draws nothing in it, WHERE an item of its section draws in it — a checkbox or radio item its mark, whether checked or not and whatever the mark is (MNU-38), an item its leading icon — so that the labels of a section start on one line, and a section where no item draws in a column SHALL keep no room for it, its labels starting that much nearer the row's start; WHILE iconSpace is always every row SHALL keep both columns, whether or not any item of the surface draws in them; WHILE iconSpace is never a row SHALL have a column only where it draws in it itself. The room kept is the column's box (comp.menu.mark.size.regular for the mark, comp.menu.leading-icon.size.regular for the icon) and comp.menu.container.gap.regular after it, draws nothing and is nothing to assistive technology. A submenu item has no mark and so draws nothing in the mark column (MNU-42). Each surface SHALL be decided by itself, every submenu with the menu's iconSpace; a group's title SHALL keep its own place whatever the columns of its section are (MNU-28). WHEN an item is added or removed, or an item's kind or leadingIcon changes, THE menu SHALL decide the item's section again; WHEN iconSpace names a value the menu does not define THE menu SHALL treat it as auto and in debug builds report the unknown value.
MNU-60 WHILE trigger is context, WHEN the person asks for a context menu in the anchor's area — a secondary click (a right click, a Control-click, a two-finger click on a trackpad), the context-menu command of assistive technology, a press of a touch or a pen held within the platform's touch slop (8 points where the platform names none) for the platform's long-press duration (500 ms where the platform names none), or Shift+F10 or the ContextMenu key with no other modifier while focus is on the area or on an element inside it — THE menu SHALL emit contextRequest once, with the point in the viewport's coordinates (where the pointer is, where the press began, or the block-end, inline-start corner of the element that has focus), the element under the pointer or with focus, and how it was asked (pointer, long-press or keyboard), and SHALL NOT open itself: the product sets open to true (MNU-1). Unless the product cancels contextRequest, THE menu SHALL keep the platform's own context menu from answering the request, and after a long press SHALL keep the release of that press from activating what is under it; a cancelled request SHALL be left to the platform. A request that an element inside the area has already taken (on the web, a contextmenu event whose default is prevented) and one made inside the menu's own surfaces SHALL NOT be answered. WHILE trigger is anchor THE menu SHALL answer none of these. WHEN trigger names a value the menu does not define THE menu SHALL treat it as anchor and in debug builds report the unknown value.
MNU-61 WHILE trigger is context THE menu SHALL place its surface at the point of the last contextRequest, with no offset, as placement names it from the point (bottom-start: the surface's block-start, inline-start corner on the point; bottom-end: its block-start, inline-end corner; top-start and top-end: its block-end corners); WHERE the side below or above the point has less room than the surface needs within comp.menu.surface.viewport-margin of the edge of the visible part of the window, above the on-screen keyboard while one is shown, and the other side has more THE menu SHALL flip to the other side of the point, and WHERE the side before or after the point along the line has less room than the surface's width and the other has more SHALL flip along the line as well, each axis by itself, so that a surface asked for near a corner of the window opens away from that corner with a corner of its own on the point; it SHALL then shift to stay within that margin, limit its height as MNU-2 says and scroll its items beyond it, and keep the point where it is in the area while the area moves. FOR a request by the keyboard THE menu SHALL place the surface against the element that had focus as MNU-2 places it against an anchor, offset included. WHEN open becomes true with no request since the menu was last closed THE menu SHALL place the surface against the area as MNU-2 says. The surface SHALL grow from its edge that faces the point or the element (MNU-4).
MNU-62 WHILE trigger is context THE menu SHALL behave as every other menu does once it is open — the same items, focus on opening (MNU-1), keys, typeahead, submenus, activation and close reasons — and WHEN it closes SHALL return focus, where focus was inside it, to the element that had focus when the request was made (the element a keyboard request was made on), and leave focus where the platform puts it where no element had it; Tab and Shift+Tab SHALL move focus on from that element. A press anywhere outside the menu's surfaces, the area included, SHALL count as a press outside (MNU-5); a secondary click or a long press elsewhere while the menu is open SHALL therefore close it and, in the area, request it again at the new point.
MNU-63 WHILE trigger is context THE menu SHALL NOT expose on its area that it opens a menu, whether it is open or which element it is (no aria-haspopup, aria-expanded or aria-controls on the web), because the area is not a button: a context menu is found by the platform's own convention — a secondary click, a long press, Shift+F10, the ContextMenu key, the context-menu command of assistive technology. THE menu SHALL NOT be named by its area; it SHALL take the name the product gives it (aria-label on the web), and have none where the product gives none, as the platform's own context menus have none.
MNU-64 THE menu SHALL draw the rows of every surface at its density. WHILE density is regular each row SHALL take the regular value of every token of the density axis this spec names (comp.menu.container.height.regular, comp.menu.container.padding-block.regular, comp.menu.container.padding-inline.regular, comp.menu.container.gap.regular, comp.menu.leading-icon.size.regular, comp.menu.trailing-icon.size.regular, comp.menu.mark.size.regular, comp.menu.group-title.padding-inline.regular); WHILE density is compact it SHALL take the compact value of each in its place (comp.menu.container.height.compact, comp.menu.container.padding-block.compact, comp.menu.container.padding-inline.compact, comp.menu.container.gap.compact, comp.menu.leading-icon.size.compact, comp.menu.trailing-icon.size.compact, comp.menu.mark.size.compact, comp.menu.group-title.padding-inline.compact), so that a compact row is lower, set in less from its edges and draws smaller glyphs, the room a row keeps for an empty column (MNU-59) shrinking with them. The text styles, the surface and its paddings, the dividers, the group gap and the control of a checkbox or radio item SHALL be the same in both. Every submenu SHALL take the menu's density. WHEN density names a value the active design system does not have THE menu SHALL draw the default density's rows and in debug builds report the unknown value; a density a design system added SHALL be drawn from that density's tokens, as the core values are. WHEN density changes while the menu is open THE menu SHALL draw the new rows at once, without a motion, keeping focus where it is.
MNU-65 WHEN the element the open menu is placed against — the anchor; for a context menu its area, or the element a keyboard request was made on (MNU-61) — is no longer drawn: taken out of the page, given no box (on the web display none on it or on an ancestor), made invisible (visibility hidden) or skipped by its platform's content visibility (on the web content-visibility hidden, or auto while it is off screen), THE menu SHALL emit close once with the reason anchor-hidden and be closed as MNU-5 says, every open submenu with it (MNU-49), and SHALL NOT place its surface against that element while it is not drawn, because a surface with nothing beside it no longer says what it belongs to. WHEN it closes with focus inside it THE menu SHALL return focus as MNU-49 says and, where that element cannot take focus, SHALL NOT keep focus inside its closed surfaces. WHEN the element is drawn again while the menu is still open THE menu SHALL follow it again, and emit close again the next time it is no longer drawn.
MNU-66 WHERE the platform has ARIA attributes (the web), WHILE an item is a link item its role is carried by its anchor (MNU-53), and WHEN the product sets an aria- attribute on its host, by the attribute or by the ARIA property that reflects it, THE menu SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on that anchor — an id reference by the elements it names in the host's tree or a tree around it, a description after its own — except those the item owns (aria-disabled, aria-current, aria-keyshortcuts), which it SHALL move, not expose and report in debug builds; its label is its name over a name given so, and while label is empty a name given so is its name. WHEN the item stops being a link item THE menu SHALL give back to its host what it moved, as it was written, and WHEN it becomes one SHALL move what the host carries. WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE menu SHALL take it off the anchor.
MNU-67 WHERE the platform has ARIA attributes (the web), the hosts of the menu, of an item that is not a link item (a checkbox item, a radio item and a submenu item included) and of a group carry their roles themselves (MNU-24), so what the product writes on them is not moved, but for a name: WHILE an item's or a group's label, or the anchor that names a menu, is not empty THE menu SHALL hold the product's aria-label and aria-labelledby on that host as their data-aria- twins, so that its own is the whole name although the platform gives aria-labelledby the precedence, and WHEN it is emptied SHALL give them back as they were written; WHILE it is empty a name the product gives so is the name and an item SHALL NOT be reported as having none in debug builds (MNU-26) — a context menu is named so (MNU-63). WHILE a menu is closed, or a group is in a group (MNU-29), it exposes nothing, and THE menu SHALL hold them as well, giving them back when it is exposed again. WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it to null, THE menu SHALL take it away.
MNU-68 WHEN a surface of the menu is shown — the menu's own as it opens, a submenu's as its item opens it — THE menu SHALL show that surface's list from its start: a list taller than the surface (MNU-2, MNU-46) SHALL open scrolled to its top, so that its first row, its first group's title included, is in view, and WHERE the item that takes focus (MNU-1, MNU-43, or the last enabled item a menubar opens its menu on, MBR-9) is not whole in view there, scrolled just far enough to bring that item whole into view and no further. WHEN focus moves to an item by a key or by the menu's own doing (MNU-7, MNU-48) THE menu SHALL scroll the list that holds it just far enough to bring the item whole into view, and not at all where it is whole in view already — the first item of the list together with the list's start, the last together with its end, and the first item of a titled group together with that title (MNU-28); focus that follows the pointer (MNU-8) SHALL scroll nothing. Whether the item is in view SHALL be judged as the surface stands at rest: while its opening motion runs (MNU-4) the list SHALL keep its scroll position until the surface stands at rest, and then bring the item that has focus into view as this sentence says, so that no frame of the motion moves the list.
MNU-69 THE menu SHALL break the lines of an item's label, supporting text and trailing text and of a group's title by the rules of the text's language: a line of Japanese or Chinese SHALL neither begin with closing punctuation, a small kana, the prolonged sound mark (ー) or an iteration mark nor end with opening punctuation (the strict rules of line breaking that JIS X 4051 and the W3C's requirements for Japanese text layout set out), whether or not the product marks the text's language; and WHERE the platform finds the phrases of Japanese text (on the web, word-break: auto-phrase, which reads the language the product marks), a line of Japanese SHALL break between two phrases rather than inside one. A phrase or a word longer than its line SHALL still break rather than overflow (MNU-20).

Correspondencia de nombres por plataforma

Paridad no significa API idénticas. Cada plataforma expresa el mismo contrato con su propio estilo.

Especificación Web SwiftUI Jetpack Compose Flutter React Native
Componente tanrei-menu TanreiMenu TanreiMenu TanreiMenuAnchor Menu
open · Propiedad open isPresented expanded controller visible
placement · Propiedad placement placement placement placement placement
trigger · Propiedad trigger trigger trigger trigger trigger
separation · Propiedad separation separation separation separation separation
iconSpace · Propiedad iconSpace iconSpace iconSpace iconSpace iconSpace
density · Propiedad density density density density density
close · Evento close isPresented onDismissRequest onClose onDismiss
contextRequest · Evento contextrequest onContextRequest onContextRequest onContextRequest onContextRequest
Notas tanrei-menu (open, placement, separation, and anchor: the id of the anchor element, as the HTML anchor attribute) holds tanrei-menu-item, tanrei-menu-checkbox-item, tanrei-menu-radio-item, tanrei-submenu and tanrei-menu-group children; item attributes are kebab-case (supporting-text, leading-icon, trailing-icon, trailing-text, mark-icon). click, change and toggle fire on the item or group and bubble, so one listener on the menu can take them all. The surface is a manual popover in the top layer, placed with CSS anchor positioning and a script fallback where the browser has none; each submenu's surface is a popover of its own, placed against its tanrei-submenu the same way. The menu implements its own light dismiss so that it can emit close with a reason, and treats every surface of its tree as inside. A press outside also reaches the element under it. It sets aria-haspopup="menu", aria-controls and aria-expanded on the anchor element, and the same three on each submenu item's row. The inline attribute, for tests, draws the open surface in place, with each open submenu beside it (MNU-19). The mark is the mark part: the box or the ring of a control look, drawn by the menu's own styles from --tanrei-comp-menu-control-*, or the icon helper's glyph (MNU-22, MNU-38). The submenu indicator is the icon helper's chevron_right in the submenu-indicator part. A group's title is inset by --tanrei-comp-menu-group-title-padding-inline-regular from the rows' edges (MNU-28). The link item is not built on this platform yet: MNU-53 to MNU-57 and what MNU-6, MNU-19 and MNU-24 say of it, with the fixtures links, links-rtl and links-trailing. A link item's current writes aria-current="page" on its anchor and draws the row from the current condition (MNU-58). Under gap each group, and each run of items outside a group, is a .tile (part tile) of its own inside the scroll box; under divider the tiles are boxes with no look (MNU-36). trigger is the trigger attribute; with context, anchor names the area. contextRequest is contextrequest, a cancelable event whose detail is { x, y, element, input }: client coordinates, the element under the pointer or with focus, and pointer, long-press or keyboard. The menu listens on the area for contextmenu (a secondary click, Android's long press, the context-menu command of assistive technology) and cancels it unless the product cancels the request; for a touch or a pen held still, which WebKit on iOS answers with no contextmenu; and for Shift+F10 and the ContextMenu key on keydown, whose own contextmenu it then cancels. After a long press the release of that press, and its click, go no further. On iOS a long press may also select text: an area whose text is not to be selected says so in its own styles (user-select, -webkit-touch-callout). A modifier on the anchor, .tanreiMenu(isPresented:placement:) { … }, as .popover, presented with presentationCompactAdaptation(.popover) so it stays a popover on iPhone; a press outside does not reach what is under it. The content builder takes the item views. disabled follows .disabled(_:). Closing writes false to isPresented, which is close. SwiftUI's own Menu is not used: its items cannot be tokenised. Ships the previous revision today (the check stroked as a path in a 16 box, a group's title starting where the labels start). Not built on this platform yet, among the rest of this spec: the submenu item kind and everything about it (MNU-42 to MNU-52), separation (MNU-35, MNU-36), the checkbox and radio looks of the mark and markIcon (MNU-38 to MNU-41) and their 17 fixtures; the selected fill and colors arrive through the tokens, the fill of a checked row with them: none at standard contrast in the default system, as MNU-37 says. Reading comp.menu.separation, comp.menu.checkbox-item.mark and comp.menu.radio-item.mark as typed choices needs this platform's token output to write a component's choice as a typed enumeration, which it does not yet. Nor is the link item: MNU-53 to MNU-57 and what MNU-6, MNU-19 and MNU-24 say of it, with the fixtures links, links-rtl and links-trailing. Lagging behind MNU-58 (a link item's current, drawn with the selected fill) and MNU-36 (the groups as separate tiles under gap): it has neither yet. Not built yet: iconSpace (MNU-59) — this platform still keeps the mark and leading icon columns in every row of a surface where any item draws in them. Not built yet: the context trigger (MNU-60 to MNU-63) — this platform opens the menu from an anchor only. Compose idiom, as DropdownMenu: TanreiMenu(expanded, onDismissRequest, placement) { TanreiMenuItem(text, onClick, …); TanreiMenuGroup { … } } inside the anchor's Box, shown in a Popup; the item composables are called in its content lambda. The caller closes it, so close is onDismissRequest and it carries the reason; the caller also keeps each checkbox item's checked (state hoisting). A press outside does not reach what is under it. enabled is the inverse flag. Ships the previous revision today (the check stroked as a path in a 16 box, a group's title starting where the labels start). Not built on this platform yet, among the rest of this spec: the submenu item kind and everything about it (MNU-42 to MNU-52), separation (MNU-35, MNU-36), the checkbox and radio looks of the mark and markIcon (MNU-38 to MNU-41) and their 17 fixtures; the selected fill and colors arrive through the tokens, the fill of a checked row with them: none at standard contrast in the default system, as MNU-37 says. Reading comp.menu.separation, comp.menu.checkbox-item.mark and comp.menu.radio-item.mark as typed choices needs this platform's token output to write a component's choice as a typed enumeration, which it does not yet. Nor is the link item: MNU-53 to MNU-57 and what MNU-6, MNU-19 and MNU-24 say of it, with the fixtures links, links-rtl and links-trailing. Lagging behind MNU-58 (a link item's current, drawn with the selected fill) and MNU-36 (the groups as separate tiles under gap): it has neither yet. Not built yet: iconSpace (MNU-59) — this platform still keeps the mark and leading icon columns in every row of a surface where any item draws in them. Not built yet: the context trigger (MNU-60 to MNU-63) — this platform opens the menu from an anchor only. Flutter idiom, as MenuAnchor: TanreiMenuAnchor(controller:, placement:, menuChildren: […], builder:) shown in the Overlay; the item widgets go in menuChildren. MenuController.open() and .close() are open, onClose is close. A press outside also reaches what is under it (consumeOutsideTap: false). Ships the previous revision today (the check stroked as a path in a 16 box, a group's title starting where the labels start). Not built on this platform yet, among the rest of this spec: the submenu item kind and everything about it (MNU-42 to MNU-52), separation (MNU-35, MNU-36), the checkbox and radio looks of the mark and markIcon (MNU-38 to MNU-41) and their 17 fixtures; the selected fill and colors arrive through the tokens, the fill of a checked row with them: none at standard contrast in the default system, as MNU-37 says. Reading comp.menu.separation, comp.menu.checkbox-item.mark and comp.menu.radio-item.mark as typed choices needs this platform's token output to write a component's choice as a typed enumeration, which it does not yet. Nor is the link item: MNU-53 to MNU-57 and what MNU-6, MNU-19 and MNU-24 say of it, with the fixtures links, links-rtl and links-trailing. Lagging behind MNU-58 (a link item's current, drawn with the selected fill) and MNU-36 (the groups as separate tiles under gap): it has neither yet. Not built yet: iconSpace (MNU-59) — this platform still keeps the mark and leading icon columns in every row of a surface where any item draws in them. Not built yet: the context trigger (MNU-60 to MNU-63) — this platform opens the menu from an anchor only. <Menu visible anchorRef placement onDismiss items={[…]}>: items is an array whose element type is the union of the five kinds, told apart by a kind field ('item', 'checkbox-item', 'radio-item', 'submenu', 'group'). Presented on the overlay layer that TanreiProvider renders (not a Modal, which would make the rest of the screen unreachable) and placed by measuring the anchor (measureInWindow); Android's back button (BackHandler) is close. A press outside closes the menu and does not reach what is under it. Controlled: the caller sets visible to false from onDismiss and keeps each checkbox item's checked and each group's value. Ships the previous revision today (the check stroked as a path in a 16 box, a group's title starting where the labels start). Not built on this platform yet, among the rest of this spec: the submenu item kind and everything about it (MNU-42 to MNU-52), separation (MNU-35, MNU-36), the checkbox and radio looks of the mark and markIcon (MNU-38 to MNU-41) and their 17 fixtures; the selected fill and colors arrive through the tokens, the fill of a checked row with them: none at standard contrast in the default system, as MNU-37 says. Reading comp.menu.separation, comp.menu.checkbox-item.mark and comp.menu.radio-item.mark as typed choices needs this platform's token output to write a component's choice as a typed enumeration, which it does not yet. Nor is the link item: MNU-53 to MNU-57 and what MNU-6, MNU-19 and MNU-24 say of it, with the fixtures links, links-rtl and links-trailing. Lagging behind MNU-58 (a link item's current, drawn with the selected fill) and MNU-36 (the groups as separate tiles under gap): it has neither yet. Not built yet: iconSpace (MNU-59) — this platform still keeps the mark and leading icon columns in every row of a surface where any item draws in them. Not built yet: the context trigger (MNU-60 to MNU-63) — this platform opens the menu from an anchor only.

Tokens

Token Alias de Valor resuelto (claro)
--tanrei-comp-menu-checkbox-item-mark — 0
--tanrei-comp-menu-container-gap-compact comp.tree-view.container.gap.compact 8px
--tanrei-comp-menu-container-gap-regular sys.space.space150 12px
--tanrei-comp-menu-container-height-compact comp.tree-view.container.height.compact 32px
--tanrei-comp-menu-container-height-regular — 48px
--tanrei-comp-menu-container-padding-block-compact comp.tree-view.container.padding-block.compact 6px
--tanrei-comp-menu-container-padding-block-regular — 8px
--tanrei-comp-menu-container-padding-inline-compact comp.tree-view.container.padding-inline.compact 8px
--tanrei-comp-menu-container-padding-inline-regular sys.space.space150 12px
--tanrei-comp-menu-container-shape — 4px
--tanrei-comp-menu-control-box-shape — 2px
--tanrei-comp-menu-control-box-size — 18px
--tanrei-comp-menu-control-fill-inset — 0px
--tanrei-comp-menu-control-fill-opacity sys.state.selected-state-layer-opacity 0.16
--tanrei-comp-menu-control-mark-size sys.icon.small 20px
--tanrei-comp-menu-control-mark-weight sys.icon.mark.weight 500
--tanrei-comp-menu-control-outline-width sys.border.thick 2px
--tanrei-comp-menu-control-ring-selected-width-share — 0.5
--tanrei-comp-menu-control-ring-size — 20px
--tanrei-comp-menu-control-ring-width sys.border.thick 2px
--tanrei-comp-menu-current-container-color sys.color.secondary-container #262626 · 16%
--tanrei-comp-menu-current-control-mark-color — #000000 · 0%
--tanrei-comp-menu-current-control-color — #000000 · 0%
--tanrei-comp-menu-current-disabled-container-color — #1b1b1b · 12%
--tanrei-comp-menu-current-disabled-control-mark-color — #1b1b1b · 38%
--tanrei-comp-menu-current-disabled-control-color — #1b1b1b · 38%
--tanrei-comp-menu-current-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-menu-current-disabled-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-current-disabled-mark-color — #1b1b1b · 38%
--tanrei-comp-menu-current-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-menu-current-disabled-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-menu-current-disabled-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-current-disabled-trailing-text-color — #1b1b1b · 38%
--tanrei-comp-menu-current-focused-container-color — #262626 · 24.4%
--tanrei-comp-menu-current-focused-control-mark-color — #000000 · 0%
--tanrei-comp-menu-current-focused-control-color — #000000 · 0%
--tanrei-comp-menu-current-focused-label-color — #262626
--tanrei-comp-menu-current-focused-leading-icon-color — #262626
--tanrei-comp-menu-current-focused-mark-color — #000000 · 0%
--tanrei-comp-menu-current-focused-state-layer-color — #262626
--tanrei-comp-menu-current-focused-supporting-text-color — #262626
--tanrei-comp-menu-current-focused-trailing-icon-color — #262626
--tanrei-comp-menu-current-focused-trailing-text-color — #262626
--tanrei-comp-menu-current-hovered-container-color — #262626 · 22.7%
--tanrei-comp-menu-current-hovered-control-mark-color — #000000 · 0%
--tanrei-comp-menu-current-hovered-control-color — #000000 · 0%
--tanrei-comp-menu-current-hovered-label-color — #262626
--tanrei-comp-menu-current-hovered-leading-icon-color — #262626
--tanrei-comp-menu-current-hovered-mark-color — #000000 · 0%
--tanrei-comp-menu-current-hovered-state-layer-color — #262626
--tanrei-comp-menu-current-hovered-supporting-text-color — #262626
--tanrei-comp-menu-current-hovered-trailing-icon-color — #262626
--tanrei-comp-menu-current-hovered-trailing-text-color — #262626
--tanrei-comp-menu-current-label-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-current-leading-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-current-mark-color — #000000 · 0%
--tanrei-comp-menu-current-pressed-container-color — #262626 · 24.4%
--tanrei-comp-menu-current-pressed-control-mark-color — #000000 · 0%
--tanrei-comp-menu-current-pressed-control-color — #000000 · 0%
--tanrei-comp-menu-current-pressed-label-color — #262626
--tanrei-comp-menu-current-pressed-leading-icon-color — #262626
--tanrei-comp-menu-current-pressed-mark-color — #000000 · 0%
--tanrei-comp-menu-current-pressed-state-layer-color — #262626
--tanrei-comp-menu-current-pressed-supporting-text-color — #262626
--tanrei-comp-menu-current-pressed-trailing-icon-color — #262626
--tanrei-comp-menu-current-pressed-trailing-text-color — #262626
--tanrei-comp-menu-current-state-layer-color comp.menu.current.label.color #262626
--tanrei-comp-menu-current-supporting-text-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-current-trailing-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-current-trailing-text-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-destructive-container-color — #000000 · 0%
--tanrei-comp-menu-destructive-control-mark-color — #000000 · 0%
--tanrei-comp-menu-destructive-control-color — #000000 · 0%
--tanrei-comp-menu-destructive-disabled-container-color — #000000 · 0%
--tanrei-comp-menu-destructive-disabled-control-mark-color — #1b1b1b · 38%
--tanrei-comp-menu-destructive-disabled-control-color — #1b1b1b · 38%
--tanrei-comp-menu-destructive-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-menu-destructive-disabled-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-destructive-disabled-mark-color — #000000 · 0%
--tanrei-comp-menu-destructive-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-menu-destructive-disabled-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-menu-destructive-disabled-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-destructive-disabled-trailing-text-color — #1b1b1b · 38%
--tanrei-comp-menu-destructive-focused-container-color — #cd262f · 10%
--tanrei-comp-menu-destructive-focused-control-mark-color — #000000 · 0%
--tanrei-comp-menu-destructive-focused-control-color — #000000 · 0%
--tanrei-comp-menu-destructive-focused-label-color — #cd262f
--tanrei-comp-menu-destructive-focused-leading-icon-color — #cd262f
--tanrei-comp-menu-destructive-focused-mark-color — #000000 · 0%
--tanrei-comp-menu-destructive-focused-state-layer-color — #cd262f
--tanrei-comp-menu-destructive-focused-supporting-text-color — #4b4b4b
--tanrei-comp-menu-destructive-focused-trailing-icon-color — #4b4b4b
--tanrei-comp-menu-destructive-focused-trailing-text-color — #4b4b4b
--tanrei-comp-menu-destructive-hovered-container-color — #cd262f · 8%
--tanrei-comp-menu-destructive-hovered-control-mark-color — #000000 · 0%
--tanrei-comp-menu-destructive-hovered-control-color — #000000 · 0%
--tanrei-comp-menu-destructive-hovered-label-color — #cd262f
--tanrei-comp-menu-destructive-hovered-leading-icon-color — #cd262f
--tanrei-comp-menu-destructive-hovered-mark-color — #000000 · 0%
--tanrei-comp-menu-destructive-hovered-state-layer-color — #cd262f
--tanrei-comp-menu-destructive-hovered-supporting-text-color — #4b4b4b
--tanrei-comp-menu-destructive-hovered-trailing-icon-color — #4b4b4b
--tanrei-comp-menu-destructive-hovered-trailing-text-color — #4b4b4b
--tanrei-comp-menu-destructive-label-color sys.color.error #cd262f
--tanrei-comp-menu-destructive-leading-icon-color sys.color.error #cd262f
--tanrei-comp-menu-destructive-mark-color — #000000 · 0%
--tanrei-comp-menu-destructive-pressed-container-color — #cd262f · 10%
--tanrei-comp-menu-destructive-pressed-control-mark-color — #000000 · 0%
--tanrei-comp-menu-destructive-pressed-control-color — #000000 · 0%
--tanrei-comp-menu-destructive-pressed-label-color — #cd262f
--tanrei-comp-menu-destructive-pressed-leading-icon-color — #cd262f
--tanrei-comp-menu-destructive-pressed-mark-color — #000000 · 0%
--tanrei-comp-menu-destructive-pressed-state-layer-color — #cd262f
--tanrei-comp-menu-destructive-pressed-supporting-text-color — #4b4b4b
--tanrei-comp-menu-destructive-pressed-trailing-icon-color — #4b4b4b
--tanrei-comp-menu-destructive-pressed-trailing-text-color — #4b4b4b
--tanrei-comp-menu-destructive-state-layer-color comp.menu.destructive.label.color #cd262f
--tanrei-comp-menu-destructive-supporting-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-destructive-trailing-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-destructive-trailing-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-disabled-container-shape — 4px
--tanrei-comp-menu-disabled-opacity — 1
--tanrei-comp-menu-divider-color sys.color.outline-variant #000000 · 8%
--tanrei-comp-menu-divider-space sys.space.space50 4px
--tanrei-comp-menu-divider-thickness sys.border.hairline 1px
--tanrei-comp-menu-expanded-container-color sys.color.surface-container-high #1b1b1b · 8%
--tanrei-comp-menu-expanded-control-mark-color — #000000 · 0%
--tanrei-comp-menu-expanded-control-color — #000000 · 0%
--tanrei-comp-menu-expanded-disabled-container-color — #1b1b1b · 8%
--tanrei-comp-menu-expanded-disabled-control-mark-color — #1b1b1b · 38%
--tanrei-comp-menu-expanded-disabled-control-color — #1b1b1b · 38%
--tanrei-comp-menu-expanded-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-menu-expanded-disabled-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-expanded-disabled-mark-color — #1b1b1b · 38%
--tanrei-comp-menu-expanded-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-menu-expanded-disabled-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-menu-expanded-disabled-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-expanded-disabled-trailing-text-color — #1b1b1b · 38%
--tanrei-comp-menu-expanded-focused-container-color — #1b1b1b · 10%
--tanrei-comp-menu-expanded-focused-control-mark-color — #000000 · 0%
--tanrei-comp-menu-expanded-focused-control-color — #000000 · 0%
--tanrei-comp-menu-expanded-focused-label-color — #1b1b1b
--tanrei-comp-menu-expanded-focused-leading-icon-color — #4b4b4b
--tanrei-comp-menu-expanded-focused-mark-color — #000000 · 0%
--tanrei-comp-menu-expanded-focused-state-layer-color — #1b1b1b
--tanrei-comp-menu-expanded-focused-supporting-text-color — #4b4b4b
--tanrei-comp-menu-expanded-focused-trailing-icon-color — #4b4b4b
--tanrei-comp-menu-expanded-focused-trailing-text-color — #4b4b4b
--tanrei-comp-menu-expanded-hovered-container-color — #1b1b1b · 8%
--tanrei-comp-menu-expanded-hovered-control-mark-color — #000000 · 0%
--tanrei-comp-menu-expanded-hovered-control-color — #000000 · 0%
--tanrei-comp-menu-expanded-hovered-label-color — #1b1b1b
--tanrei-comp-menu-expanded-hovered-leading-icon-color — #4b4b4b
--tanrei-comp-menu-expanded-hovered-mark-color — #000000 · 0%
--tanrei-comp-menu-expanded-hovered-state-layer-color — #1b1b1b
--tanrei-comp-menu-expanded-hovered-supporting-text-color — #4b4b4b
--tanrei-comp-menu-expanded-hovered-trailing-icon-color — #4b4b4b
--tanrei-comp-menu-expanded-hovered-trailing-text-color — #4b4b4b
--tanrei-comp-menu-expanded-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-menu-expanded-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-expanded-mark-color — #000000 · 0%
--tanrei-comp-menu-expanded-pressed-container-color — #1b1b1b · 10%
--tanrei-comp-menu-expanded-pressed-control-mark-color — #000000 · 0%
--tanrei-comp-menu-expanded-pressed-control-color — #000000 · 0%
--tanrei-comp-menu-expanded-pressed-label-color — #1b1b1b
--tanrei-comp-menu-expanded-pressed-leading-icon-color — #4b4b4b
--tanrei-comp-menu-expanded-pressed-mark-color — #000000 · 0%
--tanrei-comp-menu-expanded-pressed-state-layer-color — #1b1b1b
--tanrei-comp-menu-expanded-pressed-supporting-text-color — #4b4b4b
--tanrei-comp-menu-expanded-pressed-trailing-icon-color — #4b4b4b
--tanrei-comp-menu-expanded-pressed-trailing-text-color — #4b4b4b
--tanrei-comp-menu-expanded-state-layer-color comp.menu.expanded.label.color #1b1b1b
--tanrei-comp-menu-expanded-supporting-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-expanded-trailing-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-expanded-trailing-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-focused-container-shape — 4px
--tanrei-comp-menu-focused-opacity — 1
--tanrei-comp-menu-group-title-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-group-title-padding-inline-compact comp.menu.container.padding-inline.compact 8px
--tanrei-comp-menu-group-title-padding-inline-regular comp.menu.container.padding-inline.regular 12px
--tanrei-comp-menu-group-gap sys.space.space200 16px
--tanrei-comp-menu-hovered-container-shape — 4px
--tanrei-comp-menu-hovered-opacity — 1
--tanrei-comp-menu-leading-icon-size-compact sys.icon.small 20px
--tanrei-comp-menu-leading-icon-size-regular sys.icon.medium 24px
--tanrei-comp-menu-mark-size-compact sys.icon.small 20px
--tanrei-comp-menu-mark-size-regular sys.icon.medium 24px
--tanrei-comp-menu-mark-width sys.border.thick 2px
--tanrei-comp-menu-motion-close-duration sys.motion.role.appear.anchored.exit 250ms
--tanrei-comp-menu-motion-close-easing sys.motion.easing.exit cubic-bezier(0.3, 0, 1, 1)
--tanrei-comp-menu-motion-feedback-duration sys.motion.role.feedback 450ms
--tanrei-comp-menu-motion-open-duration sys.motion.role.appear.anchored.enter 300ms
--tanrei-comp-menu-motion-open-easing sys.motion.easing.enter cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-menu-motion-state-duration sys.motion.role.state.large 250ms
--tanrei-comp-menu-motion-state-easing sys.motion.easing.state cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-menu-motion-submenu-delay sys.motion.duration.short4 200ms
--tanrei-comp-menu-pressed-container-shape — 4px
--tanrei-comp-menu-pressed-opacity — 1
--tanrei-comp-menu-radio-item-mark — 0
--tanrei-comp-menu-selected-container-color sys.color.secondary-container #262626 · 0%
--tanrei-comp-menu-selected-control-mark-color sys.color.on-primary #ffffff
--tanrei-comp-menu-selected-control-color sys.color.primary #1b1b1b
--tanrei-comp-menu-selected-disabled-container-color — #1b1b1b · 0%
--tanrei-comp-menu-selected-disabled-control-mark-color — #ffffff
--tanrei-comp-menu-selected-disabled-control-color — #1b1b1b · 38%
--tanrei-comp-menu-selected-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-menu-selected-disabled-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-selected-disabled-mark-color — #1b1b1b · 38%
--tanrei-comp-menu-selected-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-menu-selected-disabled-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-menu-selected-disabled-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-selected-disabled-trailing-text-color — #1b1b1b · 38%
--tanrei-comp-menu-selected-focused-container-color — #262626 · 10%
--tanrei-comp-menu-selected-focused-control-mark-color — #ffffff
--tanrei-comp-menu-selected-focused-control-color — #1b1b1b
--tanrei-comp-menu-selected-focused-label-color — #262626
--tanrei-comp-menu-selected-focused-leading-icon-color — #262626
--tanrei-comp-menu-selected-focused-mark-color — #1b1b1b
--tanrei-comp-menu-selected-focused-state-layer-color — #262626
--tanrei-comp-menu-selected-focused-supporting-text-color — #262626
--tanrei-comp-menu-selected-focused-trailing-icon-color — #262626
--tanrei-comp-menu-selected-focused-trailing-text-color — #262626
--tanrei-comp-menu-selected-hovered-container-color — #262626 · 8%
--tanrei-comp-menu-selected-hovered-control-mark-color — #ffffff
--tanrei-comp-menu-selected-hovered-control-color — #1b1b1b
--tanrei-comp-menu-selected-hovered-label-color — #262626
--tanrei-comp-menu-selected-hovered-leading-icon-color — #262626
--tanrei-comp-menu-selected-hovered-mark-color — #1b1b1b
--tanrei-comp-menu-selected-hovered-state-layer-color — #262626
--tanrei-comp-menu-selected-hovered-supporting-text-color — #262626
--tanrei-comp-menu-selected-hovered-trailing-icon-color — #262626
--tanrei-comp-menu-selected-hovered-trailing-text-color — #262626
--tanrei-comp-menu-selected-label-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-selected-leading-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-selected-mark-color sys.color.primary #1b1b1b
--tanrei-comp-menu-selected-pressed-container-color — #262626 · 10%
--tanrei-comp-menu-selected-pressed-control-mark-color — #ffffff
--tanrei-comp-menu-selected-pressed-control-color — #1b1b1b
--tanrei-comp-menu-selected-pressed-label-color — #262626
--tanrei-comp-menu-selected-pressed-leading-icon-color — #262626
--tanrei-comp-menu-selected-pressed-mark-color — #1b1b1b
--tanrei-comp-menu-selected-pressed-state-layer-color — #262626
--tanrei-comp-menu-selected-pressed-supporting-text-color — #262626
--tanrei-comp-menu-selected-pressed-trailing-icon-color — #262626
--tanrei-comp-menu-selected-pressed-trailing-text-color — #262626
--tanrei-comp-menu-selected-state-layer-color comp.menu.selected.label.color #262626
--tanrei-comp-menu-selected-supporting-text-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-selected-trailing-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-selected-trailing-text-color sys.color.on-secondary-container #262626
--tanrei-comp-menu-separation sys.behavior.separation 0
--tanrei-comp-menu-shortcut-variant — 0
--tanrei-comp-menu-submenu-offset sys.space.space50 4px
--tanrei-comp-menu-surface-color sys.color.surface-bright #ffffff
--tanrei-comp-menu-surface-elevation sys.elevation.level2 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 2px 0px rgb(0 0 0 / 0.04)
--tanrei-comp-menu-surface-max-width — 320px
--tanrei-comp-menu-surface-min-width — 160px
--tanrei-comp-menu-surface-offset sys.space.space50 4px
--tanrei-comp-menu-surface-padding-block sys.space.space50 4px
--tanrei-comp-menu-surface-padding-inline sys.space.space50 4px
--tanrei-comp-menu-surface-shape sys.shape.corner.medium 8px
--tanrei-comp-menu-surface-viewport-margin sys.space.space100 8px
--tanrei-comp-menu-trailing-icon-size-compact sys.icon.small 20px
--tanrei-comp-menu-trailing-icon-size-regular sys.icon.medium 24px
--tanrei-comp-menu-unselected-container-color — #000000 · 0%
--tanrei-comp-menu-unselected-control-mark-color — #000000 · 0%
--tanrei-comp-menu-unselected-control-color sys.color.outline #848484
--tanrei-comp-menu-unselected-disabled-container-color — #000000 · 0%
--tanrei-comp-menu-unselected-disabled-control-mark-color — #1b1b1b · 38%
--tanrei-comp-menu-unselected-disabled-control-color — #1b1b1b · 38%
--tanrei-comp-menu-unselected-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-menu-unselected-disabled-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-unselected-disabled-mark-color — #000000 · 0%
--tanrei-comp-menu-unselected-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-menu-unselected-disabled-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-menu-unselected-disabled-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-menu-unselected-disabled-trailing-text-color — #1b1b1b · 38%
--tanrei-comp-menu-unselected-focused-container-color — #1b1b1b · 10%
--tanrei-comp-menu-unselected-focused-control-mark-color — #000000 · 0%
--tanrei-comp-menu-unselected-focused-control-color — #848484
--tanrei-comp-menu-unselected-focused-label-color — #1b1b1b
--tanrei-comp-menu-unselected-focused-leading-icon-color — #4b4b4b
--tanrei-comp-menu-unselected-focused-mark-color — #000000 · 0%
--tanrei-comp-menu-unselected-focused-state-layer-color — #1b1b1b
--tanrei-comp-menu-unselected-focused-supporting-text-color — #4b4b4b
--tanrei-comp-menu-unselected-focused-trailing-icon-color — #4b4b4b
--tanrei-comp-menu-unselected-focused-trailing-text-color — #4b4b4b
--tanrei-comp-menu-unselected-hovered-container-color — #1b1b1b · 8%
--tanrei-comp-menu-unselected-hovered-control-mark-color — #000000 · 0%
--tanrei-comp-menu-unselected-hovered-control-color — #848484
--tanrei-comp-menu-unselected-hovered-label-color — #1b1b1b
--tanrei-comp-menu-unselected-hovered-leading-icon-color — #4b4b4b
--tanrei-comp-menu-unselected-hovered-mark-color — #000000 · 0%
--tanrei-comp-menu-unselected-hovered-state-layer-color — #1b1b1b
--tanrei-comp-menu-unselected-hovered-supporting-text-color — #4b4b4b
--tanrei-comp-menu-unselected-hovered-trailing-icon-color — #4b4b4b
--tanrei-comp-menu-unselected-hovered-trailing-text-color — #4b4b4b
--tanrei-comp-menu-unselected-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-menu-unselected-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-unselected-mark-color — #000000 · 0%
--tanrei-comp-menu-unselected-pressed-container-color — #1b1b1b · 10%
--tanrei-comp-menu-unselected-pressed-control-mark-color — #000000 · 0%
--tanrei-comp-menu-unselected-pressed-control-color — #848484
--tanrei-comp-menu-unselected-pressed-label-color — #1b1b1b
--tanrei-comp-menu-unselected-pressed-leading-icon-color — #4b4b4b
--tanrei-comp-menu-unselected-pressed-mark-color — #000000 · 0%
--tanrei-comp-menu-unselected-pressed-state-layer-color — #1b1b1b
--tanrei-comp-menu-unselected-pressed-supporting-text-color — #4b4b4b
--tanrei-comp-menu-unselected-pressed-trailing-icon-color — #4b4b4b
--tanrei-comp-menu-unselected-pressed-trailing-text-color — #4b4b4b
--tanrei-comp-menu-unselected-state-layer-color comp.menu.unselected.label.color #1b1b1b
--tanrei-comp-menu-unselected-supporting-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-unselected-trailing-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-unselected-trailing-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-menu-group-title-typescale sys.typescale.label-small 500 12px/1.3333
--tanrei-comp-menu-label-typescale sys.typescale.label 500 14px/1.4286
--tanrei-comp-menu-supporting-text-typescale sys.typescale.body-small 400 13px/1.5385
--tanrei-comp-menu-trailing-text-typescale sys.typescale.label-small 500 12px/1.3333

Fixtures

Representaciones canónicas. Todas las plataformas capturan cada fixture con el mismo identificador para poder comparar los resultados.

Generado a partir de specs/menu.spec.json, tokens/comp/menu.tokens.json, design/tokens/

Pautas

Cuándo usarlo

Usa un menú para acciones secundarias y ajustes que no necesitan un lugar en la pantalla: «Más» en una fila, el desbordamiento de una barra de herramientas, un criterio de ordenación, las opciones de una vista. Las personas lo abren, eligen una cosa y vuelven a lo que estaban haciendo.

Usa botones para las acciones principales de una pantalla, porque un menú las oculta. Usa una lista, botones de opción o un select cuando la elección sea la tarea principal de la pantalla. Usa un diálogo cuando las personas deban responder antes de continuar. Un menú solo contiene sus elementos: ni campos, ni controles deslizantes, ni interruptores, que se quedarían con las teclas que el menú necesita.

Una fila que lleva a algún sitio es un elemento de enlace: da al elemento un href. Úsalo para un conjunto corto de lugares que no ocupa sitio hasta que se pide, como los niveles plegados de una ruta de navegación, una elección de idioma o «Más». Las páginas principales de un sitio, siempre a la vista, son navegación: ponlas en una barra de navegación, en un panel de navegación o en una lista de enlaces.

Cómo elegir un tipo de elemento

Tipo Qué es Después de activarlo Úsalo para
Elemento Una acción o, con un href, un enlace Emite su clic y luego el menú se cierra. El destino de un elemento de enlace se abre después. «Copiar», «Cambiar nombre», «Compartir». Activa destructive para «Eliminar» o «Descartar»: se dibuja en el color de error. Dale un href para un lugar al que ir.
Elemento de casilla de verificación Una opción que se activa o se desactiva por sí sola Conmuta su checked y emite su change. «Mostrar cuadrícula», «Mostrar reglas»: ajustes que no se afectan entre sí.
Elemento de opción Una elección entre varias dentro de su grupo Define el value de su grupo y el grupo emite su change. Un elemento de opción ya marcado no emite nada. «Ordenar por nombre», «Ordenar por fecha»: una respuesta a una pregunta.
Submenú Un elemento que abre otro nivel a su lado Abre su submenú y emite su toggle. El menú sigue abierto. «Abrir recientes», «Compartir», «Mover a»: un segundo conjunto de elementos que alargaría demasiado el menú.
Grupo Una serie de elementos relacionados, opcionalmente con un título Nada: no recibe el foco. Separar elementos relacionados y contener los elementos de opción de una misma pregunta.

Un elemento de casilla de verificación o de opción que se activa con un toque, un clic o Enter cierra el menú, como una acción. Si se activa con Espacio, deja el menú abierto, de modo que quien usa el teclado puede cambiar varios ajustes de una vez.

Grupos, títulos y separación

  • El menú separa un grupo de lo que tenga al lado, nunca en la parte superior ni en la inferior: con un divisor o con una separación. Con una separación, cada grupo, y cada serie de elementos fuera de un grupo, es una hoja propia, con espacio vacío entre las hojas. Tú no colocas los divisores ni las hojas.
  • Cuál de los dos se usa lo elige el sistema de diseño, una sola elección para todos los componentes que separan sus propias partes: un divisor en el sistema predeterminado. separation decide para un menú y sus submenús: auto (el valor predeterminado) sigue al sistema de diseño, y divider o gap indican uno de los dos, elija lo que elija el sistema. Con una separación de espacio no se expone ningún separador, y los grupos se siguen exponiendo como grupos.
  • Un grupo con un title lo dibuja como un título discreto encima de sus elementos, que empieza donde empieza el contenido de las filas, y recibe de él su nombre. Da al grupo de elementos de opción un título que haga la pregunta, como «Ordenar por».
  • Los elementos de opción van dentro de un grupo. Un elemento de opción fuera de un grupo nunca está marcado, y las compilaciones de depuración lo notifican.
  • Un grupo no puede contener otro grupo. Los elementos del grupo interior se dibujan como si fueran del grupo exterior.

Columnas de marca e icono

La marca de un elemento de casilla de verificación o de opción está en la columna de marca, al principio de la fila, y un icono inicial en la columna de icono que la sigue. iconSpace decide qué filas guardan el espacio de una columna en la que no dibujan nada:

  • auto (el valor predeterminado) decide por sección: un grupo, o una serie de elementos seguidos fuera de cualquier grupo. Una fila guarda el espacio de una columna mientras otro elemento de su sección dibuje en ella, así que las etiquetas de una sección empiezan en una misma línea. Una sección sin marcas ni iconos empieza sus etiquetas donde empieza el contenido de la fila, sin espacio vacío delante.
  • always guarda las dos columnas en todas las filas, así que todas las etiquetas del menú empiezan en una misma línea.
  • never da una columna solo a la fila que dibuja en ella, así que cada etiqueta empieza justo después de su propia marca o icono.

Cada superficie decide por sí misma, y un submenú sigue el iconSpace de su menú. El título de un grupo empieza donde empieza el contenido de las filas, guarden lo que guarden. La vista de árbol y el panel de navegación tienen el mismo iconSpace, con los mismos valores y el mismo valor predeterminado.

Densidad

density fija cuánto espacio ocupa una fila. regular (el valor por defecto: una fila de 48 con glifos de 24) es para un producto que se usa tanto con el tacto como con un puntero. compact (una fila de 32 con glifos de 20, la fila compacta de la vista de árbol) es para una herramienta que se usa con el ratón, cuyos menús son largos y se abren a menudo. El texto conserva su tamaño en ambas. Con el tacto una fila nunca es más baja que el objetivo táctil, así que un menú compacto en un teléfono sigue teniendo filas de 48. Cada submenú toma la densidad de su menú.

Enlaces

Un elemento de enlace es el enlace propio de la plataforma, así que funcionan el menú contextual del navegador, la apertura en una pestaña nueva y la dirección que se muestra al pasar el cursor. No tiene aspecto propio: se dibuja igual que la acción de la fila de al lado, en todos los estados.

  • Activación. Un clic, un toque, Enter o Espacio emiten el click del elemento; después el menú se cierra, y después se abre el destino. Espacio activa un elemento de enlace, igual que activa cualquier otra fila de un menú. En cualquier otro lugar, Espacio no activa un enlace.
  • Un enrutador. Cancela el click del elemento para hacerte cargo tú de la navegación. El menú se cierra igualmente, y no se abre nada más.
  • Otras pestañas. Un clic o Enter con una tecla modificadora, y un clic con el botón central, son cosa del navegador. El menú no emite ningún close y sigue abierto, de modo que varios de sus enlaces se pueden abrir uno tras otro.
  • Un contexto nuevo. target: new abre una pestaña o una ventana nueva. El elemento termina con el open_in_new del conjunto de iconos en la columna del icono final, salvo que trailingIcon nombre un glifo tuyo, y targetLabel se lo dice a la tecnología de apoyo. Un atajo o un texto final conserva su propia columna, antes de la marca.
  • La página en la que estás. current marca el elemento de enlace de la página en la que está la persona. Se expone como la página actual (aria-current="page" en la web) y se dibuja con un relleno propio en todos los esquemas de contraste, sin marca de verificación y sin una etiqueta de más peso: dice dónde está la persona, no una elección que haya hecho. Lo defines tú; el menú nunca lo hace. Sin un href se ignora.
  • Deshabilitado. Un elemento de enlace deshabilitado no se puede seguir de ninguna manera. Se sigue alcanzando con las teclas de flecha, como cualquier elemento deshabilitado.
  • Solo el tipo «elemento». Un elemento de casilla de verificación, un elemento de opción y un elemento de submenú no admiten un destino: guardan un estado o abren otro nivel.

Submenús

Un elemento de submenú es una fila que termina con el chevron_right del conjunto de iconos, y sus hijos son el nivel siguiente: elementos de cualquier tipo, incluidos los grupos. No es una acción y no tiene atajo.

  • Apertura. Una pulsación, un toque, Enter, Espacio o la tecla de flecha que apunta hacia el fin de la línea abren el submenú de inmediato y mueven el foco a su primer elemento habilitado. Un puntero que se detiene sobre el elemento lo abre tras una breve espera y deja el foco en el elemento.
  • De camino a él. Mientras el puntero se mueve desde el elemento hacia su submenú abierto, los elementos que cruza no pasan al estado con el cursor encima ni reciben el foco, de modo que un recorrido en diagonal no cierra el submenú.
  • Cerrar un nivel. La tecla de flecha que apunta hacia el inicio de la línea, o Escape, cierra el nivel que tiene el foco y devuelve el foco a su elemento. Escape cierra todo el menú solo cuando no hay ningún submenú abierto. Pasar a otro elemento del nivel superior también cierra el submenú. En una superficie hay como máximo un submenú abierto.
  • Cerrarlo todo. Una acción en cualquier nivel, Tab, una pulsación fuera de todas las superficies y que el foco salga de todas ellas cierran todo el menú con un solo close.
  • Dónde se coloca. Junto a la superficie que contiene su elemento, en el lado de fin, con su primera fila a la altura de la fila del elemento; en el otro lado cuando en ese no hay sitio. En una ventana demasiado estrecha para cualquiera de los dos, se coloca encima de su nivel superior y deja una franja de este sin cubrir: una pulsación en la franja cierra el submenú. El menú nunca sustituye su propio contenido por el del submenú.
  • open y toggle. El open de un elemento de submenú es suyo. El menú lo define y el elemento emite toggle cuando la persona abre o cierra el submenú. Un cambio de open hecho por ti, y el cierre de todo el menú, no emiten ningún toggle.
  • Profundidad. No pases de dos niveles de submenú por debajo del menú. Un tercero se sigue dibujando, y las compilaciones de depuración lo notifican, igual que notifican un submenú sin elementos.
  • El value de un grupo solo llega a los elementos de opción de su propia superficie, y cada superficie decide sus propias columnas de marca y de icono.

Menús contextuales

Un menú contextual ofrece las acciones que se aplican a lo que la persona señala, allí donde lo señala. Es el mismo menú, con los mismos elementos, teclas y submenús, abierto en un punto de un área en lugar de desde un botón.

  • El área. Pon trigger en context y anchor en el id del área: un lienzo, una lista, una fila de una tabla, un documento. El área no es un botón, así que el menú no escribe en ella ningún estado de ventana emergente.
  • Cómo se pide. Un clic derecho (en un Mac, también un clic con Control o con dos dedos), un toque o un lápiz sostenido sin moverse durante medio segundo, Mayús+F10 o la tecla de menú contextual sobre el elemento que tiene el foco, o la orden de menú contextual de un lector de pantalla. El menú emite contextRequest con el punto, el elemento por el que se pidió y cómo se pidió: pointer, long-press o keyboard. Tú pones open, como al abrirlo desde un ancla.
  • Dónde se abre. Con una esquina en el punto, sin separación: por defecto debajo del punto y hacia el final de la línea, como indique placement. Cerca de un borde de la ventana se abre al otro lado del punto, en cada eje por separado, de modo que nunca lo tapa. Se mueve con el área cuando la página se desplaza. Pedido con una tecla, se abre junto al elemento que tiene el foco, como junto a un ancla.
  • El menú propio del navegador. El menú cancela el menú contextual del navegador en cada petición que atiende. Cancela contextRequest para dejar una petición al navegador, por ejemplo sobre un campo, un enlace o un texto seleccionado que tu menú no trata.
  • Foco y cierre. El foco pasa al primer elemento habilitado y, al cerrarse, vuelve al elemento que tenía el foco cuando se pidió el menú. Una pulsación en cualquier lugar fuera del menú lo cierra, también dentro del área, y un clic derecho en otro sitio lo cierra y lo vuelve a pedir allí.
  • Qué poner en él. Solo acciones que se aplican a lo señalado, las más usadas primero. Ofrece cada una también en otro lugar de tu producto: quien no sabe pedirlo nunca ve un menú contextual.
  • En iOS una pulsación larga también puede seleccionar el texto que hay debajo. Si el texto del área no debe seleccionarse, indícalo en sus estilos (user-select: none, -webkit-touch-callout: none).

Apertura, foco y cierre

Un menú es una superposición que la persona maneja y que no es modal. Las personas actúan dentro de él, así que toma el foco y lo devuelve. No se bloquea nada más en la pantalla.

  • Apertura. Tú defines open cuando se activa el ancla. La superficie aparece en la capa de superposición de la plataforma, colocada junto al ancla según indique placement: de forma predeterminada debajo de ella, con los bordes de inicio alineados. Pasa al otro lado cuando en ese no hay sitio, se desplaza para quedarse dentro de la pantalla, permite el desplazamiento de su contenido cuando es demasiado alta y sigue al ancla mientras esta se mueve.
  • Entrada del foco. El foco pasa al primer elemento habilitado. Las teclas de flecha, Inicio, Fin y la escritura anticipada recorren entonces los elementos, dando la vuelta en los extremos y atravesando los grupos. Un puntero que se mueve sobre un elemento también lleva ahí el foco, de modo que las teclas continúan desde donde estaba el puntero.
  • No modal. El menú no retiene el foco, no deja inerte el resto de la pantalla y no dibuja ningún velo. Tab sale del menú y lo cierra.
  • Cierre. El menú pide que lo cierren y dice por qué. Los motivos tienen los mismos nombres en todas las plataformas, con el uso de mayúsculas propio de cada una:
Motivo Cuándo
escape Se pulsó Escape.
back La acción de retroceso de la plataforma, como el botón o el gesto de retroceso de Android.
outside Una pulsación cayó fuera de la superficie y del ancla. En un menú contextual, en cualquier lugar fuera de la superficie, también dentro del área.
tab Se pulsó Tab o Mayús+Tab. El foco sigue entonces adelante desde el ancla, como habría hecho desde ella. En un menú contextual, sigue desde el elemento que tenía el foco cuando se pidió el menú.
focus-out El foco salió del menú de alguna otra forma.
item Se activó un elemento.
  • Vuelta del foco. Cuando el menú se cierra y el foco estaba dentro de él, el foco vuelve al ancla. Un menú contextual lo devuelve al elemento que tenía el foco cuando se pidió el menú y, si ninguno lo tenía, el foco simplemente sale del menú.

Quién cierra el menú depende de la plataforma. En la web y en React Native, vuelves a poner open (visible) en false desde el evento de cierre. En Compose, pones expanded en false desde onDismissRequest, que lleva un TanreiMenuCloseReason. En Flutter, el menú llama a onClose con el motivo y se cierra solo mediante su controlador. En SwiftUI, el menú escribe false en isPresented, así que el motivo no se transmite.

También depende de la plataforma que una pulsación fuera llegue además a lo que hay debajo. En la web y en Flutter llega. En SwiftUI, Compose y React Native la pulsación solo cierra el menú.

Recomendado

  • Mantén cortas las etiquetas y, en las acciones, empiézalas con un verbo: «Copiar enlace», «Eliminar archivo».
  • Indica el atajo de teclado de una acción en shortcut, con nombres de tecla neutros (mod+c), y vincula las teclas en tu producto. Reserva trailingText para todo lo demás: el texto final nunca se expone como un atajo.
  • Pon una acción destructiva en último lugar, separada en su propio grupo o después de uno.
  • Mantén en su sitio un elemento deshabilitado cuando a veces está disponible, para que el menú no cambie de forma. Las personas pueden seguir llegando a él con las teclas de flecha.

Evita

  • No pongas en un menú la acción principal de una pantalla.
  • No uses elementos de casilla de verificación para opciones que se excluyen entre sí. Usa elementos de opción en un grupo.
  • No hagas un menú de un solo elemento. Muestra la acción como un botón.
  • No pongas en un submenú una acción que las personas usan a menudo, y no pases de dos niveles de submenú.
  • No uses un chevron_right final en una acción. Es la marca de un elemento de submenú.
  • No construyas la navegación principal de un sitio como un menú de enlaces. Un menú se pide y desaparece.

Accesibilidad

Rol
menu
Etiquetado
The menu is named by its anchor (a context menu by the name the product gives it, and by none where it gives none), and a submenu by its submenu item's label. Each item's label is its name and its supporting text its description; its shortcut is exposed as the item's keyboard shortcut where the platform has one, and otherwise given as text in its description (KBS-10); trailing text is description only. A link item is a menu item like any other, named by its label; while it opens a new context its targetLabel ends its description. Checkbox and radio items expose their checked state, whatever their mark looks like. A submenu item exposes that it opens a menu and whether that menu is open. A group is named by its title. Icons, the mark and the submenu indicator are decorative.
Objetivo mínimo
El área de pulsación cumple el tamaño mínimo de objetivo aunque el tamaño visual sea menor. minimum 32px · pointer 44px · spacing 8px · touch 48px

Teclado

Teclas Acción
Enter, Space / Down Arrow on the anchor The product opens the menu; focus moves to the first enabled item.
Shift+F10 / the ContextMenu key on the area / an element inside it (trigger context) The menu asks to be opened against that element (contextRequest); the product opens it, and focus moves to the first enabled item.
Down Arrow / Up Arrow Moves focus to the next or previous item of the level that holds focus, wrapping at the ends.
Home / End Moves focus to the first or last item of that level.
Printable characters Moves focus to the next item of that level whose label begins with the characters typed.
Right Arrow (Left Arrow in a right-to-left layout) on a submenu item Opens the submenu and moves focus to its first enabled item.
Left Arrow (Right Arrow in a right-to-left layout) in a submenu Closes that submenu and returns focus to its submenu item.
Enter Activates the focused item and closes the menu and its submenus, a link item's destination opening after that; on a submenu item, opens the submenu instead.
Space Activates the focused item, a link item as Enter does; a checkbox or radio item leaves the menu open; on a submenu item, opens the submenu.
Enter with a modifier key, on a link item (the web) What the browser does with a link: another tab or window, a download. The menu emits no close and stays open, with focus on the item (MNU-54).
Escape Closes the innermost open submenu and returns focus to its submenu item; with no submenu open, closes the menu and returns focus to the anchor (a context menu: to the element that had focus when it was asked for).
Tab / Shift+Tab Closes the menu and every submenu, and moves focus on from the anchor.

Notas

  • The anchor is the product's own button, icon-only Button or field. The menu does not draw it; it reads its position, returns focus to it, and sets its expanded state where the platform has one (aria-haspopup and aria-expanded on the web).
  • A context menu (trigger context) is the same menu opened at a point of an area instead of from a button: a secondary click, a long press, Shift+F10 or the ContextMenu key, as the WAI-ARIA menu pattern names for a context-specific menu. The area is not a button, so it carries no popup state; the menu cancels the platform's own context menu only where it answers the request, and a product that cancels contextRequest — over a field, a link, selected text it does not handle — leaves the platform's menu in place. Every action a context menu offers is also reachable elsewhere in the product, because a context menu is found only by those who know to ask for it (MNU-60 to MNU-63).
  • Five item kinds: item (an action), checkbox-item, radio-item, submenu and group. A checkbox item's checked is its own; which radio item is checked is its group's, by value; a submenu item's open is its own. A group may hold items of any kind except another group; a submenu may hold items of every kind.
  • A link item is the platform's own link exposed as a menu item: on the web an anchor with the menu item role. Assistive technology reads a menu by its items — how many, which one, the arrow keys — and that reading holds only while every child of the menu is a menu item; so the role stays, and what makes the row a link stays underneath it: its address, the browser's context menu and new-tab gestures, a crawler that follows it. It is the one link form that Space activates, because it is operated as a menu item and looks like the action beside it. A menu of nothing but links to a site's main pages, always in view, is navigation and belongs in a navigation landmark of links; the menu is for a short set that is out of the way until asked for (the folded levels of a trail, a choice of language, "more").
  • Focus: the menu is an operated overlay that is not modal. It moves focus in when it opens and returns it to the anchor when it closes, but does not keep it: Tab leaves the menu and closes it, at whatever level focus is. A submenu takes focus when it is opened by a key or a press and returns it to its submenu item when it closes alone.
  • Submenus follow the WAI-ARIA menu pattern: the arrow key that points to where the submenu opens, Enter and Space open it and move focus in; the opposite arrow key and Escape close it and return focus to its item; type-ahead, Home, End and the vertical arrow keys work inside the level that holds focus. A pointer opens a submenu by resting on its item, and may then cross other items on its way to the submenu without closing it (MNU-45); touch opens it with a tap. In a window too narrow to set two surfaces side by side the submenu is laid over its parent, never swapped in for it, so the keys, the roles and the reading order are the same at every size (MNU-47).
  • Depth: one level of submenus where possible, never more than two. Each level costs a person a precise sideways movement and hides the level before it on a small screen; a set that needs more belongs in a dialog, a sheet or a page. A debug build reports a third level; nothing stops it from working (MNU-52).
  • A checked item is told from an unchecked one by its mark, which differs in shape (an empty and a filled box, a thin and a heavy ring, a check or none), never by the row's fill alone: the fill supports the mark and is not held to a contrast of its own. It is still meant to be seen: in the default system a checked row's fill is a layer of the row's content color over the sheet, so it differs from the sheet and from a hovered row by the same step in every scheme (MNU-37), and the content on it keeps at least 4.5:1 in every state. The control looks are drawings in the mark column; the menu holds no checkbox and no radio button.
  • Under gap no separator is exposed between groups; the groups themselves, and their titles, carry the structure to assistive technology, as they do under divider.
  • A menu holds only its items. A field, slider or switch inside a menu would take arrow keys and typing that the menu needs.
  • Text size does not clip an item: items grow at 200% text size, and the surface scrolls when it no longer fits the visible part of the window.
  • An item's shortcut is drawn by the keyboard shortcut component (KBS-13), which writes the keys for the keyboard the person uses and only shows them: the product binds them. Trailing text is never read as a shortcut; a product that typed its shortcut there keeps the text but not the keyboard shortcut semantics until it moves the keys to shortcut.
  • How a state looks is a value decision of the active design system; components read the state's tokens and never compute the effect themselves.
  • WCAG 2.5.8 (target size): for touch every row is at least the touch target high in both densities (MNU-12). The compact density is lower than the pointer target for a fine pointer, by design: it is the density of a tool used with a mouse, it stays above the 24 by 24 the criterion asks, and its rows reach across the surface's whole width. A product that cannot accept that uses regular.

Generado a partir de specs/menu.spec.json, tokens/sys/measure.tokens.json

De qué se encarga el componente

  • La superficie se expone como un menú. Las acciones son elementos de menú, los elementos de casilla de verificación son elementos de menú de casilla de verificación y los elementos de opción son elementos de menú de opción, cada uno con su estado marcado, su posición y el tamaño del menú.
  • Cada elemento recibe su nombre de su etiqueta y su descripción de su texto de apoyo. El shortcut de un elemento se expone como su atajo de teclado donde la plataforma tiene esa propiedad (aria-keyshortcuts en la web); donde no, sus teclas escritas con todas las letras forman parte de su descripción. El texto final solo forma parte de la descripción. Los iconos y las marcas quedan ocultos: el recuadro, el anillo y la marca de verificación son dibujos, y el estado marcado es del propio elemento.
  • Un elemento de enlace se expone como un elemento de menú, no como un enlace: un menú se lee por sus elementos, por su número y con las teclas de flecha, y eso solo se cumple mientras todas las filas son elementos de menú. Por debajo sigue siendo el enlace de la plataforma. Mientras abre un contexto nuevo, su targetLabel cierra su descripción, y su marca queda oculta. Un elemento de enlace actual se expone además como la página actual.
  • Un grupo se expone como un grupo que recibe su nombre de su título. Los divisores son separadores que no reciben el foco; donde los grupos se separan con espacio vacío, no hay separador. Los títulos no reciben el foco y no se leen como elementos.
  • Un elemento de submenú se expone como un elemento que abre un menú, con su estado expandido, también mientras está deshabilitado. Su superficie es un menú por sí misma, que recibe su nombre de la etiqueta del elemento, y cada superficie cuenta sus propias posiciones.
  • Al ancla se le indica que abre un menú y si el menú está abierto, donde la plataforma tiene esa propiedad (en la web, aria-haspopup, aria-controls y aria-expanded).
  • Un menú contextual deja su área como está: el área no es un botón y no se le indica que abre un menú. El menú se abre con un clic derecho, una pulsación larga, Mayús+F10, la tecla de menú contextual o la orden de menú contextual de las tecnologías de apoyo, y su único nombre es el aria-label que le des, igual que los menús contextuales propios de las plataformas no tienen nombre.
  • Al abrirse, el foco pasa al primer elemento habilitado. Al cerrarse, el foco vuelve al ancla si estaba dentro del menú. Flecha arriba y Flecha abajo dan la vuelta, Inicio y Fin van a los extremos, y al escribir el foco pasa al siguiente elemento cuya etiqueta empieza por las letras escritas. Enter activa y cierra. Espacio en un elemento de casilla de verificación o de opción deja el menú abierto. Escape cierra y devuelve el foco. Tab cierra y sigue adelante desde el ancla. En un submenú, la tecla de flecha hacia el inicio de la línea y Escape cierran solo ese nivel y devuelven el foco a su elemento; las teclas de flecha a lo largo de la línea que no hacen nada nunca llegan a la página que hay detrás del menú.
  • Los elementos deshabilitados se siguen alcanzando con las teclas de flecha, pero no se activan.
  • El anillo de foco se dibuja dentro del elemento, así que la superficie no puede recortarlo. Solo se muestra para el foco del teclado. Un puntero que se mueve sobre un elemento lo muestra con el cursor encima, no con foco.
  • Cada elemento tiene como mínimo la altura del objetivo mínimo, y toda la fila es su área de pulsación.
  • Los elementos crecen con un texto más grande, y la superficie permite el desplazamiento cuando ya no cabe en la pantalla.
  • Mientras la fuente de iconos todavía no puede dibujar una marca que es un glifo, como la marca de verificación, el menú no dibuja nada en su lugar, nunca la palabra «check». Conserva el ancho de la columna y sigue exponiendo el estado marcado, y muestra la marca cuando la fuente se ha cargado.
  • Las compilaciones de depuración notifican un elemento sin etiqueta, un elemento de opción fuera de un grupo, un valor de grupo que no nombra ninguno de sus elementos de opción, dos elementos de opción que comparten un valor, un grupo dentro de un grupo, un elemento con atajo y texto final a la vez, un separation o un iconSpace que el menú no define, un tercer nivel de submenú y un submenú sin elementos. También notifican un elemento de enlace que abre un contexto nuevo sin targetLabel, un href en un elemento de casilla de verificación, de opción o de submenú, un href que nombra un script o que no se puede leer, un target que el menú no define, y current en un elemento sin href. Notifican además un trigger que el menú no define.

Carencias conocidas por plataforma:

  • SwiftUI, Compose, Flutter y React Native no tienen elementos de enlace: ahí un elemento todavía no admite href.
  • SwiftUI, Compose, Flutter y React Native no tienen activador contextual: sus menús se abren solo desde un ancla.
  • SwiftUI, Compose, Flutter y React Native no tienen submenús, separation, iconSpace, markIcon ni relleno en una fila marcada. Guardan las columnas de marca e icono en todas las filas de un menú en el que algún elemento dibuje en ellas. Todavía dibujan la marca de verificación como una marca propia en un recuadro de 16, y empiezan el título de un grupo donde empiezan las etiquetas.
  • El punto donde empieza el título con m3-like y cupertino sigue la regla de Tanrei y no se ha medido frente a los originales.
  • Web: el menú no escucha la petición de cierre de la plataforma, como el gesto de retroceso de Android, así que back nunca se envía.
  • Web: aria-haspopup y aria-expanded se definen en el ancla. En un ancla que es un elemento personalizado, como un <tanrei-button> de solo icono, van en el botón que está dentro de su shadow root. aria-controls y el nombre del menú, que procede del ancla, no pueden cruzar un shadow root.
  • Web: la superficie se coloca mediante un script, no con el posicionamiento por anclas de CSS.
  • Web: un elemento de submenú no puede apuntar a su superficie con aria-controls, que no puede llegar al interior de un shadow root. En su lugar, la superficie es descendiente del elemento en el árbol de accesibilidad.
  • Web, WebKit en macOS: el elemento de un submenú abierto conserva su color de cursor encima bajo una pulsación en lugar de tomar el de pulsado.
  • SwiftUI no tiene rol de menú, de elemento de menú ni de separador, ni propiedad de atajo, ni forma de decir que el ancla abre un menú o que el menú está abierto. Cada elemento es un botón con su posición como valor («2 of 3», solo en inglés), un elemento de casilla de verificación es un conmutador, y un elemento de opción está seleccionado mientras está marcado.
  • SwiftUI nunca da el foco del teclado a un botón deshabilitado, así que las teclas de flecha pasan por alto un elemento deshabilitado en lugar de llegar a él.
  • SwiftUI presenta el menú como un popover, que se coloca solo. Los tokens solo eligen el lado: el popover mantiene su propia distancia respecto al ancla y a los bordes de la ventana, se alinea solo y se cierra con su propio movimiento. En iOS 26 recorta la superficie a sus propias esquinas, más grandes, y dibuja su propia sombra. Todavía no se ha comprobado cómo se ve esto en un dispositivo.
  • SwiftUI: Tab cierra el menú y devuelve el foco al ancla, pero el foco no puede seguir adelante desde ella en la misma pulsación de tecla.
  • SwiftUI lee el valor de un elemento de opción solo de un .tag(_:) aplicado al propio TanreiMenuOption.
  • SwiftUI: las teclas de flecha, Inicio, Fin, la escritura anticipada, Escape y Tab se prueban como reglas, todavía no con un teclado real.
  • React Native dibuja el menú en la capa de superposición de TanreiProvider, así que debe haber un TanreiProvider por encima de él. Tab cierra el menú y devuelve el foco al ancla, pero no puede seguir adelante desde ella en la misma pulsación de tecla. El siguiente Tab sí sigue adelante.
  • React Native: qué hace un lector de pantalla con un elemento que está detrás del menú abierto, y la salida del foco del menú, aún no se han comprobado en un dispositivo.
  • React Native no tiene rol de elemento de menú de casilla de verificación ni de elemento de menú de opción, ni propiedad de atajo. Todos los elementos son elementos de menú, los elementos de casilla de verificación y de opción llevan un estado marcado, y el texto final se lee como parte de la descripción.
  • React Native no puede definir propiedades en tu ancla. Dale tú accessibilityState={{ expanded: open }}.
  • React Native no sigue a un ancla que se mueve mientras el menú está abierto. Mide el ancla cuando el menú se abre y cuando la ventana cambia de tamaño.
  • React Native: las teclas de flecha, la escritura anticipada, Escape, Tab y la diferencia entre Espacio y Enter aún no se han comprobado en un dispositivo con teclado físico.
  • Compose no tiene rol de menú, de elemento de menú ni de separador, ni propiedad de atajo. La superficie es una colección, una acción no tiene rol, un elemento de casilla de verificación es una casilla de verificación y un elemento de opción es un botón de opción, y el texto final se lee como parte de la descripción. El menú no puede definir el estado expandido del ancla.
  • Compose: una pulsación en el ancla mientras el menú está abierto cuenta como una pulsación fuera. Cierra el menú con outside y no llega al ancla.
  • Compose: el foco de ventana del popup, una pulsación real fuera, el botón de retroceso, la vuelta del foco al ancla, que Tab siga adelante desde ella y TalkBack en el menú solo se han probado con Robolectric, todavía no en un dispositivo.
  • Flutter no tiene rol de grupo ni de separador, no puede dar nombre al menú a partir de su ancla ni decir que el ancla abre un menú, y no da a los elementos posición ni tamaño del conjunto. Un grupo es un contenedor que recibe su nombre de su título. El ancla sí recibe el estado expandido.
  • Flutter: qué anuncian TalkBack, VoiceOver y un lector de pantalla web para el menú, y el gesto de retroceso de Android con un menú abierto, aún no se han comprobado en un dispositivo.

Qué debes hacer tú

  • Da nombre al menú mediante su ancla. Da a un ancla que es un botón de icono una etiqueta como «Más» u «Ordenar». En React Native, pasa el mismo nombre como accessibilityLabel del menú.
  • En Compose y React Native, expón en el ancla si el menú está abierto. El menú no puede definirlo ahí.
  • Cierra el menú cuando lo pida y no muevas tú el foco cuando se cierre. El menú devuelve el foco al ancla.
  • Da nombre a un menú contextual con aria-label, como «Lienzo», y ofrece cada una de sus acciones también en otro lugar de tu producto.
  • Da una etiqueta a todos los elementos, incluidos los que muestran un icono. La etiqueta es el nombre y lo que busca la escritura anticipada.
  • Da al grupo de elementos de opción un título que diga de qué trata la elección.
  • Da una etiqueta a todos los elementos de submenú: da nombre al elemento y al submenú que abre.
  • Da a un elemento de enlace que abre una pestaña nueva un targetLabel, en el idioma de tu producto, como «se abre en una pestaña nueva». Tanrei no incluye textos propios.