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ú FAB

Menú FAB

Abre una lista breve de acciones relacionadas desde un conmutador dibujado como un botón de acción principal contraído, que se convierte en el botón de cierre de la lista mientras se muestran.

content_copyVer en Markdown
Categoría
Acciones
Tokens
comp.fab-menu
Nivel
Base

Descripción general

Web
tanrei-fab-menu
schedule
SwiftUI
TanreiFabMenu
schedule
Jetpack Compose
TanreiFabMenu
schedule
Flutter
TanreiFabMenu
schedule
React Native
FabMenu
schedule

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

No está en todos los sistemas de diseño

Lo incluyen m3-expressive-like, m3-like, registrado por @tanrei/web/m3-expressive-like, @tanrei/web/m3-like.

No está en tanrei, cupertino. Vive en la capa m3-like, que esos sistemas no extienden. A system outside this layer draws no floating action button that opens a list: its main action button opens a bottom sheet that lists the related actions. Usa Main action button, Bottom sheet en su lugar.

Un producto sobre un sistema que no lo tiene puede usarlo igualmente: inclúyelo en tu propia capa (Tu propio sistema de diseño) e importa su propio módulo. Entonces se dibuja con los tokens de tu propio sistema.

"components": {
  "include": [{ "name": "fab-menu", "from": "m3-like" }]
}
import '@tanrei/web/components/fab-menu';

Cerrado y abierto

addadd
Con m3-like

Uso

El conmutador se dibuja como un botón de acción principal contraído, con una etiqueta y un icono. Los elementos son acciones, cada una con un icono y una etiqueta, que se pasan como cada plataforma pasa los hijos repetidos. Igual que el botón de acción principal, el menú FAB no se coloca solo: tu diseño sitúa el conmutador, normalmente en la esquina inferior del lado de fin. Los elementos aparecen encima de él, en la capa de superposición de la plataforma.

<tanrei-fab-menu class="primary-action" label="Crear">
  <span slot="icon">add</span>
  <tanrei-fab-menu-item label="Documento" icon="description"></tanrei-fab-menu-item>
  <tanrei-fab-menu-item label="Carpeta" icon="create_new_folder"></tanrei-fab-menu-item>
  <tanrei-fab-menu-item label="Subir" icon="upload"></tanrei-fab-menu-item>
</tanrei-fab-menu>

<style>
  /* El elemento no coloca nada: la página pone el conmutador en la esquina inferior del lado de fin. */
  tanrei-fab-menu.primary-action {
    position: fixed;
    inset-inline-end: var(--tanrei-sys-space-space400);
    inset-block-end: var(--tanrei-sys-space-space400);
  }
</style>

<script type="module">
  // El menú FAB vive en la capa m3-like: la entrada de un sistema m3-like lo registra junto con el resto.
  import '@tanrei/web/m3-like';
  const fabMenu = document.querySelector('tanrei-fab-menu');
  // El conmutador pide abrir o cerrar; tú defines open.
  fabMenu.addEventListener('toggle', (e) => (fabMenu.open = e.detail.open));
  // close lleva el motivo: escape, outside, tab, focus-out o item. La web nunca envía back.
  fabMenu.addEventListener('close', () => (fabMenu.open = false));
  // El clic de un elemento se propaga hacia arriba y llega antes que close.
  fabMenu.querySelector('[label="Subir"]').addEventListener('click', upload);
</script>
// El menú FAB vive en la capa m3-like: está en las entradas de los sistemas que lo tienen (m3-like, m3-expressive-like) y en /all.
import { FabMenu, FabMenuItem } from '@tanrei/react/m3-like';

<FabMenu
  className="primary-action"
  label="Crear"
  open={open}
  onToggle={(e) => setOpen(e.detail.open)}
  onClose={() => setOpen(false)}
>
  <span slot="icon">add</span>
  <FabMenuItem label="Documento" icon="description" onClick={newDocument} />
  <FabMenuItem label="Carpeta" icon="create_new_folder" onClick={newFolder} />
  <FabMenuItem label="Subir" icon="upload" onClick={upload} />
</FabMenu>
import Tanrei

// Se coloca como el FAB. Tanto el conmutador como los elementos escriben en el binding.
DocumentList()
    .overlay(alignment: .bottomTrailing) {
        TanreiFabMenu("Crear", icon: "add", isPresented: $showsCreate) {
            TanreiFabMenuItem("Documento", icon: "description", action: newDocument)
            TanreiFabMenuItem("Carpeta", icon: "create_new_folder", action: newFolder)
            TanreiFabMenuItem("Subir", icon: "upload", action: upload)
        }
        .padding()
    }
import io.github.chnotchy.tanrei.components.TanreiFabMenu
import io.github.chnotchy.tanrei.components.TanreiFabMenuItem
import io.github.chnotchy.tanrei.primitives.TanreiText

// Elevación de estado: expanded lo guardas tú. Ponlo en el slot floatingActionButton de un scaffold, o colócalo tú.
TanreiFabMenu(
    expanded = expanded,
    onExpandedChange = { expanded = it },
    onDismissRequest = { expanded = false },
    contentDescription = "Crear",
    icon = { TanreiText("add") },
) {
    TanreiFabMenuItem(text = "Documento", icon = "description", onClick = ::newDocument)
    TanreiFabMenuItem(text = "Carpeta", icon = "create_new_folder", onClick = ::newFolder)
    TanreiFabMenuItem(text = "Subir", icon = "upload", onClick = ::upload)
}
import 'package:tanrei/tanrei.dart';

// El menú FAB se abre y se cierra solo mediante el controlador; onToggle y onClose lo notifican.
Scaffold(
  body: const DocumentList(),
  floatingActionButton: TanreiFabMenu(
    controller: createMenu,
    semanticLabel: 'Crear',
    icon: const Icon(AppSymbols.add),
    items: <TanreiFabMenuItem>[
      TanreiFabMenuItem(label: 'Documento', icon: const Icon(AppSymbols.description), onPressed: newDocument),
      TanreiFabMenuItem(label: 'Carpeta', icon: const Icon(AppSymbols.createNewFolder), onPressed: newFolder),
      TanreiFabMenuItem(label: 'Subir', icon: const Icon(AppSymbols.upload), onPressed: upload),
    ],
  ),
);
import { View } from 'react-native';
import { FabMenu } from '@tanrei/react-native';

// Controlado: define visible desde onToggle, y ponlo en false desde onDismiss, que recibe el motivo.
// El conmutador nunca se posiciona solo: lo coloca style.
<View style={{ flex: 1 }}>
  <DocumentList />
  <FabMenu
    icon="add"
    accessibilityLabel="Crear"
    visible={open}
    onToggle={setOpen}
    onDismiss={() => setOpen(false)}
    items={[
      { label: 'Documento', icon: 'description', onPress: newDocument },
      { label: 'Carpeta', icon: 'create_new_folder', onPress: newFolder },
      { label: 'Subir', icon: 'upload', onPress: upload },
    ]}
    style={styles.primaryAction}
  />
</View>

Especificaciones

Variantes, tamaños y estado deshabilitado

addaddaddaddadd
Con m3-like

Anatomía

Parte Descripción
1 container The toggle that opens and closes the menu, drawn as a collapsed main action button. It casts the shadow, carries hover, focus and press, and changes its colors and corners between closed and open.
2 icon The toggle's glyph: the product's icon while closed, the glyph close of the system's icon set while open. The FAB menu draws no mark of its own for it.
3 list The column of items above the toggle, end-aligned with it. It draws nothing of its own and is presented above the page.
4 item One action: a raised container holding an icon and a label. It carries its own hover, focus and press.
5 item-icon An item's glyph, before its label.
6 item-label An item's text and its accessible name.
7 focus-ring Gap-then-ring indicator shown for keyboard focus, around the toggle or the item that has it.

Propiedades

Propiedad Tipo Valor predeterminado Descripción
variant filled | tonal | surface "filled" Emphasis, as the main action button's variants of the same names: it colors the toggle and the items. A design system may add values.
size small | medium | large "medium" The toggle's size (40 / 48 / 64, square), as the main action button's. The items keep one size. A design system may add values.
open boolean false Whether the items are shown. Where open is a plain value (web, Compose, React Native) the product sets it from toggle and sets it to false from close; where it is a binding or a controller (SwiftUI, Flutter) the FAB menu opens and closes itself through it (FBM-1, FBM-15).
label string "" The accessible name of the toggle and of the list ("Create"). Required: an icon alone is not a name.
disabled boolean false Prevents opening and removes the toggle from the focus order. Hiding a FAB menu whose actions are unavailable is usually better than disabling it.

Slots

Slot Descripción
icon The toggle's glyph while closed (a glyph of the system's icon set).

Eventos

Evento Descripción
toggle The toggle asks to open or close the menu; carries the open value it asks for.
close The menu asks to be closed; carries the reason: escape, back, outside, tab, focus-out or item (in the platform's casing).

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

One action of the menu: an icon and a label on a raised container.

Propiedad Tipo Valor predeterminado Descripción
label string "" The item's text and its accessible name. Required.
icon string "" The name of the item's icon (a glyph of the system's icon set). Required.
Evento Descripción
click The item was activated by pointer, touch, keyboard or assistive technology.
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento tanrei-fab-menu-item TanreiFabMenuItem TanreiFabMenuItem TanreiFabMenuItem FabMenuItem
label · Propiedad label title text label label
icon · Propiedad icon icon icon icon icon
click · Evento click action onClick onPressed onPress
Notas A child element: <tanrei-fab-menu-item label="Upload" icon="upload">; its click bubbles. TanreiFabMenuItem(_ title:, icon:, action:) in the view builder. TanreiFabMenuItem(text, icon, onClick) called in the content lambda. TanreiFabMenuItem(label:, icon:, onPressed:) in the items: list. The element type of the items array: { label, icon, onPress }, where icon is the glyph's name.

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
FBM-1 WHEN the user activates the toggle by pointer, touch, Enter or Space THE FAB menu SHALL emit toggle exactly once with the opposite of open, and WHEN the user presses Down Arrow or Up Arrow on the toggle while open is false it SHALL emit toggle with true; the menu SHALL then open or close through open: through its binding or controller where open is one (SwiftUI, Flutter), and by the product setting open where open is a plain value (web, Compose, React Native).
FBM-2 WHILE disabled is true THE FAB menu SHALL NOT emit toggle or close, SHALL NOT make the toggle focusable, SHALL expose the disabled state to assistive technology, and SHALL draw the toggle closed and present no items, whatever open says.
FBM-3 WHILE the toggle or an item is hovered, keyboard-focused or pressed THE FAB menu SHALL render its container, icon, label, elevation and opacity from the component tokens of its variant in its own highest-precedence active state — the toggle from its closed parts (comp.fab-menu.filled.container.color for filled, and so on) or its open parts (comp.fab-menu.filled.open.container.color, and so on), an item from the item parts (comp.fab-menu.filled.item.container.color, and so on) — with precedence pressed over focused over hovered; focus gained by pointer or touch SHALL NOT select the focused state.
FBM-4 WHEN the toggle or an item receives keyboard focus THE FAB menu SHALL show the focus ring around that one's own shape, without animation; focus gained by pointer or touch SHALL NOT show it.
FBM-5 WHERE the toggle is smaller than the minimum target THE FAB menu SHALL extend its hit area on both axes to sys.target.touch for touch input and sys.target.pointer for fine pointers, without changing its visual size; THE FAB menu SHALL make each item at least that target tall, because stacked items cannot extend a hit area over each other, and each item's whole container SHALL be its hit area; a platform that cannot tell the input type SHALL use sys.target.touch.
FBM-6 WHILE disabled is true THE FAB menu SHALL render the toggle's container, icon, elevation and opacity from the disabled component tokens of its variant's closed parts.
FBM-7 WHEN variant or size names a value that the active design system does not have THE FAB menu SHALL render with the default value's tokens, and in debug builds report the unknown value.
FBM-8 WHEN the toggle is pressed by pointer, touch, Enter or Space THE toggle 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.
FBM-9 WHILE pressed THE toggle 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; icon, elevation, 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.fab-menu.motion.feedback-duration with comp.fab-menu.motion.state-easing, fades over comp.fab-menu.motion.state-duration with comp.fab-menu.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced. Each item SHALL answer its own press in the same way, within its own container and from its item parts: label, icon, elevation and opacity still come from its pressed tokens.
FBM-10 THE toggle 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. Each item SHALL ease its corners and show its keyboard focus in the same way.
FBM-11 THE FAB menu SHALL draw the toggle as a square whose side is the container size of its size (comp.fab-menu.container.size.medium for medium, and so on), with the corners of its size (comp.fab-menu.container.shape.medium, and so on) while closed and comp.fab-menu.container.shape-open while open, and its icon centred in it at the icon size of its size (comp.fab-menu.icon.size.medium, and so on) through the icon helper, with the family and fill of an unselected icon, sys.icon.weight, and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max: the icon slot's glyph while closed, and the glyph close of the system's icon set while open, not mirrored when the layout direction is right-to-left. THE FAB menu SHALL draw no mark of its own (no path, shape or character of its own) for the close glyph.
FBM-12 THE FAB menu SHALL NOT position itself: the toggle SHALL lay out at its own size where the layout that holds it places it (normally the inline-end bottom corner of the screen, clear of any navigation bar), as a main action button does (MAB-18), and the items SHALL be presented above the page rather than laid out, so that they move no content.
FBM-13 WHEN open becomes true THE FAB menu SHALL present its items in the platform's overlay layer (the web's top layer through the popover API, SwiftUI's presentation as the menu's, Compose's Popup, Flutter's Overlay, React Native's overlay layer that TanreiProvider renders) as a column above the toggle, comp.fab-menu.list.offset from its top edge and comp.fab-menu.list.gap apart, the first item at the top and every item's end edge aligned with the toggle's, and SHALL move focus to the first item, or to the last (the one nearest the toggle) when Up Arrow opened it; WHEN open becomes false THE FAB menu SHALL remove the items and, if focus was in them, return focus to the toggle. The FAB menu is an operated overlay, so it takes focus and returns it although it is not modal.
FBM-14 WHILE open is true THE FAB 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.
FBM-15 WHEN the user presses Escape or the platform's back, presses outside the items and the toggle, presses Tab or Shift+Tab on an item, or moves focus out of the items and the toggle THE FAB menu SHALL emit close once with that reason (escape, back, outside, tab, focus-out) and SHALL be closed: 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 toggle as they would have from it. A press on the toggle while open is not a press outside: it SHALL emit toggle with false (FBM-1) and no close. Whether a press outside also reaches the element under it SHALL follow the platform, as the menu's does, and be stated in its README.
FBM-16 WHEN the user activates an item by pointer, touch, Enter or Space THE FAB menu SHALL emit the item's click once and then emit close with the reason item, in that order, and SHALL be closed as FBM-15 says.
FBM-17 WHILE open is true and an item has keyboard focus THE FAB menu SHALL move focus to the next item with Down Arrow and to the previous one with Up Arrow, wrapping at the ends, and to the first and last item with Home and End.
FBM-18 THE FAB menu SHALL draw each item as a container at least comp.fab-menu.item.height tall and as wide as its content, with corners comp.fab-menu.item.shape eased by sys.shape.corner-smoothing, holding its icon and then its label comp.fab-menu.item.gap apart, padded by comp.fab-menu.item.padding-inline-start at the start and comp.fab-menu.item.padding-inline-end at the end; the label SHALL be set in comp.fab-menu.item.label.typescale on one line while it fits and SHALL wrap rather than truncate, the item growing with it; the icon SHALL be drawn at comp.fab-menu.item.icon.size through the icon helper with the family and fill of an unselected icon, sys.icon.weight, and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max; and the item SHALL cast its item elevation (comp.fab-menu.filled.item.container.elevation for filled, and so on) outside its container without clipping it.
FBM-19 WHEN the FAB menu opens THE FAB menu SHALL change the toggle's colors and corners and cross-fade its icon to the glyph close (a cross-fade of two glyphs, neither drawn along a path) over comp.fab-menu.motion.open-duration with comp.fab-menu.motion.open-easing, and fade and grow each item in from its end edge over the same, starting with the item nearest the toggle and delaying each next one by comp.fab-menu.motion.stagger; WHEN it closes it SHALL reverse the toggle's change and fade all items out together over comp.fab-menu.motion.close-duration with comp.fab-menu.motion.close-easing; WHERE motion is reduced it SHALL change and show or hide everything at once.
FBM-20 THE FAB menu SHALL expose the toggle as a button named by label that opens a menu, with whether the menu is open where the platform has those properties, and while open SHALL expose the items as a menu named by label, each a menu item named by its own label with its position and the number of items; the icons SHALL be hidden.
FBM-21 THE FAB menu SHALL cast the toggle's elevation, of its variant and state (comp.fab-menu.filled.container.elevation for filled at rest, comp.fab-menu.filled.open.container.elevation while open, and so on), outside its container without clipping it, and WHEN the elevation or the colors of the toggle or an item change between interaction states SHALL animate the change over comp.fab-menu.motion.state-duration with comp.fab-menu.motion.state-easing, changing them at once where motion is reduced.
FBM-22 WHEN the layout direction is right-to-left THE FAB menu SHALL align the items' end edges with the toggle's left edge, mirror the order of each item's icon and label (a glyph itself only as FBM-26 says) and apply its paddings by logical start and end, and SHALL grow a ripple from the point of contact as in left-to-right.
FBM-23 WHILE label is not empty THE FAB menu SHALL expose it as the toggle's accessible name, over a name the product gives it on the element (aria-label or aria-labelledby). WHILE label is empty and the product gives the FAB menu a name of its own on the element — aria-label, or aria-labelledby naming an element — THE FAB menu SHALL expose that name as the toggle's accessible name and name the list by the toggle. WHILE label is empty and the product gives it none, an item's label or icon is empty, or the menu holds fewer than two or more than six items, THE FAB menu SHALL still render, and in debug builds report it.
FBM-24 IF the icon set cannot draw the close glyph yet (its font is still loading or failed to load) WHILE open is true THE FAB menu SHALL draw nothing in the toggle's icon box, never the glyph's name as text, SHALL keep the toggle's size, colors, corners, name and expanded state, and SHALL draw the glyph once the icon set can, without the cross-fade of FBM-19.
FBM-25 THE FAB 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 FAB menu is shown THE FAB menu SHALL draw itself for the new direction at once, without a motion of its own, keeping its open state and its focus.
FBM-26 WHERE the layout direction is right-to-left THE FAB menu SHALL draw the glyph of its icon slot while closed and the icon of each item 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 FBM-22 gives. THE FAB 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 close glyph of the open toggle is not marked and is never mirrored (FBM-11).
FBM-27 THE FAB menu SHALL lay out the label of each item 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 FAB menu SHALL still place each label and align its lines by the layout direction, not by the text's own.
FBM-28 WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE FAB 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 the element that carries its toggle's role — an id reference by the elements it names in the host's tree or a tree around it, an error message as part of the description while the toggle is invalid — except those the FAB menu owns (aria-haspopup, aria-expanded, aria-controls, aria-disabled), which it SHALL move and not expose. WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE FAB menu SHALL take it off that element. An item's host carries its menu item role itself (FBM-20), so what the product writes on it is not moved, but for a name: WHILE an item's label is not empty THE FAB menu SHALL hold the product's aria-label and aria-labelledby on that host as their data-aria- twins, so that label is the item's whole name although the platform gives aria-labelledby the precedence, and WHEN label is emptied SHALL give them back as they were written; WHILE an item's label is empty a name the product gives so is the item's name and SHALL NOT be reported as having none in debug builds (FBM-23); WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it to null, THE FAB menu SHALL take it away. WHEN the product sets an aria- attribute the FAB menu owns THE FAB menu SHALL report it in debug builds.

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-fab-menu TanreiFabMenu TanreiFabMenu TanreiFabMenu FabMenu
variant · Propiedad variant variant variant variant variant
size · Propiedad size size size size size
open · Propiedad open isPresented expanded controller visible
label · Propiedad label label contentDescription semanticLabel accessibilityLabel
disabled · Propiedad disabled disabled enabled enabled disabled
toggle · Evento toggle isPresented onExpandedChange onToggle onToggle
close · Evento close isPresented onDismissRequest onClose onDismiss
Notas <tanrei-fab-menu label="Create" variant size open> with a slot="icon" glyph and tanrei-fab-menu-item children. The toggle is a <button> in the shadow root with aria-haspopup="menu", aria-expanded and aria-controls; the items are a manual popover in the top layer with role menu, placed with CSS anchor positioning and a script fallback, as the menu is. toggle is a CustomEvent whose detail.open is the value asked for (not the popover's ToggleEvent, which does not leave the shadow root); close carries detail.reason. The page positions the element. The inline attribute, for tests, draws the open items in place (FBM-13). The open toggle's glyph is the icon helper's close in the icon part (FBM-11). It lives in the m3-like layer: @tanrei/web/m3-like registers it (with @tanrei/web/components/fab-menu, its own module), and @tanrei/web does not; its tokens are in the stylesheet of the m3-like systems (@tanrei/web/systems/m3-like.css, m3-expressive-like.css) and of a product that includes it. TanreiFabMenu(_ label:, icon:, variant:, size:, isPresented: Binding<Bool>) { TanreiFabMenuItem … }, placed by the caller as the main action button is (an .overlay(alignment: .bottomTrailing)). The items are presented as TanreiMenu presents its surface, with a clear background, so a press outside closes them. Writing the binding is toggle and close. disabled follows .disabled(_:). iOS has no menu role: the items are buttons in a container named by label. Without an installed icon font it strokes the close glyph as a shape of its own (TanreiChipGlyph(kind: .close)) instead of drawing nothing (FBM-11, FBM-24). The FAB menu lives in the m3-like layer, and only the systems that descend from it have its tokens; whether this platform ships it in a package of its own is decided when the platform catches up with the web. TanreiFabMenu(expanded, onExpandedChange, onDismissRequest, contentDescription, icon, variant, size, enabled) { TanreiFabMenuItem(…) }, placed through Scaffold's floatingActionButton slot or by the caller; the items are shown in a Popup. State hoisting: the caller keeps expanded, sets it from onExpandedChange and to false from onDismissRequest, which carries the reason. Material 3's FloatingActionButtonMenu is not used. enabled is the inverse flag. Without an installed icon font it strokes the close glyph as a vector of its own (TanreiDrawnGlyph) instead of drawing nothing (FBM-11, FBM-24). The FAB menu lives in the m3-like layer, and only the systems that descend from it have its tokens; whether this platform ships it in a package of its own is decided when the platform catches up with the web. TanreiFabMenu(controller:, semanticLabel:, icon:, variant:, size:, enabled:, items: [TanreiFabMenuItem(…)], onToggle:, onClose:), built with the widgets library and placed through Scaffold.floatingActionButton or by the caller; the items are shown in the Overlay. The controller is a TanreiMenuController (the Tanrei menu's own), passed or created internally; it is open, so the FAB menu opens and closes itself; onToggle and onClose report it. enabled is a flag here, because the toggle's press is the component's own. Lagging behind FBM-24: it writes the close glyph as the Material Symbols code point U+E5CD (tanreiCloseGlyph), and without the icon font it draws whatever the fallback face has there instead of nothing. The FAB menu lives in the m3-like layer, and only the systems that descend from it have its tokens; whether this platform ships it in a package of its own is decided when the platform catches up with the web. <FabMenu icon="add" accessibilityLabel visible onToggle onDismiss items={FabMenuItem[]} variant size disabled>; the caller positions it. The items are 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 toggle (measureInWindow); Android's back button (BackHandler) is close. Controlled: the caller sets visible from onToggle and to false from onDismiss, which carries the reason. Lagging behind FBM-24: without the icon font the ligature close prints as a word. The FAB menu lives in the m3-like layer, and only the systems that descend from it have its tokens; whether this platform ships it in a package of its own is decided when the platform catches up with the web.

Tokens

Token Alias de Valor resuelto (claro)
--tanrei-comp-fab-menu-container-shape-open sys.shape.corner.full 9999px
--tanrei-comp-fab-menu-container-shape-large sys.shape.corner.extra-large 28px
--tanrei-comp-fab-menu-container-shape-medium sys.shape.corner.large 16px
--tanrei-comp-fab-menu-container-shape-small sys.shape.corner.medium 12px
--tanrei-comp-fab-menu-container-size-large — 96px
--tanrei-comp-fab-menu-container-size-medium — 56px
--tanrei-comp-fab-menu-container-size-small — 40px
--tanrei-comp-fab-menu-disabled-opacity — 1
--tanrei-comp-fab-menu-filled-container-color sys.color.primary #6750a4
--tanrei-comp-fab-menu-filled-container-elevation sys.elevation.level3 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-disabled-container-color — #1d1b20 · 12%
--tanrei-comp-fab-menu-filled-disabled-container-elevation —
--tanrei-comp-fab-menu-filled-disabled-icon-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-filled-disabled-item-container-color — #1d1b20 · 12%
--tanrei-comp-fab-menu-filled-disabled-item-container-elevation —
--tanrei-comp-fab-menu-filled-disabled-item-icon-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-filled-disabled-item-label-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-filled-disabled-item-state-layer-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-filled-disabled-open-container-color — #1d1b20 · 12%
--tanrei-comp-fab-menu-filled-disabled-open-container-elevation —
--tanrei-comp-fab-menu-filled-disabled-open-icon-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-filled-disabled-open-state-layer-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-filled-disabled-state-layer-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-filled-focused-container-color — #7965af
--tanrei-comp-fab-menu-filled-focused-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-focused-icon-color — #ffffff
--tanrei-comp-fab-menu-filled-focused-item-container-color — #7965af
--tanrei-comp-fab-menu-filled-focused-item-container-elevation — 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-focused-item-icon-color — #ffffff
--tanrei-comp-fab-menu-filled-focused-item-label-color — #ffffff
--tanrei-comp-fab-menu-filled-focused-item-state-layer-color — #ffffff
--tanrei-comp-fab-menu-filled-focused-open-container-color — #d0c6df
--tanrei-comp-fab-menu-filled-focused-open-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-focused-open-icon-color — #1d192b
--tanrei-comp-fab-menu-filled-focused-open-state-layer-color — #1d192b
--tanrei-comp-fab-menu-filled-focused-state-layer-color — #ffffff
--tanrei-comp-fab-menu-filled-hovered-container-color — #735eab
--tanrei-comp-fab-menu-filled-hovered-container-elevation — 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-hovered-icon-color — #ffffff
--tanrei-comp-fab-menu-filled-hovered-item-container-color — #735eab
--tanrei-comp-fab-menu-filled-hovered-item-container-elevation — 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-hovered-item-icon-color — #ffffff
--tanrei-comp-fab-menu-filled-hovered-item-label-color — #ffffff
--tanrei-comp-fab-menu-filled-hovered-item-state-layer-color — #ffffff
--tanrei-comp-fab-menu-filled-hovered-open-container-color — #d8cee8
--tanrei-comp-fab-menu-filled-hovered-open-container-elevation — 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-hovered-open-icon-color — #1d192b
--tanrei-comp-fab-menu-filled-hovered-open-state-layer-color — #1d192b
--tanrei-comp-fab-menu-filled-hovered-state-layer-color — #ffffff
--tanrei-comp-fab-menu-filled-icon-color sys.color.on-primary #ffffff
--tanrei-comp-fab-menu-filled-item-container-color sys.color.primary #6750a4
--tanrei-comp-fab-menu-filled-item-container-elevation sys.elevation.level2 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-item-icon-color sys.color.on-primary #ffffff
--tanrei-comp-fab-menu-filled-item-label-color sys.color.on-primary #ffffff
--tanrei-comp-fab-menu-filled-item-state-layer-color comp.fab-menu.filled.item.label.color #ffffff
--tanrei-comp-fab-menu-filled-open-container-color sys.color.secondary-container #e8def8
--tanrei-comp-fab-menu-filled-open-container-elevation sys.elevation.level3 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-open-icon-color sys.color.on-secondary-container #1d192b
--tanrei-comp-fab-menu-filled-open-state-layer-color comp.fab-menu.filled.open.icon.color #1d192b
--tanrei-comp-fab-menu-filled-pressed-container-color — #7965af
--tanrei-comp-fab-menu-filled-pressed-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-pressed-icon-color — #ffffff
--tanrei-comp-fab-menu-filled-pressed-item-container-color — #7965af
--tanrei-comp-fab-menu-filled-pressed-item-container-elevation — 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-pressed-item-icon-color — #ffffff
--tanrei-comp-fab-menu-filled-pressed-item-label-color — #ffffff
--tanrei-comp-fab-menu-filled-pressed-item-state-layer-color — #ffffff
--tanrei-comp-fab-menu-filled-pressed-open-container-color — #d0c6df
--tanrei-comp-fab-menu-filled-pressed-open-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-filled-pressed-open-icon-color — #1d192b
--tanrei-comp-fab-menu-filled-pressed-open-state-layer-color — #1d192b
--tanrei-comp-fab-menu-filled-pressed-state-layer-color — #ffffff
--tanrei-comp-fab-menu-filled-state-layer-color comp.fab-menu.filled.icon.color #ffffff
--tanrei-comp-fab-menu-focused-opacity — 1
--tanrei-comp-fab-menu-hovered-opacity — 1
--tanrei-comp-fab-menu-icon-size-large sys.icon.large 36px
--tanrei-comp-fab-menu-icon-size-medium sys.icon.medium 24px
--tanrei-comp-fab-menu-icon-size-small sys.icon.small 24px
--tanrei-comp-fab-menu-item-gap sys.space.space100 8px
--tanrei-comp-fab-menu-item-height — 56px
--tanrei-comp-fab-menu-item-icon-size sys.icon.small 24px
--tanrei-comp-fab-menu-item-padding-inline-end sys.space.space250 20px
--tanrei-comp-fab-menu-item-padding-inline-start sys.space.space200 16px
--tanrei-comp-fab-menu-item-shape sys.shape.corner.large 9999px
--tanrei-comp-fab-menu-list-gap sys.space.space100 4px
--tanrei-comp-fab-menu-list-offset sys.space.space100 8px
--tanrei-comp-fab-menu-motion-close-duration sys.motion.duration.short3 150ms
--tanrei-comp-fab-menu-motion-close-easing sys.motion.easing.emphasized-accelerate cubic-bezier(0.3, 0, 0.8, 0.15)
--tanrei-comp-fab-menu-motion-feedback-duration sys.motion.role.feedback 450ms
--tanrei-comp-fab-menu-motion-open-duration sys.motion.duration.medium1 250ms
--tanrei-comp-fab-menu-motion-open-easing sys.motion.easing.emphasized-decelerate cubic-bezier(0.05, 0.7, 0.1, 1)
--tanrei-comp-fab-menu-motion-stagger sys.motion.duration.stagger 100ms
--tanrei-comp-fab-menu-motion-state-duration sys.motion.role.state.default 150ms
--tanrei-comp-fab-menu-motion-state-easing sys.motion.easing.state cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-fab-menu-pressed-opacity — 1
--tanrei-comp-fab-menu-surface-container-color sys.color.surface-container-lowest #ece6f0
--tanrei-comp-fab-menu-surface-container-elevation sys.elevation.level3 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-disabled-container-color — #1d1b20 · 12%
--tanrei-comp-fab-menu-surface-disabled-container-elevation —
--tanrei-comp-fab-menu-surface-disabled-icon-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-surface-disabled-item-container-color — #1d1b20 · 12%
--tanrei-comp-fab-menu-surface-disabled-item-container-elevation —
--tanrei-comp-fab-menu-surface-disabled-item-icon-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-surface-disabled-item-label-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-surface-disabled-item-state-layer-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-surface-disabled-open-container-color — #1d1b20 · 12%
--tanrei-comp-fab-menu-surface-disabled-open-container-elevation —
--tanrei-comp-fab-menu-surface-disabled-open-icon-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-surface-disabled-open-state-layer-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-surface-disabled-state-layer-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-surface-focused-container-color — #dcd4e7
--tanrei-comp-fab-menu-surface-focused-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-focused-icon-color — #6750a4
--tanrei-comp-fab-menu-surface-focused-item-container-color — #d3ced7
--tanrei-comp-fab-menu-surface-focused-item-container-elevation — 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-focused-item-icon-color — #6750a4
--tanrei-comp-fab-menu-surface-focused-item-label-color — #1d1b20
--tanrei-comp-fab-menu-surface-focused-item-state-layer-color — #1d1b20
--tanrei-comp-fab-menu-surface-focused-open-container-color — #d0c6df
--tanrei-comp-fab-menu-surface-focused-open-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-focused-open-icon-color — #1d192b
--tanrei-comp-fab-menu-surface-focused-open-state-layer-color — #1d192b
--tanrei-comp-fab-menu-surface-focused-state-layer-color — #6750a4
--tanrei-comp-fab-menu-surface-hovered-container-color — #e1daea
--tanrei-comp-fab-menu-surface-hovered-container-elevation — 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-hovered-icon-color — #6750a4
--tanrei-comp-fab-menu-surface-hovered-item-container-color — #dbd6df
--tanrei-comp-fab-menu-surface-hovered-item-container-elevation — 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-hovered-item-icon-color — #6750a4
--tanrei-comp-fab-menu-surface-hovered-item-label-color — #1d1b20
--tanrei-comp-fab-menu-surface-hovered-item-state-layer-color — #1d1b20
--tanrei-comp-fab-menu-surface-hovered-open-container-color — #d8cee8
--tanrei-comp-fab-menu-surface-hovered-open-container-elevation — 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-hovered-open-icon-color — #1d192b
--tanrei-comp-fab-menu-surface-hovered-open-state-layer-color — #1d192b
--tanrei-comp-fab-menu-surface-hovered-state-layer-color — #6750a4
--tanrei-comp-fab-menu-surface-icon-color sys.color.primary #6750a4
--tanrei-comp-fab-menu-surface-item-container-color sys.color.surface-container-lowest #ece6f0
--tanrei-comp-fab-menu-surface-item-container-elevation sys.elevation.level2 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-item-icon-color sys.color.primary #6750a4
--tanrei-comp-fab-menu-surface-item-label-color sys.color.on-surface #1d1b20
--tanrei-comp-fab-menu-surface-item-state-layer-color comp.fab-menu.surface.item.label.color #1d1b20
--tanrei-comp-fab-menu-surface-open-container-color sys.color.secondary-container #e8def8
--tanrei-comp-fab-menu-surface-open-container-elevation sys.elevation.level3 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-open-icon-color sys.color.on-secondary-container #1d192b
--tanrei-comp-fab-menu-surface-open-state-layer-color comp.fab-menu.surface.open.icon.color #1d192b
--tanrei-comp-fab-menu-surface-pressed-container-color — #dcd4e7
--tanrei-comp-fab-menu-surface-pressed-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-pressed-icon-color — #6750a4
--tanrei-comp-fab-menu-surface-pressed-item-container-color — #d3ced7
--tanrei-comp-fab-menu-surface-pressed-item-container-elevation — 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-pressed-item-icon-color — #6750a4
--tanrei-comp-fab-menu-surface-pressed-item-label-color — #1d1b20
--tanrei-comp-fab-menu-surface-pressed-item-state-layer-color — #1d1b20
--tanrei-comp-fab-menu-surface-pressed-open-container-color — #d0c6df
--tanrei-comp-fab-menu-surface-pressed-open-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-surface-pressed-open-icon-color — #1d192b
--tanrei-comp-fab-menu-surface-pressed-open-state-layer-color — #1d192b
--tanrei-comp-fab-menu-surface-pressed-state-layer-color — #6750a4
--tanrei-comp-fab-menu-surface-state-layer-color comp.fab-menu.surface.icon.color #6750a4
--tanrei-comp-fab-menu-tonal-container-color sys.color.secondary-container #eaddff
--tanrei-comp-fab-menu-tonal-container-elevation sys.elevation.level3 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-disabled-container-color — #1d1b20 · 12%
--tanrei-comp-fab-menu-tonal-disabled-container-elevation —
--tanrei-comp-fab-menu-tonal-disabled-icon-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-tonal-disabled-item-container-color — #1d1b20 · 12%
--tanrei-comp-fab-menu-tonal-disabled-item-container-elevation —
--tanrei-comp-fab-menu-tonal-disabled-item-icon-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-tonal-disabled-item-label-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-tonal-disabled-item-state-layer-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-tonal-disabled-open-container-color — #1d1b20 · 12%
--tanrei-comp-fab-menu-tonal-disabled-open-container-elevation —
--tanrei-comp-fab-menu-tonal-disabled-open-icon-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-tonal-disabled-open-state-layer-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-tonal-disabled-state-layer-color — #1d1b20 · 38%
--tanrei-comp-fab-menu-tonal-focused-container-color — #d2c2ec
--tanrei-comp-fab-menu-tonal-focused-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-focused-icon-color — #21005d
--tanrei-comp-fab-menu-tonal-focused-item-container-color — #d2c2ec
--tanrei-comp-fab-menu-tonal-focused-item-container-elevation — 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-focused-item-icon-color — #21005d
--tanrei-comp-fab-menu-tonal-focused-item-label-color — #21005d
--tanrei-comp-fab-menu-tonal-focused-item-state-layer-color — #21005d
--tanrei-comp-fab-menu-tonal-focused-open-container-color — #7965af
--tanrei-comp-fab-menu-tonal-focused-open-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-focused-open-icon-color — #ffffff
--tanrei-comp-fab-menu-tonal-focused-open-state-layer-color — #ffffff
--tanrei-comp-fab-menu-tonal-focused-state-layer-color — #21005d
--tanrei-comp-fab-menu-tonal-hovered-container-color — #dacbf2
--tanrei-comp-fab-menu-tonal-hovered-container-elevation — 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-hovered-icon-color — #21005d
--tanrei-comp-fab-menu-tonal-hovered-item-container-color — #dacbf2
--tanrei-comp-fab-menu-tonal-hovered-item-container-elevation — 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-hovered-item-icon-color — #21005d
--tanrei-comp-fab-menu-tonal-hovered-item-label-color — #21005d
--tanrei-comp-fab-menu-tonal-hovered-item-state-layer-color — #21005d
--tanrei-comp-fab-menu-tonal-hovered-open-container-color — #735eab
--tanrei-comp-fab-menu-tonal-hovered-open-container-elevation — 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-hovered-open-icon-color — #ffffff
--tanrei-comp-fab-menu-tonal-hovered-open-state-layer-color — #ffffff
--tanrei-comp-fab-menu-tonal-hovered-state-layer-color — #21005d
--tanrei-comp-fab-menu-tonal-icon-color sys.color.on-secondary-container #21005d
--tanrei-comp-fab-menu-tonal-item-container-color sys.color.secondary-container #eaddff
--tanrei-comp-fab-menu-tonal-item-container-elevation sys.elevation.level2 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-item-icon-color sys.color.on-secondary-container #21005d
--tanrei-comp-fab-menu-tonal-item-label-color sys.color.on-secondary-container #21005d
--tanrei-comp-fab-menu-tonal-item-state-layer-color comp.fab-menu.tonal.item.label.color #21005d
--tanrei-comp-fab-menu-tonal-open-container-color sys.color.primary #6750a4
--tanrei-comp-fab-menu-tonal-open-container-elevation sys.elevation.level3 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-open-icon-color sys.color.on-primary #ffffff
--tanrei-comp-fab-menu-tonal-open-state-layer-color comp.fab-menu.tonal.open.icon.color #ffffff
--tanrei-comp-fab-menu-tonal-pressed-container-color — #d2c2ec
--tanrei-comp-fab-menu-tonal-pressed-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-pressed-icon-color — #21005d
--tanrei-comp-fab-menu-tonal-pressed-item-container-color — #d2c2ec
--tanrei-comp-fab-menu-tonal-pressed-item-container-elevation — 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-pressed-item-icon-color — #21005d
--tanrei-comp-fab-menu-tonal-pressed-item-label-color — #21005d
--tanrei-comp-fab-menu-tonal-pressed-item-state-layer-color — #21005d
--tanrei-comp-fab-menu-tonal-pressed-open-container-color — #7965af
--tanrei-comp-fab-menu-tonal-pressed-open-container-elevation — 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15)
--tanrei-comp-fab-menu-tonal-pressed-open-icon-color — #ffffff
--tanrei-comp-fab-menu-tonal-pressed-open-state-layer-color — #ffffff
--tanrei-comp-fab-menu-tonal-pressed-state-layer-color — #21005d
--tanrei-comp-fab-menu-tonal-state-layer-color comp.fab-menu.tonal.icon.color #21005d
--tanrei-comp-fab-menu-item-label-typescale sys.typescale.label 500 16px/1.5

Fixtures

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

Generado a partir de specs/fab-menu.spec.json, tokens/systems/m3-like/comp/fab-menu.tokens.json, design/tokens/m3-like/

Pautas

Cuándo usarlo

Usa un menú FAB cuando la acción principal de una pantalla se presenta en unas pocas formas relacionadas: un documento nuevo, una carpeta o una subida; una foto o una nota. El conmutador mantiene al alcance la única acción flotante de la pantalla, y al pulsarlo muestra esas formas como acciones con etiqueta justo encima de él.

Cuando solo hay una acción, usa un botón de acción principal. Cuando las opciones son muchas, o necesitan descripciones, usa un menú abierto desde un botón, o una hoja. Cuando una acción pertenece a un elemento y no a toda la pantalla, pon un botón junto a ese elemento.

Cómo elegir una variante

Variante Énfasis Úsala para
Filled (predeterminada) El más alto Las acciones principales de la pantalla con toda su fuerza.
Tonal Alto Una pantalla en la que el contenido debe llevar la delantera.
Surface Neutro Un menú FAB sobre un contenido que ya tiene color, como un mapa o una foto.

La variante da color por igual al conmutador y a los elementos. Mientras está abierto, el conmutador toma el otro aspecto de la variante, se redondea hasta ser un círculo y muestra un glifo de cierre, de modo que abierto nunca se parece a cerrado. El tamaño solo afecta al conmutador (40, 48 o 64): los elementos tienen un único tamaño.

Abrir y cerrar

El menú FAB es una superposición sobre la que se actúa y que no es modal, como el menú. Las personas actúan dentro de él, así que recibe el foco y lo devuelve, pero no se bloquea nada más de la pantalla: no hay velo ni retención del foco.

  • Apertura. El conmutador pide abrir con toggle. Los elementos aparecen encima de él, alineados con su lado de fin, uno tras otro empezando por el más cercano, y el foco pasa al primer elemento, arriba. Si se abre con Flecha arriba, el foco va en cambio al último elemento, el que está junto al conmutador.
  • Cierre. Escape, la acción de retroceso de la plataforma, una pulsación fuera de los elementos y del conmutador, Tab o la salida del foco piden cerrar con close y el motivo. Activar un elemento emite primero su clic y después close con el motivo item. Una pulsación en el conmutador mientras está abierto es una conmutación, no una pulsación fuera.
  • Devolución del foco. Cuando los elementos se cierran y el foco estaba entre ellos, el foco vuelve al conmutador. Tab desde un elemento los cierra y avanza a partir del conmutador, como habría hecho desde él.
  • Quién cierra. En la web, Compose y React Native defines open (expanded, visible) desde toggle y lo vuelves a poner en false desde close. En SwiftUI y Flutter el menú FAB se abre y se cierra solo mediante su binding o su controlador.

Recomendado

  • Da al conmutador una etiqueta que nombre lo que sus acciones tienen en común, como «Crear». Conserva ese nombre mientras está abierto. Su estado expandido le dice a un lector de pantalla que ahora cierra la lista.
  • Da a cada elemento una etiqueta corta y un icono. La etiqueta es lo que las personas leen; el icono solo las ayuda a encontrarla.
  • Limítate a entre dos y seis elementos. Ordénalos como los recorre la vista: el primero se dibuja arriba, y el último queda justo encima del conmutador.
  • Deja espacio al final del contenido que se desplaza, para que el conmutador nunca tape de forma permanente el último elemento.

Evita

  • No deshabilites un elemento. Deja fuera de la lista una acción que no esté disponible. No existe el elemento deshabilitado.
  • No cambies el nombre del conmutador a «Cerrar» mientras está abierto, ni cambies tú su icono. El componente se encarga de ambas cosas.
  • No pongas un menú FAB y un botón de acción principal del mismo tamaño en una misma pantalla. Una pantalla tiene una sola acción flotante.
  • No uses un menú FAB para acciones menores, destructivas o de configuración.

Accesibilidad

Rol
button
Etiquetado
label names the toggle and the list ("Create"). Each item is named by its label. Icons are decorative. The toggle keeps its name while open; its expanded state says it is open.
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
Tab / Shift+Tab Moves focus to the toggle; from an item, closes the menu and moves focus on from the toggle.
Enter, Space / Down Arrow on the toggle Opens the menu (Enter and Space also close it); focus moves to the first item.
Up Arrow on the toggle Opens the menu; focus moves to the last item, the one nearest the toggle.
Down Arrow / Up Arrow Moves focus to the next or previous item, wrapping at the ends.
Home / End Moves focus to the first or last item.
Enter / Space on an item Activates the item and closes the menu.
Escape Closes the menu and returns focus to the toggle.

Notas

  • Focus: the FAB menu is an operated overlay that is not modal, like the menu. It moves focus in when it opens and returns it to the toggle when it closes, but does not keep it: Tab from an item closes it.
  • Its place in the focus and reading order is where the layout puts the toggle in the element tree, not where it is drawn, as for the main action button.
  • Compose has no menu role: the items are buttons in a traversal group named by label, with their position in their collection info.
  • Text size does not change the toggle: an icon is not text. The items grow with enlarged text and wrap their labels.
  • 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.

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

De qué se encarga el componente

  • El conmutador es un botón cuyo nombre es la etiqueta. Expone que abre un menú y si el menú está abierto, donde la plataforma tiene esas propiedades, y conserva su nombre mientras está abierto.
  • Mientras está abierto, los elementos son un menú con esa misma etiqueta como nombre, y cada uno es un elemento de menú con su propia etiqueta como nombre, con su posición y el número de elementos. Los iconos quedan ocultos.
  • Enter, Espacio o Flecha abajo en el conmutador abren el menú y llevan el foco al primer elemento. Flecha arriba lo abre y lleva el foco al último elemento. Flecha abajo y Flecha arriba se mueven entre los elementos y dan la vuelta, y Inicio y Fin llegan a los extremos. Enter o Espacio en un elemento lo activan y cierran el menú. Escape cierra el menú y devuelve el foco al conmutador.
  • El menú no es modal: no retiene el foco, no vuelve inerte el resto de la pantalla y no dibuja ningún velo. Tab desde un elemento lo cierra.
  • El área de pulsación del conmutador alcanza el objetivo mínimo en todos los tamaños. Cada elemento tiene al menos la altura de un objetivo, y todo su contenedor es su área de pulsación.
  • El conmutador no crece con el tamaño del texto, porque un icono no es texto. Los elementos crecen y ajustan sus etiquetas en varias líneas.
  • Donde el movimiento está reducido, el conmutador cambia y los elementos aparecen y desaparecen de inmediato.
  • Las compilaciones de depuración notifican la falta de la etiqueta del conmutador, un elemento sin etiqueta o sin icono, y menos de dos o más de seis elementos.

Carencias conocidas por plataforma:

  • Web: la web nunca envía back. Escape es su petición de cierre.
  • Web: los elementos se colocan mediante un script respecto al conmutador, no con el posicionamiento por anclas de CSS como dice la nota para la web de la especificación, igual que ocurre con el menú.
  • Una pulsación fuera cierra los elementos en todas partes. En la web y Flutter llega además a lo que hay debajo. En SwiftUI, Compose y React Native no.
  • SwiftUI no tiene rol de menú ni de elemento de menú, ni forma de decir que el conmutador abre un menú o que el menú está abierto. Los elementos son botones en un contenedor cuyo nombre es la etiqueta, cada uno con su posición («2 of 3», solo en inglés) como valor.
  • SwiftUI presenta los elementos en un popover, que se coloca solo. Dibuja su propia burbuja y su propia flecha detrás de los elementos, centra la columna en la esquina del conmutador en lugar de alinearla con el borde de fin del conmutador, y la recorta en el sistema predeterminado; list.offset no define la distancia. El popover se sustituirá por la capa de superposición de Tanrei.
  • SwiftUI: Tab cierra los elementos y devuelve el foco al conmutador, pero el foco no puede avanzar desde él en la misma pulsación de tecla.
  • SwiftUI: el marco de accesibilidad de un elemento incluye su sombra dibujada, así que los marcos de los elementos vecinos se solapan unos pocos puntos.
  • SwiftUI: un SF Symbol más ancho que alto, como folder.badge.plus, sobresale de la caja de icono del elemento.
  • SwiftUI: las teclas del conmutador y de los elementos, y la entrada y la devolución del foco, se prueban como reglas, todavía no con un teclado real.
  • Compose no tiene rol de menú ni ninguna propiedad que diga que un botón abre un menú. Los elementos son botones en un grupo de recorrido cuyo nombre es la etiqueta, con su posición en su información de colección, y las acciones de expandir y contraer del conmutador dicen si los abre o los cierra.
  • Flutter no expone en el conmutador que tiene un elemento emergente (has-popup), ni la posición en el menú ni el número de elementos.
  • React Native no tiene ninguna propiedad que diga que un botón abre un menú. El conmutador solo expone si el menú está abierto.
  • React Native dibuja los elementos en la capa de superposición de TanreiProvider, así que debe haber un TanreiProvider por encima. Tab cierra los elementos y devuelve el foco al conmutador, y el siguiente Tab avanza desde él.
  • React Native: la colocación de los elementos, su entrada escalonada, la entrada y la salida del foco, y la pulsación fuera todavía no se han comprobado en un dispositivo.

Qué debes hacer tú

  • Escribe una etiqueta para el conmutador y para cada elemento. Un icono solo no es un nombre.
  • Pon el menú FAB en el árbol de elementos en el lugar al que pertenecen sus acciones. La tecnología de apoyo lee el conmutador en el orden del árbol, no donde se dibuja.
  • En la web, Compose y React Native, define open desde toggle y ponlo en false desde close. El menú FAB solo pide.
  • No muevas tú el foco cuando se cierra. El menú FAB devuelve el foco al conmutador.
  • Mantén el conmutador apartado de la barra de navegación y deja espacio encima de él para los elementos.