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
InicioComponentesBarra de navegación

Barra de navegación

Permite moverse entre tres y cinco destinos de nivel superior desde el borde inferior de una pantalla compacta, y muestra cuál es el actual.

content_copyVer en Markdown
Categoría
Navegación
Tokens
comp.navigation-bar
Nivel
Base

Descripción general

Web
tanrei-navigation-bar
schedule
SwiftUI
TanreiNavigationBar
schedule
Jetpack Compose
TanreiNavigationBar
schedule
Flutter
TanreiNavigationBar
schedule
React Native
NavigationBar
schedule
Variantes que no se incluyen en
cupertino: filled · m3-expressive-like: filled · m3-like: filled

Generado a partir de docs/status/status.json, specs/navigation-bar.spec.json

Una especificación revisada que aún no está en todas las plataformas

Esta página describe la barra de navegación tal como la define ahora la especificación, tomando como modelo las pestañas. El indicador activo es toda la parte igual de la barra que corresponde al destino actual, retirada de los bordes de la barra, no una píldora que se ciñe al icono y a la etiqueta. Cuando la selección cambia, se desliza hasta el nuevo destino con su tamaño completo, y un arrastre a lo largo de la barra lo lleva con el puntero. El paso del cursor, la pulsación y el foco se dibujan en ese mismo recuadro. variant elige entre tonal (el valor predeterminado) y filled. Todas las demás propiedades y eventos son iguales en ambas revisiones. Las plataformas marcadas arriba como rezagadas ofrecen la primera revisión hasta que se pongan al día: sin variant, sin arrastre y con un indicador solo detrás del icono que crece desde su centro, y sus ejemplos muestran esa revisión. Las demostraciones de esta página son el componente web tal como se distribuye hoy.

Cuatro destinos, uno de ellos con un recuento

La variante filled: pulsa y arrastra a lo largo de la barra para llevar el indicador

Una barra de enlaces: la página nombra el actual por su dirección, y «Ayuda» abre una pestaña nueva

Uso

Una barra de navegación tiene un solo tipo de elemento, destination: un icono, una etiqueta debajo y una insignia opcional sobre el icono. Cada plataforma recibe los destinos como recibe cualquier conjunto de hijos repetidos: elementos hijos <tanrei-navigation-bar-item> en la web, vistas TanreiNavigationBarItem en el view builder de SwiftUI, llamadas a TanreiNavigationBarItem en la lambda de contenido de Compose, objetos TanreiNavigationDestination en la lista destinations: de Flutter y un array destinations de objetos planos en React Native.

La selección pertenece a la barra, por el valor de un destino. value nombra el destino actual, change lleva el que eligió la persona y reselect se emite cuando elige de nuevo el actual, para que puedas desplazar esa pantalla de vuelta al principio. Un valor vacío no selecciona ninguno, para una pantalla que no es uno de los destinos de la barra. SwiftUI escribe el nuevo valor en el binding selection. Dos plataformas conservan su estilo propio, como hacen sus propias barras de navegación. Compose no tiene un valor en la barra: marcas el elemento actual con selected, y su onClick sirve a la vez de change y de reselect. Flutter selecciona por índice: selectedIndex y onDestinationSelected, al que también llama para el destino actual.

variant define con cuánta fuerza se marca el destino actual: tonal (el valor predeterminado) rellena su indicador con suavidad, y filled lo rellena con fuerza, con el icono y la etiqueta invertidos sobre él. Hoy está en la web.

Un destino con un href es un enlace: el enlace propio de la plataforma, que se dibuja exactamente igual que un destino de botón. Enter lo activa y Espacio no. La barra no selecciona un enlace con una pulsación: emite change con el valor del destino y su href, y después se sigue el enlace. La página que llega nombra el destino actual mediante value, como en el panel de navegación, y un enlace sin value toma su href como valor. target y targetLabel son los del botón. La web incluye los destinos de enlace; SwiftUI, Compose, Flutter y React Native todavía no los tienen.

El icon de un destino es el nombre de un glifo de la fuente de iconos del sistema de diseño. La barra lo dibuja con contorno mientras no está seleccionado y relleno mientras lo está. badge, badgeCount y badgeLabel dibujan una insignia de Tanrei sobre el icono: un punto cuando no hay recuento y, si lo hay, el número. SwiftUI, Compose y Flutter los reciben como un único valor TanreiNavigationBadge, y React Native, como un objeto badge.

La barra es una región del diseño, no una superposición. Su color llega hasta debajo del indicador de inicio o de la barra de gestos que tiene debajo, y sus destinos se sitúan por encima de ellos y dentro de los márgenes interiores laterales. En React Native, los márgenes interiores proceden solo de las propiedades, sin ninguna dependencia de área segura: los insets de TanreiProvider, o los insets de la propia barra, cuyos lados sustituyen a los del proveedor.

<!-- El host es una región de referencia (landmark) de navegación que recibe su nombre de label. -->
<!-- variant="filled" invierte el destino actual, para una barra sobre una pantalla recargada. Tonal es el valor predeterminado. -->
<tanrei-navigation-bar label="Principal" value="inbox">
  <tanrei-navigation-bar-item value="inbox" label="Recibidos" icon="inbox"
                              badge badge-count="3" badge-label="3 sin leer"></tanrei-navigation-bar-item>
  <tanrei-navigation-bar-item value="chat" label="Chat" icon="chat"
                              badge badge-label="Mensajes nuevos"></tanrei-navigation-bar-item>
  <tanrei-navigation-bar-item value="starred" label="Destacados" icon="star"></tanrei-navigation-bar-item>
  <tanrei-navigation-bar-item value="settings" label="Configuración" icon="settings" disabled></tanrei-navigation-bar-item>
</tanrei-navigation-bar>

<script type="module">
  import '@tanrei/web/components/navigation-bar';
  const bar = document.querySelector('tanrei-navigation-bar');
  // La barra define su propio value antes de emitir change, que lo lleva consigo.
  bar.addEventListener('change', (e) => showScreen(e.detail.value));
  bar.addEventListener('reselect', () => scrollTo({ top: 0 }));
</script>

<!-- Una barra de enlaces: cada página define value como su propia dirección. Un enlace sin value toma su href. -->
<tanrei-navigation-bar label="Principal" value="/search/">
  <tanrei-navigation-bar-item href="/" label="Inicio" icon="home"></tanrei-navigation-bar-item>
  <tanrei-navigation-bar-item href="/search/" label="Buscar" icon="search"></tanrei-navigation-bar-item>
  <tanrei-navigation-bar-item href="/library/" label="Biblioteca" icon="video_library"></tanrei-navigation-bar-item>
  <tanrei-navigation-bar-item href="https://example.com/help" label="Ayuda" icon="help"
                              target="new" target-label="se abre en una pestaña nueva"></tanrei-navigation-bar-item>
</tanrei-navigation-bar>

<script type="module">
  // Un enrutador se hace cargo de la navegación: cancela change, ve a detail.href y di dónde está ahora el producto.
  bar.addEventListener('change', (e) => {
    if (!e.detail.href) return;
    e.preventDefault();
    router.push(e.detail.href);
    bar.value = e.detail.value;
  });
</script>
import { NavigationBar, NavigationBarItem } from '@tanrei/react';

// variant="filled" para una barra sobre una pantalla recargada; tonal es el valor predeterminado.
<NavigationBar label="Principal" value={screen} onChange={(e) => setScreen(e.detail.value)} onReselect={scrollToTop}>
  <NavigationBarItem value="inbox" label="Recibidos" icon="inbox" badge badgeCount={3} badgeLabel="3 sin leer" />
  <NavigationBarItem value="chat" label="Chat" icon="chat" />
  <NavigationBarItem value="starred" label="Destacados" icon="star" />
</NavigationBar>
import Tanrei

// Una barra propia en el margen inferior del área segura. Al elegir un destino se escribe el binding (change).
ScreenView(screen)
    .safeAreaInset(edge: .bottom, spacing: 0) {
        TanreiNavigationBar(selection: $screen, accessibilityLabel: "Principal", onReselect: { _ in scrollToTop() }) {
            TanreiNavigationBarItem("Recibidos", icon: "inbox", value: "inbox",
                                    badge: TanreiNavigationBadge(count: 3, label: "3 sin leer"))
            TanreiNavigationBarItem("Chat", icon: "chat", value: "chat")
            TanreiNavigationBarItem("Destacados", icon: "star", value: "starred")
            // disabled es el modificador propio de SwiftUI.
            TanreiNavigationBarItem("Configuración", icon: "settings", value: "settings").disabled(true)
        }
    }
import io.github.chnotchy.tanrei.components.TanreiNavigationBadge
import io.github.chnotchy.tanrei.components.TanreiNavigationBar
import io.github.chnotchy.tanrei.components.TanreiNavigationBarItem

// Como el NavigationBar de Material: no hay un valor en la barra. Marcas el elemento actual con selected,
// y un solo onClick es a la vez change y reselect.
var screen by rememberSaveable { mutableStateOf("inbox") }
TanreiNavigationBar(contentDescription = "Principal") {
    TanreiNavigationBarItem(
        selected = screen == "inbox",
        onClick = { if (screen == "inbox") { scrollToTop() } else { screen = "inbox" } },
        icon = "inbox",
        label = "Recibidos",
        badge = TanreiNavigationBadge(count = 3, contentDescription = "3 sin leer"),
    )
    TanreiNavigationBarItem(selected = screen == "chat", onClick = { screen = "chat" }, icon = "chat", label = "Chat")
    // enabled es el inverso del disabled de la especificación.
    TanreiNavigationBarItem(selected = screen == "settings", onClick = { screen = "settings" }, icon = "settings", label = "Configuración", enabled = false)
}
import 'package:tanrei/tanrei.dart';

// Como NavigationBar: el valor de un destino es su índice. La barra no guarda ninguna selección propia.
TanreiNavigationBar(
  selectedIndex: current, // null no selecciona ninguno
  onDestinationSelected: (int index) {
    if (index == current) {
      scrollToTop(); // reselect
    } else {
      setState(() => current = index); // change
    }
  },
  semanticLabel: 'Principal',
  destinations: const <TanreiNavigationDestination>[
    TanreiNavigationDestination(icon: 'inbox', label: 'Recibidos', badge: TanreiNavigationBadge(count: 3, semanticLabel: '3 sin leer')),
    TanreiNavigationDestination(icon: 'chat', label: 'Chat'),
    TanreiNavigationDestination(icon: 'settings', label: 'Configuración', enabled: false),
  ],
),
import { useState } from 'react';
import { NavigationBar } from '@tanrei/react-native';

const [screen, setScreen] = useState('inbox');

// Controlada: cambia value desde onValueChange.
<NavigationBar
  accessibilityLabel="Principal"
  value={screen}
  onValueChange={setScreen}
  onReselect={scrollToTop}
  destinations={[
    { value: 'inbox', label: 'Recibidos', icon: 'inbox', badge: { count: 3, label: '3 sin leer' } },
    { value: 'chat', label: 'Chat', icon: 'chat', badge: { label: 'Mensajes nuevos' } },
    { value: 'settings', label: 'Configuración', icon: 'settings', disabled: true },
  ]}
/>

Especificaciones

Solo la etiqueta seleccionada, y sin etiquetas

Anatomía

Parte Descripción
1 container The bar along the bottom edge, and the ground the active indicator moves on: one constant color, whichever destination is selected. Its color reaches under the system inset below it (home indicator, gesture bar).
2 outline The bar's top edge, which sets it apart from the content above.
3 item One destination (an item of the destination kind): an equal share of the bar's width, less the bar's inset at its two ends, and its full height. The whole cell is the hit area.
4 item.container The destination's box: its cell, held in from the bar's top and bottom edges by the bar's inset. It is as wide as the destination's share of the bar, not as wide as its icon and label, so every destination of a bar has the same box. It carries hover, focus and press for every destination, which are therefore drawn where the active indicator is or would be, and it is filled as the active indicator while the destination is selected. When the selection changes the filled box slides from the previous destination to the new one, and it follows a finger dragged across the bar.
5 item.icon-indicator The box behind the icon alone, the icon centered in it. The default system leaves it transparent; a system that marks the selection behind the glyph only (Material's navigation bar) fills it and leaves the item container clear. It grows in from its center rather than sliding.
6 item.icon The destination's glyph (its icon). Outlined when unselected, filled when selected.
7 item.label · opcional The destination's name under the icon indicator, inside the item container, on one line. Always its accessible name, drawn or not.
8 item.covered-content A second, decorative copy of the icon and label in the covered colors, clipped to the active indicator wherever it is drawn, so that the content inverts where the indicator covers it and the inversion travels with the indicator, in a slide and under a finger alike. Hidden from assistive technology.
9 item.badge · opcional A Tanrei badge anchored to the icon, drawn from the destination's badge, badgeCount and badgeLabel: a dot or a count (the badge component). It is drawn above both copies of the icon.
10 focus-ring Gap-then-ring indicator around the item container, concentric with it and in its shape, held inside the bar, shown for keyboard focus.

Propiedades

Propiedad Tipo Valor predeterminado Descripción
value string "" The value of the selected destination (selection is the bar's, by a destination's value). Empty selects none (the current screen is not one of the bar's destinations).
variant tonal | filled "tonal" How the active indicator is filled. Tonal: a soft container with the content in that container's own color. Filled: the strong color, with the content inverted on it, for a bar that must stand out from a busy screen. Tonal destinations are drawn from the unselected and selected conditions, filled ones from filled-unselected and filled-selected. The indicator's size and place are the same in both.
labelVisibility always | selected | never "always" Which destinations draw their label: all of them, only the selected one, or none. A label that is not drawn is still the destination's accessible name, and no item container changes size with it.
label string "" Names the bar itself for assistive technology ("Main"); on the web it names the navigation landmark.

Eventos

Evento Descripción
change The user activated a destination that is not the selected one, or released a drag of the active indicator on one; carries its value and, for a link, its href. For a link it comes before the link is followed, only for an activation that follows it in place (NVB-31), and a product that cancels it follows the link itself.
reselect The user activated the destination that is already selected (a product may scroll that screen to its top); carries its value and, for a link, its href, as change does. A drag that returns the indicator to it does not emit it.

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.

destination

One top-level destination of the bar: an icon, a label under the icon, and optionally a badge on the icon.

Propiedad Tipo Valor predeterminado Descripción
value string "" What identifies the destination. The bar's value selects the destination whose value equals it; change and reselect carry it. Unique within the bar. Empty on a link, its href stands as its value.
href string "" The address the destination leads to. Not empty, the destination is a link and not a button (NVB-30): it is followed as the platform follows a link, and the bar does not select it by itself (NVB-31). Empty, the destination is a button and the product moves on change. As the Button's href (a control with a destination is a link).
target same | new "same" Where a link 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 a bar's cell has no place to mark and says through targetLabel (NVB-32). A link that opens elsewhere is never selected by a press (NVB-31). Ignored without href.
targetLabel string "" What assistive technology reads after the link'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.
label string "" Its name, drawn under the icon (see the bar's labelVisibility) and always its accessible name. Required.
icon string "" The name of its glyph in the system's icon set, drawn in the destination's own icon style: outlined while unselected, filled while selected. Required.
disabled boolean false Makes it unavailable. It keeps its place, draws its disabled look, cannot be activated and leaves the focus order.
badge boolean false Draws a Tanrei badge (the badge component) on the icon, from badgeCount and badgeLabel.
badgeCount number null The badge's count, as the badge component's count: null draws the dot, 0 draws nothing, above 0 the number (above 999 as 999+). Ignored while badge is false.
badgeLabel string "" The badge's label, as the badge component's: what assistive technology reads after the destination's name and state ("3 unread"). A dot needs one. Ignored while badge is false.
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento tanrei-navigation-bar-item TanreiNavigationBarItem TanreiNavigationBarItem TanreiNavigationDestination NavigationBarDestination
value · Propiedad value value selected destinations value
href · Propiedad href destination href href href
target · Propiedad target target target target target
targetLabel · Propiedad targetLabel targetLabel targetLabel targetSemanticLabel targetLabel
label · Propiedad label title label label label
icon · Propiedad icon icon icon icon icon
disabled · Propiedad disabled disabled enabled enabled disabled
badge · Propiedad badge badge badge badge badge
badgeCount · Propiedad badgeCount count count count count
badgeLabel · Propiedad badgeLabel label contentDescription semanticLabel label
Notas icon is the glyph's name, as <tanrei-tab>'s. badgeCount and badgeLabel are the badge-count and badge-label attributes; the element draws a <tanrei-badge> around the glyph from them. href is the spec's own prop: the cell is an <a href> in the item's shadow root. 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, all as <tanrei-button>'s. Space is not handled on it. With no value attribute the item's value property reads its href attribute as written (not the resolved URL). TanreiNavigationBarItem(_ title: String, icon: String, value: Value, badge: TanreiNavigationBadge? = nil), where icon is the glyph's name and TanreiNavigationBadge(count: Int? = nil, label: String = "") is drawn with .tanreiBadge(…) on the glyph (no badge is nil). disabled follows .disabled(_:) on the item. destination: URL? = nil makes it a link (the link trait beside the item's selected trait), opened by the environment's openURL after the selection callback, which an app that routes addresses itself answers with its own OpenURLAction; the binding is not written for it. target: TanreiLinkTarget = .same and targetLabel (the accessibility hint), as the Button's. TanreiNavigationBarItem(selected, onClick, icon = "inbox", label = "…", enabled = true, badge = null) in the bar's RowScope content lambda. enabled is the inverse flag. badge: TanreiNavigationBadge? with TanreiNavigationBadge(count: Int? = null, contentDescription: String = ""); the item draws a TanreiBadge around its icon from it. icon is the glyph's name; the item draws it in the selection's icon style. href: String? = null makes it a link: opened through LocalUriHandler.current after onClick (an app that routes addresses itself provides its own UriHandler); the item keeps Role.Tab, which Compose cannot join with a link role. target: TanreiLinkTarget = TanreiLinkTarget.Same, and targetLabel is appended to the state description, as the Button's. TanreiNavigationDestination(icon:, label:, enabled: true, badge: null) in the bar's destinations: list; its value is its index there. icon is the glyph's name. badge: TanreiNavigationBadge(count:, semanticLabel:), drawn as a TanreiBadge around the icon. enabled: false disables it. href: Uri? = null makes it a link (Semantics(link: true) with the address as its linkUrl, beside the tab semantics), opened by the nearest TanreiLinkOpener after onDestinationSelected; target is a TanreiLinkTarget and targetSemanticLabel the semantics hint, as the Button's. An entry of destinations: NavigationBarDestination[]: { value, label, icon, disabled?, badge? }, where icon is the glyph's name and badge?: { count?: number | null; label?: string } (absent is no badge) is drawn as a Badge around the icon. href?: string makes it a link: the destination keeps the tab role (React Native gives an element one role), onValueChange receives the value and the href, and Linking.openURL(href) follows it unless the product handles it; target is "same" | "new" and targetLabel the accessibilityHint, as the Button's.

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
NVB-1 WHEN the user activates a destination by pointer, touch, Enter or Space (Enter only for a destination that is a link, and for a link only an activation that NVB-31 counts) THE navigation bar SHALL emit change exactly once with the destination's value WHILE it is not the selected destination, and reselect exactly once with its value WHILE it is.
NVB-2 WHILE a destination's disabled is true THE navigation bar SHALL NOT emit change or reselect for it, SHALL NOT make it focusable and SHALL expose it as disabled to assistive technology.
NVB-3 THE navigation bar SHALL draw the destination whose value equals the bar's value from its variant's selected condition and every other destination from its variant's unselected condition: WHILE variant is tonal the selected condition is selected (comp.navigation-bar.selected.item.container.color, comp.navigation-bar.selected.item.icon-indicator.color, comp.navigation-bar.selected.item.icon.color, comp.navigation-bar.selected.item.label.color, comp.navigation-bar.selected.item.covered-icon.color, comp.navigation-bar.selected.item.covered-label.color) and the unselected condition is unselected; WHILE variant is filled they are filled-selected (comp.navigation-bar.filled-selected.item.container.color and the same parts) and filled-unselected. IF no destination's value equals a value that is not empty THE navigation bar SHALL draw every destination from its variant's unselected condition, and in debug builds report the value.
NVB-4 WHILE a destination is hovered, keyboard-focused or pressed THE navigation bar SHALL render its item container, icon indicator, icon, label, covered icon and label, 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.
NVB-5 WHEN a destination receives keyboard focus THE navigation bar SHALL show the focus indicator (the ring or the glow, NVB-10) around its item container (NVB-12), concentric with it and in its shape, with its inner edge sys.focus.offset outside the container's edge, and SHALL hold it inside the bar: WHERE the space between the item container and the bar's edges (comp.navigation-bar.container.padding) is less than the indicator reaches, it SHALL be drawn further in by the difference, on every side alike, keeping the container's shape. THE indicator SHALL be drawn above the neighbouring destinations and the active indicator, without animation; focus gained by pointer or touch SHALL NOT show it.
NVB-6 THE navigation bar SHALL make each destination's hit area its whole cell, the bar's full height above the system inset and its share of the bar's width (NVB-11), the first and the last cell reaching the bar's start and end edges, and SHALL NOT let a cell's hit area fall below sys.target.touch in either direction; hover and press over any part of the cell SHALL answer on the item container. WHERE an equal share of the bar's width is narrower than sys.target.touch (more destinations than the width holds), the cells SHALL still share the bar's width and narrow below the target rather than make the bar wider than the space it is given.
NVB-7 WHILE a destination's disabled is true THE navigation bar SHALL render its item container, icon indicator, icon, label, covered icon and label, and opacity from the disabled component tokens of its condition.
NVB-8 WHEN a destination is pressed by pointer, touch, Enter or Space THE navigation bar SHALL show its pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner.
NVB-9 WHILE a destination is pressed THE navigation bar SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole item container at once; ripple grows the pressed state layer, clipped to the item container, from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole destination and draws no state layer; none draws no answer to the press. Under ripple and dim only the item container color stays with the state under the press; icon indicator, icon, label, covered icon and label, and opacity still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.navigation-bar.motion.feedback-duration with comp.navigation-bar.motion.state-easing, fades over comp.navigation-bar.motion.state-duration with comp.navigation-bar.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced.
NVB-10 THE navigation bar SHALL ease its item container and icon indicator 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.
NVB-11 THE navigation bar SHALL span the full width of its parent, at least comp.navigation-bar.container.height high above the system inset, filled with comp.navigation-bar.container.color, raised by comp.navigation-bar.container.elevation and edged at the top by comp.navigation-bar.outline.width of comp.navigation-bar.outline.color, and SHALL divide its width, less comp.navigation-bar.container.padding at its start and at its end, into equal cells, one per destination, in reading order from the start edge; right-to-left layouts SHALL mirror the order. THE bar's container SHALL be the same whichever destination is selected, whatever state a destination is in and while the active indicator moves: only the active indicator marks the selection.
NVB-12 THE navigation bar SHALL draw each destination's item container as wide as its cell and as tall as the bar above the system inset less comp.navigation-bar.container.padding above and below, the same size for every destination of the bar whatever each holds, with its four corners rounded by comp.navigation-bar.item.container.shape, each at most half its height, and SHALL NOT size it to the destination's content. Inside it THE navigation bar SHALL lay out the destination as a column centered in the item container: the icon indicator, comp.navigation-bar.item.icon-indicator.width wide and comp.navigation-bar.item.icon-indicator.height high with corners comp.navigation-bar.item.icon-indicator.shape, its icon centered in it; and WHILE the label is drawn, the label below it, comp.navigation-bar.item.gap away, centered, on one line in comp.navigation-bar.item.label.typescale. THE column SHALL keep comp.navigation-bar.item.container.padding-block between itself and the item container's top and bottom edges, the bar growing in height to keep it (NVB-21), and a label wider than the item container less comp.navigation-bar.item.container.padding-inline on each side SHALL be truncated at its end with an ellipsis. THE navigation bar SHALL NOT draw hover, focus or press on the part of the cell outside the item container. Every part is laid out in every system; a transparent color hides a part without changing the layout.
NVB-13 WHILE labelVisibility is always THE navigation bar SHALL draw every destination's label; WHILE it is selected it SHALL draw only the selected destination's label; WHILE it is never it SHALL draw none. A destination that draws no label SHALL center its icon indicator in its item container, whose size does not change with the label, and every destination's label SHALL remain its accessible name.
NVB-14 THE navigation bar SHALL draw each icon at comp.navigation-bar.item.icon.size with the family and fill of its condition (selected for the selected destination, unselected for every other), sys.icon.weight, and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max.
NVB-15 WHEN the selected destination changes while no drag is followed (NVB-27) THE navigation bar SHALL slide the active indicator (the fill of the selected condition's item container) from the previous destination's item container to the new one's, at its full size throughout and drawn in the new destination's colors, every frame of the slide being the in-between look of NVB-27 (the indicator as a drag would draw it at that place, each icon and label in its covered color exactly where it is covered); SHALL grow the new destination's icon indicator in width from its center to its full width, at its full height throughout, and fade the previous one out; and SHALL change icon and label colors and icon fill; all over comp.navigation-bar.motion.selection-duration with comp.navigation-bar.motion.selection-easing. WHERE no destination was selected before, the active indicator SHALL fade in on the new item container at its full size, and WHERE none is selected after, it SHALL fade out, over the same time; it SHALL NOT grow or shrink in width to appear or disappear. WHILE labelVisibility is selected the labels SHALL appear and disappear over the same time, without resizing or moving any item container. WHERE motion is reduced it SHALL show the new selection at once. WHEN the bar first appears, or the selected destination changes size or place without a new selection (a font arriving, the text size changing, the bar resizing), THE navigation bar SHALL put the active indicator on it at once, without a slide.
NVB-16 WHILE a destination's badge is true THE navigation bar SHALL draw a Tanrei badge anchored to its icon as the badge component draws one whose count is the destination's badgeCount and whose label is its badgeLabel, SHALL keep it while the destination is disabled, and SHALL expose the badge's description (badgeLabel, or the displayed count when it is empty) as part of the destination's own description, after its name and state.
NVB-17 IF the navigation bar holds fewer than three or more than five destinations THE navigation bar SHALL still render them, and in debug builds report the count.
NVB-18 THE navigation bar SHALL extend its container under the bottom system inset (home indicator, gesture bar, taskbar) and SHALL place its destinations above that inset and inside the left and right insets.
NVB-19 THE navigation bar SHALL expose each destination as one element named by its label, with its selected state and its position in the set (for example 2 of 4), and its icon hidden: a link WHILE its href is not empty (NVB-30) and a button otherwise. On the web it is an anchor or a button inside a navigation landmark named by the bar's label, the selected destination carrying aria-current="page"; on Apple platforms, Android, Flutter and React Native a tab of a tab bar, with the selected state, a link destination included: there the tab is what carries the position and the selection a bar is read by, and a link destination adds the link trait to it where the platform lets one element carry both. The selected destination is the place the product is showing, in a product of one screen and in a site of many pages alike: THE navigation bar SHALL have no second state and no second mark for a current page, and SHALL expose exactly one destination, or none, as current.
NVB-20 WHILE a destination's label is not empty THE navigation bar SHALL expose it as the destination's accessible name, over a name the product gives the destination on the element (aria-label or aria-labelledby); WHILE a destination's label is empty and the product gives the destination a name of its own on the element — aria-label, or aria-labelledby naming an element — THE navigation bar SHALL expose that name as its accessible name; WHILE a destination's label is empty and the product gives it none THE navigation bar SHALL still render it, and in debug builds report that it has no accessible name.
NVB-21 WHILE the text size is enlarged THE navigation bar SHALL grow its height to hold its labels on one line inside their item containers (NVB-12), every item container growing with the bar alike, and SHALL NOT scale its icon indicators or icons; WHERE the platform offers a large-content viewer for bar items (iOS) a long press SHALL show the destination's icon and label enlarged.
NVB-22 IF two destinations have the same value THE navigation bar SHALL treat the first of them as the one the bar's value selects, and in debug builds report the repeated value.
NVB-23 THE navigation bar SHALL draw each destination's icon and label a second time, over the first, in the covered colors of its condition (comp.navigation-bar.unselected.item.covered-icon.color and comp.navigation-bar.unselected.item.covered-label.color for a tonal destination that is not selected, comp.navigation-bar.selected.item.covered-icon.color and comp.navigation-bar.selected.item.covered-label.color for the tonal selected one, comp.navigation-bar.filled-unselected.item.covered-icon.color, comp.navigation-bar.filled-unselected.item.covered-label.color, comp.navigation-bar.filled-selected.item.covered-icon.color and comp.navigation-bar.filled-selected.item.covered-label.color for a filled bar), clipped to the active indicator's current shape, its corners included, wherever the indicator is drawn; WHILE the indicator slides (NVB-15) or follows a drag (NVB-27) the clip SHALL move with it in the same frame, so that a destination it passes over changes where it is covered and the newly selected destination's icon and label are wholly in their covered colors when it lands, neither before nor after. THE navigation bar SHALL NOT approximate the covered colors with a blend mode, SHALL draw the badge above both copies, and SHALL expose each destination to assistive technology once.
NVB-24 THE navigation bar 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 navigation bar is shown THE navigation bar SHALL draw itself for the new direction at once, without a motion of its own, keeping the selected destination and its focus; a drag (NVB-27) SHALL carry the active indicator to where the pointer is in either direction.
NVB-25 WHERE the layout direction is right-to-left THE navigation bar SHALL draw a destination's icon, in both of its copies (NVB-23) 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 NVB-11 gives. THE navigation bar 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.
NVB-26 THE navigation bar SHALL lay out a destination's label, in both of its copies (NVB-23) 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 navigation bar SHALL still place each label and align its lines by the layout direction, not by the text's own, and SHALL cut a label that does not fit (NVB-12) at the end of its own direction, so that a left-to-right label in a right-to-left layout loses its right end and never its beginning.
NVB-27 WHEN the user presses the bar with touch, a pen or a mouse and drags past the platform's touch slop (8 points where the platform names none), further sideways than up or down, WHILE a destination is selected, THE navigation bar SHALL take the drag: the press SHALL activate no destination when it ends (NVB-1), the destination under it SHALL leave its pressed state as it does when a press ends (NVB-8), and from then until the drag ends THE navigation bar SHALL draw the active indicator where the pointer is along the bar — on an enabled destination's item container while the pointer is at that cell's center, halfway between two neighbouring enabled destinations while the pointer is where their cells meet (the middle of any disabled destinations between them), at the linear interpolation in between, and on the first or last enabled destination while the pointer is before or beyond its center — at its full size, in the selected destination's colors. Everything that differs between the indicator on the one destination and on the other SHALL change continuously with the pointer, with no threshold: its place, and the color of each icon and label, which is its covered color exactly where the indicator covers it and its own color elsewhere, the clip of NVB-23 moving with the indicator in the same frame. The icon indicator, each icon's fill and, WHILE labelVisibility is selected, the label that is drawn SHALL stay with the selected destination until the release (NVB-28). THE navigation bar SHALL place the indicator in the frame the pointer moves, without a motion of its own; WHEN the drag is taken with the pointer away from where the indicator rests THE navigation bar SHALL bring it to the pointer's place over comp.navigation-bar.motion.state-duration with comp.navigation-bar.motion.state-easing instead of at once, following the pointer meanwhile. A drag that is further up or down than sideways when it passes the slop SHALL be left to the page and to the system's gestures at the bottom edge for its whole length, a wheel or a trackpad SHALL move nothing, and WHILE no destination is selected THE navigation bar SHALL take no drag.
NVB-28 WHEN the user releases a drag the navigation bar has taken THE navigation bar SHALL choose one enabled destination: the one the active indicator is nearer to of the two it lies between (the one after it from halfway on), or, WHEN the release is a fling along the bar, the one of those two that the fling is toward; a fling is a release faster than the platform's minimum fling velocity (Android's ViewConfiguration.getScaledMinimumFlingVelocity, Flutter's kMinFlingVelocity) or, where the platform names none (the web, SwiftUI, React Native), 0.5 points per millisecond. A release's velocity is the pointer's over its last moves: WHERE the pointer has not moved for longer than the time after which the platform's velocity tracker takes a pointer as stopped (40 milliseconds in Android's VelocityTracker, Jetpack Compose's VelocityTracker and Flutter's VelocityTracker) or, where the platform names none (the web, SwiftUI, React Native), 40 milliseconds before the release THE navigation bar SHALL take the release as having no velocity, so that a drag that stops where it is wanted and is let go there is no fling, however fast it moved before it stopped. WHERE the chosen destination is not the selected one and is not a link THE navigation bar SHALL activate it as NVB-1 does — emit change exactly once with its value — and SHALL slide the active indicator from where the release left it onto that destination over comp.navigation-bar.motion.selection-duration with comp.navigation-bar.motion.selection-easing, as NVB-15 does from a destination; WHERE the chosen destination is not the selected one and is a link THE navigation bar SHALL treat the release as a plain press on that link — emitting change and following the link as NVB-31 says, or only opening it where NVB-31 does not count it — SHALL NOT write value, and SHALL return the active indicator to the destination that value selects as NVB-29 returns it, the indicator going on to the chosen destination when the product sets value (NVB-15); WHERE it is the selected destination THE navigation bar SHALL return the indicator to it over the same motion and SHALL emit neither change nor reselect. The place where two cells meet decides only what a release chooses; nothing drawn changes at it. The release SHALL NOT also activate the destination under the pointer.
NVB-29 WHEN a drag the navigation bar has taken is cancelled — the platform cancels the pointer (a system gesture, a lost capture), Escape is pressed while it lasts, or the product sets value or removes or disables a destination the indicator lies between — THE navigation bar SHALL return the active indicator to the selected destination (the one the product has just set, where it set one) over comp.navigation-bar.motion.selection-duration with comp.navigation-bar.motion.selection-easing and emit nothing. WHILE a drag lasts THE navigation bar SHALL NOT change value, keyboard focus, or the selected state that each destination exposes to assistive technology, whatever the indicator covers, and SHALL announce nothing: the selection is made, and announced, by the release alone (NVB-28). WHERE motion is reduced THE navigation bar SHALL still draw the active indicator where the pointer is, which the person's own finger moves, taking the drag without the catch-up motion of NVB-27, and SHALL put it on its destination at once when the drag is released or cancelled.
NVB-30 WHILE a destination's href is not empty THE navigation bar SHALL make it a link to that address and not a button. On the web it SHALL be an anchor element with that address, whose behaviours are the browser's own — following it on a plain click, a touch or Enter; opening it in another tab or window by a modified click or a middle click; the link's context menu; copying its address; showing its address on hover — and THE navigation bar SHALL NOT prevent, replace or imitate any of them, with one exception: a sideways drag that begins on a link destination moves the active indicator (NVB-27) and does not drag the link's address, because one gesture cannot do both and the drag is the bar's own way of moving its selection; Space SHALL NOT activate a link. On other platforms it SHALL be the platform's own link (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. THE navigation bar SHALL draw a link exactly as it draws a button destination, from the same tokens in every state and both variants (NVB-3, NVB-4, NVB-7 to NVB-9, NVB-12, NVB-23), SHALL draw no look of its own for a link the user has visited, and SHALL give its hit area (NVB-6) to the link. WHILE a link's value is empty THE navigation bar SHALL take its href as its value, for value, change, reselect and NVB-22. WHILE a link's disabled is true it SHALL keep the link role with the disabled state and no destination, and SHALL open nothing by any means the platform gives a link (NVB-2; on the web the anchor carries no address).
NVB-31 WHEN the user activates a link destination THE navigation bar SHALL count it as an activation (NVB-1) only WHILE it follows the link in the place where the product is shown: a click with the primary button and no modifier key, a touch, Enter, or the default action of assistive technology, on a link whose target is same and that asks for no download. It SHALL then emit change or reselect exactly once, carrying the destination's value and its href, before the link is followed, and SHALL NOT hold the link back for sys.motion.duration.press-hold; SHALL NOT write value itself on any platform, because where the product is, is the product's to say, and the page or the route shown next sets value; SHALL keep the active indicator on the destination that value selects, moving it as NVB-15 says only when value changes; and SHALL let the link be followed unless the product cancels the event (preventDefault on the web), as a product does that moves between its pages without loading a document. For every other activation of a link — a modified or a middle click, a link whose target is new, a download — THE navigation bar SHALL emit neither change nor reselect, and SHALL keep value and the drawn selection as they are.
NVB-32 WHILE a destination is a link and its target is new THE navigation bar SHALL open its href in a new context, as BTN-24 says for a link button (on the web a new tab or window that is given no handle to the page that opened it), and SHALL draw no mark for it: a destination's cell is an icon over a label and has no trailing glyph place for a mark to stand in, as an icon-only button has none, and a second glyph beside the icon would be read as its badge. It SHALL expose targetLabel as part of the destination's description, after its badge's description (NVB-16), 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 navigation bar SHALL ignore targetLabel.
NVB-33 WHEN a destination's target names a value the navigation bar does not define THE navigation bar SHALL treat it as same, and in debug builds report the unknown value. IF a destination'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 navigation bar SHALL make the destination a button without a destination, and in the last two cases report it in debug builds, as BTN-26 says for a button. WHILE a destination's href is empty THE navigation bar SHALL ignore its target and targetLabel.
NVB-34 WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on a destination's host, by the attribute or by the ARIA property that reflects it, THE navigation bar 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 the destination's role (its link or its button) — 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 destination is invalid — except those a destination owns (aria-current, 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 navigation bar SHALL take it off that element. WHEN the product sets an aria- attribute a destination owns THE navigation bar SHALL report it in debug builds. The bar's host carries its navigation landmark itself (NVB-19), so what the product writes on it is not moved, but for a name: WHILE the bar's label is not empty THE navigation bar SHALL hold the product's aria-label and aria-labelledby on that host as their data-aria- twins, so that label is the landmark's whole name although the platform gives aria-labelledby the precedence, and WHEN label is emptied SHALL give them back as they were written, so that a name the product gives names the landmark; WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it to null, THE navigation bar SHALL take it away.

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-navigation-bar TanreiNavigationBar TanreiNavigationBar TanreiNavigationBar NavigationBar
value · Propiedad value selection selected selectedIndex value
variant · Propiedad variant variant variant variant variant
labelVisibility · Propiedad labelVisibility labelVisibility labelVisibility labelBehavior labelVisibility
label · Propiedad label accessibilityLabel contentDescription semanticLabel accessibilityLabel
change · Evento change selection onClick onDestinationSelected onValueChange
reselect · Evento reselect onReselect onClick onDestinationSelected onReselect
Notas Destinations are <tanrei-navigation-bar-item> children, in order. The bar selects the activated destination itself (sets value) before firing change, as a native control does, unless the destination is a link. For a link destination change and reselect are cancelable, carry detail.href beside detail.value, and are fired from the anchor's click only when event.button is 0, no modifier key is down, the click was not already cancelled, and the item's target is same and it has no download; cancelling one calls preventDefault() on that click. The bar does not set value for a link. variant is the variant attribute (tonal, filled) and labelVisibility the label-visibility attribute. The host is a navigation landmark (role="navigation", aria-label from label); the selected destination has aria-current="page". The active indicator is one layer of the bar that slides between the destinations' item containers; the covered copy of every icon and label (NVB-23) is an aria-hidden layer clipped with clip-path to the indicator's box, animated with it. A drag (NVB-27 to NVB-29) is followed with Pointer Events, as tanrei-tabs follows its own: pointerdown on the bar, setPointerCapture once the drag is taken, pointermove placing the indicator through the same custom properties a slide transitions, pointerup choosing the destination, and pointercancel, lostpointercapture or Escape cancelling; the click that follows a taken drag is swallowed, aria-current is written only on the release, a destination that is a link is not draggable as a link (draggable="false", the one behaviour of a link the bar takes, NVB-30) and is followed with its own click() when a release chooses it, the indicator then returning to the destination value selects (NVB-28), and touch-action: pan-y leaves vertical drags to the page. The bottom inset is env(safe-area-inset-bottom). It does not yet follow NVB-30 to NVB-33, nor the parts of NVB-1, NVB-19 and NVB-28 that concern links: a pressed link destination is selected like a button, change is not cancelable and carries no href, and the destination writes the link rules itself instead of using the shared link primitive (packages/web/src/primitives/link.ts). TanreiNavigationBar(selection: Binding<Value>, variant:, labelVisibility:, onReselect:) { TanreiNavigationBarItem(…) … }, placed with .safeAreaInset(edge: .bottom). A bar of its own rather than a restyled TabView, whose bar the tokens cannot reach (indicator, geometry, colors). Activation writes the binding (change); activating the selected item calls onReselect. The bar is exposed as a tab bar and each item as a button with the selected trait; each item supports the large-content viewer. It lags the spec: it has no variant, and draws the active indicator behind the icon alone (the item container at its least size), growing in from its center instead of sliding, with the label outside it. Not built on this platform yet: the item container as wide as the destination's cell and held in by container.padding, hover, focus and press drawn in that box, and the drag of the active indicator (NVB-27 to NVB-29): a DragGesture(minimumDistance:) beside each item's tap, predictedEndTranslation for the fling, the binding written in onEnded. Link destinations are not built yet either (NVB-30 to NVB-33 and the parts of NVB-1, NVB-19 and NVB-28 that concern links, with the fixtures links, links-rtl and links-disabled). Compose idiom (as NavigationBar): TanreiNavigationBar(variant, labelVisibility, contentDescription) { TanreiNavigationBarItem(…) … }. There is no bar-level value: the caller marks the selected item with selected = (current == value), so the bar's value and a destination's value both map to the item's selected, and one onClick per item covers change and reselect (the caller knows which item is selected). The bar draws the sliding active indicator behind its items, from the bounds of the selected item. Insets follow WindowInsets.navigationBars plus the horizontal display cutout, as a windowInsets parameter. Items have Role.Tab in a selectableGroup(). It lags the spec: it has no variant, and draws the active indicator behind the icon alone (the item container at its least size), growing in from its center instead of sliding, with the label outside it. Not built on this platform yet: the item container as wide as the destination's cell and held in by container.padding, hover, focus and press drawn in that box, and the drag of the active indicator (NVB-27 to NVB-29): pointerInput with detectHorizontalDragGestures on the bar and a VelocityTracker for the fling; the release calls the chosen item's onClick. Link destinations are not built yet either (NVB-30 to NVB-33 and the parts of NVB-1, NVB-19 and NVB-28 that concern links, with the fixtures links, links-rtl and links-disabled). Flutter idiom (as NavigationBar): TanreiNavigationBar(selectedIndex:, onDestinationSelected:, variant:, labelBehavior:, destinations: [TanreiNavigationDestination(…), …]). A destination's value is its index in destinations; a selectedIndex of null selects none. onDestinationSelected is called for the selected destination too (reselect); the caller compares. variant is TanreiNavigationBarVariant.tonal or .filled; labelBehavior is alwaysShow, onlyShowSelected or alwaysHide. Bottom inset from MediaQuery.padding via SafeArea. It lags the spec: it has no variant, and draws the active indicator behind the icon alone (the item container at its least size), growing in from its center instead of sliding, with the label outside it. Not built on this platform yet: the item container as wide as the destination's cell and held in by container.padding, hover, focus and press drawn in that box, and the drag of the active indicator (NVB-27 to NVB-29): a GestureDetector's horizontal drag callbacks on the bar, DragEndDetails.primaryVelocity for the fling; the release calls onDestinationSelected. Link destinations are not built yet either (NVB-30 to NVB-33 and the parts of NVB-1, NVB-19 and NVB-28 that concern links, with the fixtures links, links-rtl and links-disabled). Controlled, in React Native's value / onValueChange idiom, with a destinations data array; variant is 'tonal' or 'filled'. The bar has the tablist role and each destination the tab role with accessibilityState.selected; the covered copy is hidden with importantForAccessibility="no-hide-descendants" and accessibilityElementsHidden. Insets come from the bar's own insets prop, side by side, falling back to TanreiProvider's insets; the package takes no safe-area dependency, and a product that uses react-native-safe-area-context passes its useSafeAreaInsets() to either. It lags the spec: it has no variant, and draws the active indicator behind the icon alone (the item container at its least size), growing in from its center instead of sliding, with the label outside it. Not built on this platform yet: the item container as wide as the destination's cell and held in by container.padding, hover, focus and press drawn in that box, and the drag of the active indicator (NVB-27 to NVB-29): a PanResponder on the bar (or React Native Gesture Handler's Gesture.Pan() where the product has it), the release's vx for the fling; the release calls onValueChange. Link destinations are not built yet either (NVB-30 to NVB-33 and the parts of NVB-1, NVB-19 and NVB-28 that concern links, with the fixtures links, links-rtl and links-disabled).

Tokens

Token Alias de Valor resuelto (claro)
--tanrei-comp-navigation-bar-container-color sys.color.surface #ffffff
--tanrei-comp-navigation-bar-container-elevation sys.elevation.level0
--tanrei-comp-navigation-bar-container-height — 64px
--tanrei-comp-navigation-bar-container-padding sys.space.space50 4px
--tanrei-comp-navigation-bar-disabled-opacity — 1
--tanrei-comp-navigation-bar-filled-selected-disabled-item-container-color — #1b1b1b · 12%
--tanrei-comp-navigation-bar-filled-selected-disabled-item-covered-icon-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-selected-disabled-item-covered-label-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-selected-disabled-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-filled-selected-disabled-item-icon-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-selected-disabled-item-label-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-selected-disabled-item-state-layer-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-selected-focused-item-container-color — #323232
--tanrei-comp-navigation-bar-filled-selected-focused-item-covered-icon-color — #ffffff
--tanrei-comp-navigation-bar-filled-selected-focused-item-covered-label-color — #ffffff
--tanrei-comp-navigation-bar-filled-selected-focused-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-filled-selected-focused-item-icon-color — #262626
--tanrei-comp-navigation-bar-filled-selected-focused-item-label-color — #1b1b1b
--tanrei-comp-navigation-bar-filled-selected-focused-item-state-layer-color — #ffffff
--tanrei-comp-navigation-bar-filled-selected-hovered-item-container-color — #2d2d2d
--tanrei-comp-navigation-bar-filled-selected-hovered-item-covered-icon-color — #ffffff
--tanrei-comp-navigation-bar-filled-selected-hovered-item-covered-label-color — #ffffff
--tanrei-comp-navigation-bar-filled-selected-hovered-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-filled-selected-hovered-item-icon-color — #262626
--tanrei-comp-navigation-bar-filled-selected-hovered-item-label-color — #1b1b1b
--tanrei-comp-navigation-bar-filled-selected-hovered-item-state-layer-color — #ffffff
--tanrei-comp-navigation-bar-filled-selected-item-container-color sys.color.primary #1b1b1b
--tanrei-comp-navigation-bar-filled-selected-item-covered-icon-color sys.color.on-primary #ffffff
--tanrei-comp-navigation-bar-filled-selected-item-covered-label-color sys.color.on-primary #ffffff
--tanrei-comp-navigation-bar-filled-selected-item-icon-indicator-color comp.navigation-bar.selected.item.icon-indicator.color #000000 · 0%
--tanrei-comp-navigation-bar-filled-selected-item-icon-color comp.navigation-bar.selected.item.icon.color #262626
--tanrei-comp-navigation-bar-filled-selected-item-label-color comp.navigation-bar.selected.item.label.color #1b1b1b
--tanrei-comp-navigation-bar-filled-selected-item-state-layer-color comp.navigation-bar.filled-selected.item.covered-icon.color #ffffff
--tanrei-comp-navigation-bar-filled-selected-pressed-item-container-color — #323232
--tanrei-comp-navigation-bar-filled-selected-pressed-item-covered-icon-color — #ffffff
--tanrei-comp-navigation-bar-filled-selected-pressed-item-covered-label-color — #ffffff
--tanrei-comp-navigation-bar-filled-selected-pressed-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-filled-selected-pressed-item-icon-color — #262626
--tanrei-comp-navigation-bar-filled-selected-pressed-item-label-color — #1b1b1b
--tanrei-comp-navigation-bar-filled-selected-pressed-item-state-layer-color — #ffffff
--tanrei-comp-navigation-bar-filled-unselected-disabled-item-container-color — #000000 · 0%
--tanrei-comp-navigation-bar-filled-unselected-disabled-item-covered-icon-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-unselected-disabled-item-covered-label-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-unselected-disabled-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-filled-unselected-disabled-item-icon-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-unselected-disabled-item-label-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-unselected-disabled-item-state-layer-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-filled-unselected-focused-item-container-color — #4b4b4b · 10%
--tanrei-comp-navigation-bar-filled-unselected-focused-item-covered-icon-color — #ffffff
--tanrei-comp-navigation-bar-filled-unselected-focused-item-covered-label-color — #ffffff
--tanrei-comp-navigation-bar-filled-unselected-focused-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-filled-unselected-focused-item-icon-color — #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-focused-item-label-color — #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-focused-item-state-layer-color — #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-hovered-item-container-color — #4b4b4b · 8%
--tanrei-comp-navigation-bar-filled-unselected-hovered-item-covered-icon-color — #ffffff
--tanrei-comp-navigation-bar-filled-unselected-hovered-item-covered-label-color — #ffffff
--tanrei-comp-navigation-bar-filled-unselected-hovered-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-filled-unselected-hovered-item-icon-color — #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-hovered-item-label-color — #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-hovered-item-state-layer-color — #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-item-container-color comp.navigation-bar.unselected.item.container.color #000000 · 0%
--tanrei-comp-navigation-bar-filled-unselected-item-covered-icon-color sys.color.on-primary #ffffff
--tanrei-comp-navigation-bar-filled-unselected-item-covered-label-color sys.color.on-primary #ffffff
--tanrei-comp-navigation-bar-filled-unselected-item-icon-indicator-color comp.navigation-bar.unselected.item.icon-indicator.color #000000 · 0%
--tanrei-comp-navigation-bar-filled-unselected-item-icon-color comp.navigation-bar.unselected.item.icon.color #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-item-label-color comp.navigation-bar.unselected.item.label.color #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-item-state-layer-color comp.navigation-bar.filled-unselected.item.icon.color #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-pressed-item-container-color — #4b4b4b · 10%
--tanrei-comp-navigation-bar-filled-unselected-pressed-item-covered-icon-color — #ffffff
--tanrei-comp-navigation-bar-filled-unselected-pressed-item-covered-label-color — #ffffff
--tanrei-comp-navigation-bar-filled-unselected-pressed-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-filled-unselected-pressed-item-icon-color — #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-pressed-item-label-color — #4b4b4b
--tanrei-comp-navigation-bar-filled-unselected-pressed-item-state-layer-color — #4b4b4b
--tanrei-comp-navigation-bar-focused-opacity — 1
--tanrei-comp-navigation-bar-hovered-opacity — 1
--tanrei-comp-navigation-bar-item-container-padding-block sys.space.space50 4px
--tanrei-comp-navigation-bar-item-container-padding-inline sys.space.space150 12px
--tanrei-comp-navigation-bar-item-container-shape sys.shape.corner.full 9999px
--tanrei-comp-navigation-bar-item-gap sys.space.space50 4px
--tanrei-comp-navigation-bar-item-icon-indicator-height comp.navigation-bar.item.icon.size 24px
--tanrei-comp-navigation-bar-item-icon-indicator-shape sys.shape.corner.full 9999px
--tanrei-comp-navigation-bar-item-icon-indicator-width comp.navigation-bar.item.icon.size 24px
--tanrei-comp-navigation-bar-item-icon-size sys.icon.medium 24px
--tanrei-comp-navigation-bar-motion-feedback-duration sys.motion.role.feedback 450ms
--tanrei-comp-navigation-bar-motion-selection-duration sys.motion.role.selection.travel 250ms
--tanrei-comp-navigation-bar-motion-selection-easing sys.motion.easing.spatial cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-navigation-bar-motion-state-duration sys.motion.role.state.large 250ms
--tanrei-comp-navigation-bar-motion-state-easing sys.motion.easing.state cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-navigation-bar-outline-color sys.color.outline-variant #000000 · 8%
--tanrei-comp-navigation-bar-outline-width sys.border.hairline 1px
--tanrei-comp-navigation-bar-pressed-opacity — 1
--tanrei-comp-navigation-bar-selected-disabled-item-container-color — #eeeeee
--tanrei-comp-navigation-bar-selected-disabled-item-covered-icon-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-selected-disabled-item-covered-label-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-selected-disabled-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-selected-disabled-item-icon-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-selected-disabled-item-label-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-selected-disabled-item-state-layer-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-selected-focused-item-container-color — #dadada
--tanrei-comp-navigation-bar-selected-focused-item-covered-icon-color — #262626
--tanrei-comp-navigation-bar-selected-focused-item-covered-label-color — #262626
--tanrei-comp-navigation-bar-selected-focused-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-selected-focused-item-icon-color — #262626
--tanrei-comp-navigation-bar-selected-focused-item-label-color — #1b1b1b
--tanrei-comp-navigation-bar-selected-focused-item-state-layer-color — #262626
--tanrei-comp-navigation-bar-selected-hovered-item-container-color — #dedede
--tanrei-comp-navigation-bar-selected-hovered-item-covered-icon-color — #262626
--tanrei-comp-navigation-bar-selected-hovered-item-covered-label-color — #262626
--tanrei-comp-navigation-bar-selected-hovered-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-selected-hovered-item-icon-color — #262626
--tanrei-comp-navigation-bar-selected-hovered-item-label-color — #1b1b1b
--tanrei-comp-navigation-bar-selected-hovered-item-state-layer-color — #262626
--tanrei-comp-navigation-bar-selected-item-container-color sys.color.secondary-container #eeeeee
--tanrei-comp-navigation-bar-selected-item-covered-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-navigation-bar-selected-item-covered-label-color sys.color.on-secondary-container #262626
--tanrei-comp-navigation-bar-selected-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-selected-item-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-navigation-bar-selected-item-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-navigation-bar-selected-item-state-layer-color comp.navigation-bar.selected.item.icon.color #262626
--tanrei-comp-navigation-bar-selected-pressed-item-container-color — #dadada
--tanrei-comp-navigation-bar-selected-pressed-item-covered-icon-color — #262626
--tanrei-comp-navigation-bar-selected-pressed-item-covered-label-color — #262626
--tanrei-comp-navigation-bar-selected-pressed-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-selected-pressed-item-icon-color — #262626
--tanrei-comp-navigation-bar-selected-pressed-item-label-color — #1b1b1b
--tanrei-comp-navigation-bar-selected-pressed-item-state-layer-color — #262626
--tanrei-comp-navigation-bar-unselected-disabled-item-container-color — #000000 · 0%
--tanrei-comp-navigation-bar-unselected-disabled-item-covered-icon-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-unselected-disabled-item-covered-label-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-unselected-disabled-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-unselected-disabled-item-icon-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-unselected-disabled-item-label-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-unselected-disabled-item-state-layer-color — #1b1b1b · 38%
--tanrei-comp-navigation-bar-unselected-focused-item-container-color — #4b4b4b · 10%
--tanrei-comp-navigation-bar-unselected-focused-item-covered-icon-color — #262626
--tanrei-comp-navigation-bar-unselected-focused-item-covered-label-color — #262626
--tanrei-comp-navigation-bar-unselected-focused-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-unselected-focused-item-icon-color — #4b4b4b
--tanrei-comp-navigation-bar-unselected-focused-item-label-color — #4b4b4b
--tanrei-comp-navigation-bar-unselected-focused-item-state-layer-color — #4b4b4b
--tanrei-comp-navigation-bar-unselected-hovered-item-container-color — #4b4b4b · 8%
--tanrei-comp-navigation-bar-unselected-hovered-item-covered-icon-color — #262626
--tanrei-comp-navigation-bar-unselected-hovered-item-covered-label-color — #262626
--tanrei-comp-navigation-bar-unselected-hovered-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-unselected-hovered-item-icon-color — #4b4b4b
--tanrei-comp-navigation-bar-unselected-hovered-item-label-color — #4b4b4b
--tanrei-comp-navigation-bar-unselected-hovered-item-state-layer-color — #4b4b4b
--tanrei-comp-navigation-bar-unselected-item-container-color — #000000 · 0%
--tanrei-comp-navigation-bar-unselected-item-covered-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-navigation-bar-unselected-item-covered-label-color sys.color.on-secondary-container #262626
--tanrei-comp-navigation-bar-unselected-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-unselected-item-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-navigation-bar-unselected-item-label-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-navigation-bar-unselected-item-state-layer-color comp.navigation-bar.unselected.item.icon.color #4b4b4b
--tanrei-comp-navigation-bar-unselected-pressed-item-container-color — #4b4b4b · 10%
--tanrei-comp-navigation-bar-unselected-pressed-item-covered-icon-color — #262626
--tanrei-comp-navigation-bar-unselected-pressed-item-covered-label-color — #262626
--tanrei-comp-navigation-bar-unselected-pressed-item-icon-indicator-color — #000000 · 0%
--tanrei-comp-navigation-bar-unselected-pressed-item-icon-color — #4b4b4b
--tanrei-comp-navigation-bar-unselected-pressed-item-label-color — #4b4b4b
--tanrei-comp-navigation-bar-unselected-pressed-item-state-layer-color — #4b4b4b
--tanrei-comp-navigation-bar-item-label-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/navigation-bar.spec.json, tokens/comp/navigation-bar.tokens.json, design/tokens/

Pautas

Cuándo usarlo

Usa una barra de navegación en una pantalla compacta, como un teléfono en vertical, para moverse entre tres y cinco destinos de nivel superior: los lugares a los que una persona vuelve a lo largo de todo el día, como Recibidos, Buscar y Perfil. La barra permanece en todas las pantallas de nivel superior y muestra dónde está la persona.

Usa pestañas para cambiar entre vistas de un mismo tema dentro de una pantalla. Usa el botón de retroceso de una barra de la aplicación para salir de una pantalla a la que una persona llegó desde otra: las pantallas de detalle suelen ocultar la barra de navegación.

Cómo elegir entre la barra y el panel

Aspecto Barra de navegación Panel de navegación, contraído Panel de navegación, expandido
Dónde se sitúa A lo largo del borde inferior Una columna de iconos a lo largo del borde de inicio En un panel en el borde de inicio
Ventana Compacta Media y más anchas Expandida y más anchas (estándar), o compacta (modal)
Destinos De tres a cinco Sus destinos y grupos de nivel superior, una celda cada uno Tantos como tenga el producto, en secciones y grupos
Un destino Un icono sobre una etiqueta de una línea, con una insignia sobre el icono La fila cortada después de su icono: el icono solo, con una insignia encima y su nombre en un tooltip Una fila: un icono opcional, una etiqueta que salta de línea y, al final, texto o contenido final
También contiene Nada más Una cabecera: el botón de menú y la acción principal, contraída a su icono Una cabecera: el botón de menú, un título o un selector de cuentas, y la acción principal
En las plataformas nativas, un destino es Una pestaña de una barra de pestañas Un botón con un estado seleccionado Un botón con un estado seleccionado
Qué es Una región del diseño Una región del diseño, o una columna del diseño desde la que se ensancha un panel modal Estándar: una región del diseño. Modal: una superposición modal
  • El panel contraído es el carril de navegación. Un panel que se puede contraer se estrecha hasta una columna de iconos allí donde el contenido necesita el ancho, y su botón de menú lo vuelve a ensanchar; todos los iconos se quedan donde están. El carril de navegación que Tanrei tenía aparte queda retirado e integrado en esta forma.
  • La barra y el panel son un mismo conjunto de destinos en dos lugares. Reciben los mismos destinos y la misma selección, así que un producto que se adapta a la ventana cambia la barra por el panel en un punto de ruptura sin tocar sus destinos. Muestra uno de los dos cada vez, nunca ambos.
  • En el panel cabe más. Úsalo expandido cuando el producto tenga más destinos de los que caben en una barra, o cuando se repartan en grupos, como las carpetas y etiquetas de una aplicación de correo. En una ventana grande, un panel estándar se sitúa junto al contenido. En una compacta, un panel modal se desliza sobre él desde un botón de menú.
  • Un panel modal que se puede contraer mantiene su columna de iconos en el diseño y se ensancha sobre el contenido, por encima de un velo, cuando su botón de menú lo abre.
  • En todas las plataformas, ambos seleccionan por el valor de un destino (Compose con un parámetro en cada elemento, Flutter por índice) y emiten change y reselect.

Etiquetas

labelVisibility Qué hace Úsalo para
always (predeterminado) Todos los destinos dibujan su etiqueta. La mayoría de las barras. Las etiquetas hacen que los destinos se reconozcan de un vistazo.
selected Solo el destino actual dibuja su etiqueta. Un icono sin etiqueta se sitúa en el centro de su destino. Ningún destino cambia de tamaño ni de lugar con la selección. Barras en las que el espacio es escaso y los iconos son muy conocidos.
never Ningún destino dibuja su etiqueta. Productos multimedia y sociales cuyos cuatro o cinco iconos conoce todo el mundo.

Una etiqueta que no se dibuja sigue siendo el nombre accesible del destino, así que todos los destinos necesitan una.

Selección e insignias

  • El indicador activo. Cada destino tiene un recuadro: su parte igual del ancho de la barra, retirada de los bordes de la barra, por arriba, por abajo y en los dos extremos, con un mismo margen interior (4 en el sistema predeterminado). Cuatro destinos en una barra de 360 de ancho y 64 de alto tienen cada uno un recuadro de 88 × 56, una cápsula en el sistema predeterminado. El recuadro es el mismo para todos los destinos de una barra, contenga lo que contenga, y nunca se ajusta al tamaño de su icono y su etiqueta. El recuadro del destino actual está relleno, lo que lo convierte en el indicador activo, y su icono también está relleno. El paso del cursor, el foco y la pulsación se dibujan en ese mismo recuadro para todos los destinos.
  • El deslizamiento. Cuando la selección cambia, el indicador se desliza del destino anterior al nuevo con su tamaño completo. Los iconos y las etiquetas se invierten solo donde el indicador los cubre, así que un destino por el que pasa cambia de colores bajo él por un momento, y el nuevo destino queda invertido por completo cuando el indicador llega. Desde una selección vacía, el indicador aparece con un fundido sobre el nuevo destino; hacia una selección vacía, desaparece con un fundido. Nunca crece ni se encoge. Cuando la barra aparece por primera vez, o el destino actual cambia de tamaño sin una nueva selección (llega una fuente, cambia el tamaño del texto), el indicador se coloca en su sitio de inmediato. Donde el movimiento está reducido, el cambio ocurre de inmediato.
  • El arrastre. Una pulsación sobre la barra que se mueve hacia un lado, con el dedo, un lápiz o un ratón, mientras hay un destino seleccionado, lleva el indicador con el puntero. No se elige nada hasta soltar: value, el foco, el relleno de cada icono y lo que se anuncia siguen en el destino actual. Al soltar se elige el más cercano de los dos destinos entre los que se encuentra el indicador, o aquel hacia el que apunta un lanzamiento, y se emite change una vez. Si se suelta de vuelta sobre el destino actual, no se emite nada, tampoco reselect. Escape cancela el arrastre. Un arrastre nunca es la única forma: un toque, un clic, Enter y Espacio eligen como antes.
  • Volver a seleccionar. Elegir de nuevo el destino actual emite reselect, no change. La respuesta habitual es desplazar esa pantalla hasta el principio o volver a su primera página.
  • Insignias. Una insignia con recuento muestra el número (999+ por encima de 999), y una sin recuento muestra un punto. badgeLabel es lo que dice un lector de pantalla después del nombre del destino, como «3 sin leer». Un punto no tiene número que leer, así que siempre necesita uno. Una insignia se mantiene mientras su destino está deshabilitado.

Enlaces

En un sitio de páginas separadas, da a cada destino un href y define el value de la barra como la dirección de la propia página. El destino actual y la página actual son entonces una sola cosa, con una sola marca.

  • Se sigue, no se selecciona. Una activación corriente de un enlace emite change, o reselect si es el actual, con el valor del destino y su href, y después se sigue el enlace. La barra no escribe value: el indicador activo se queda en el destino que value nombra y se mueve cuando value cambia.
  • Un enrutador. Cancela el evento para hacerte cargo tú de la navegación, ve a detail.href y define value. El indicador se desliza entonces hasta el nuevo destino.
  • Otras pestañas. Un clic o Enter con una tecla modificadora, un clic con el botón central, un enlace cuyo target es new y una descarga son cosa del navegador. La barra no emite nada y no selecciona nada.
  • Un contexto nuevo. target: new abre una pestaña o una ventana nueva. Un destino es un icono sobre una etiqueta, sin sitio para una marca, así que no se dibuja ninguna: targetLabel se lo dice a la tecnología de apoyo, después de la descripción de la insignia.
  • El arrastre. Un arrastre hacia un lado que empieza sobre un enlace mueve el indicador activo y no arrastra la dirección del enlace. Si se suelta sobre un enlace, es una pulsación corriente en ese enlace: el indicador vuelve al destino actual hasta que value cambia.
  • Deshabilitado. Un enlace deshabilitado no se puede seguir de ninguna manera y sale del orden del foco.

Tonal y filled

variant El destino actual Úsala para
tonal (predeterminada) Un indicador suave en el color del contenedor secundario, con su icono y su etiqueta en el color de contenido de ese contenedor. La mayoría de las barras.
filled Un indicador fuerte en el color primario, con su icono y su etiqueta invertidos sobre él. Una barra que debe destacar sobre una pantalla recargada: un mapa, una cuadrícula de fotos, un reproductor.

Los destinos que el indicador no cubre se ven igual en las dos. La variante cambia con cuánta fuerza se dibuja la selección, no lo que se anuncia: una barra tonal y una filled exponen los mismos nombres, roles y estados. Un destino actual deshabilitado conserva su indicador tonal con contenido deshabilitado sobre él; un indicador filled se atenúa como lo hace un botón filled deshabilitado, porque el contenido deshabilitado sobre el color primario sería difícil de leer.

Los preajustes m3-like y cupertino omiten filled, ya que la barra de navegación de Material y la barra de pestañas de Apple tienen un solo estilo. m3-like conserva el recuadro de Material solo detrás del icono, con la etiqueta debajo y sin recuadro alrededor del destino, y cupertino marca la pestaña actual únicamente con su tinte. Con ambos, un arrastre no muestra nada que se mueva, y al soltar se sigue eligiendo.

Recomendado

  • Limita las etiquetas a una palabra corta: «Recibidos», «Buscar», «Perfil». Una etiqueta que no cabe en su destino se corta al final con puntos suspensivos, y nada salta de línea.
  • Usa filled solo donde el indicador tonal se perdería contra la pantalla que hay detrás de la barra.
  • Mantén los mismos destinos y el mismo orden en todas las pantallas de nivel superior.
  • Usa el mismo estilo de icono para todos los destinos y deja que la barra dibuje la forma rellena para el actual.

Evita

  • No uses la barra para acciones como «Crear» o «Compartir». Usa un botón de acción principal o la barra de la aplicación.
  • No pongas en ella menos de tres destinos ni más de cinco. Las compilaciones de depuración notifican el número. Lleva el resto a un panel de navegación.
  • No ocultes un destino que a veces no está disponible. Deshabilítalo, para que la barra conserve su forma.
  • No muestres una insignia en todos los destinos a la vez.

Accesibilidad

Rol
navigation
Etiquetado
Each destination is named by its label, drawn or not; its icon is decorative, and so is the covered copy of its icon and label. A badge adds its description (badgeLabel, or the displayed count) after the name, and a link that opens a new context its targetLabel after that. The bar's label names the bar itself.
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 Moves focus to the next destination that is not disabled: each destination is a tab stop, as the links of a navigation region are.
Enter / Space Activates the focused destination (Enter only for a destination that is a link: it follows the link, and Space is left to the platform).
Enter with a modifier key, on a link (the web) What the browser does with a link: another tab or window, a download. The bar emits nothing and selects nothing (NVB-31).
Escape Cancels a pointer drag of the active indicator in progress: the indicator returns and nothing is selected. Otherwise it does nothing.

Notas

  • The semantics follow each platform's idiom for a bar of top-level destinations. On the web it is a navigation landmark whose destinations are links (or buttons in an app that does not change the URL) with aria-current="page" on the current one: that is how a web user recognises site navigation, and the tablist pattern would promise tab panels and arrow-key movement that a switch of whole screens does not have. On iOS, Android, Flutter and React Native the same bar is the platform's tab bar (UITabBar, BottomNavigationView and Material's NavigationBar all expose their items as tabs), so each destination is announced as a tab with its selected state and position.
  • Selected and the current page are one thing. The bar lists the product's top-level places and the selected one is the place being shown, which the web exposes as aria-current="page" on a button and on a link alike. A site of separate pages changes who says which it is — the page, through value, not a press — and nothing else: a link destination is followed, the bar never selects it by itself, and no destination is ever selected and another current.
  • Screen readers announce, for example, "Inbox, tab, 1 of 4, selected, 3 unread": name, role, position, state, then the badge's description.
  • Labels do not wrap: a bar's height is part of the screen's layout. A label longer than its item container allows is truncated, and stays whole as the destination's name. At 200% text size the bar and its item containers grow, and iOS shows the large-content viewer on a long press, as its own tab bar does.
  • The variant changes how strongly the selection is drawn, not what is announced: a tonal and a filled bar expose the same names, roles and states.
  • The drag is never the only way to choose (WCAG 2.5.7): a tap, a click, Enter and Space remain, and the keyboard is unchanged. While a drag lasts nothing a destination exposes changes and nothing is announced; the release announces once, as an activation does. With a screen reader running a one-finger drag is the reader's, and activation works as before.
  • 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/navigation-bar.spec.json, tokens/sys/measure.tokens.json

De qué se encarga el componente

  • En la web, la barra es una región de referencia (landmark) de navegación que recibe su nombre de label. Cada destino es un enlace (con href) o un botón, y el actual lleva aria-current="page", tanto en un enlace como en un botón: no hay un segundo estado para una página actual. No es una lista de pestañas: un cambio entre pantallas completas no tiene paneles de pestaña ni movimiento con las teclas de flecha.
  • En iOS, Android, Flutter y React Native, la barra es la barra de pestañas de la plataforma, como lo son sus propias barras inferiores. Cada destino es un solo elemento con el rol de pestaña (en SwiftUI, un botón con el rasgo de seleccionado), su estado seleccionado y su posición, que recibe su nombre de su etiqueta tanto si la etiqueta se dibuja como si no. Un lector de pantalla dice, por ejemplo, «Recibidos, pestaña, 1 de 4, seleccionado, 3 sin leer».
  • El icono queda oculto para la tecnología de apoyo. Una insignia añade su descripción (badgeLabel, o el número que muestra) después del nombre y del estado, y un enlace que abre un contexto nuevo añade su targetLabel después de ella.
  • Cada destino que no está deshabilitado es su propia parada de tabulación, como lo son los enlaces de una región de navegación. Enter y Espacio activan el destino que tiene el foco (solo Enter, en el caso de un enlace en la web). Los destinos deshabilitados no reciben el foco y se anuncian como deshabilitados.
  • El anillo de foco se dibuja alrededor del recuadro del destino y se mantiene dentro de la barra, solo para el foco del teclado.
  • Un arrastre del indicador no cambia nada de lo que se anuncia hasta que se suelta, y nunca mueve el foco.
  • La copia invertida de cada icono y de cada etiqueta que el indicador cubre es decorativa: la tecnología de apoyo encuentra cada destino una sola vez, y variant no cambia lo que se anuncia.
  • Toda la celda es el área de pulsación: la altura completa de la barra y una parte igual de su ancho, como mínimo el objetivo mínimo. Una barra demasiado estrecha para sus destinos sigue repartiendo su ancho a partes iguales: las celdas se estrechan por debajo del objetivo en lugar de hacer la barra más ancha que su espacio.
  • Un texto más grande hace crecer la barra, y con ella el recuadro de cada destino, para que las etiquetas sigan cabiendo en una línea. Los iconos no se escalan. Una etiqueta truncada se conserva entera como nombre del destino. Donde iOS ofrece el visor de contenido grande, una pulsación larga muestra ampliados el icono y la etiqueta del destino.
  • Las compilaciones de depuración notifican menos de tres o más de cinco destinos, un destino sin etiqueta, un valor que no nombra ningún destino y un valor usado dos veces. También notifican un enlace que abre un contexto nuevo sin targetLabel, un href que nombra un script o que no se puede leer, y un target que la barra no define.

Carencias conocidas por plataforma:

  • SwiftUI, Compose, Flutter y React Native no tienen destinos de enlace: ahí un destino todavía no admite href.
  • Web: todos los tamaños siguen rem, así que un tamaño de texto mayor en el navegador también hace crecer los contenedores de los elementos y los iconos. Ampliar solo la tipografía de Tanrei no lo hace.
  • SwiftUI: el mínimo de las celdas es el objetivo propio de la plataforma, 44 puntos en macOS, donde la especificación indica el objetivo táctil de 48 puntos.
  • SwiftUI: fuera de iOS, un destino lleva su posición como texto de valor («2 of 4»), solo en inglés. Un icono que es una imagen, como un SF Symbol, no sigue la selección: pasa tú el símbolo relleno para el destino seleccionado. El visor de contenido grande está integrado, pero ninguna prueba lo ejercita todavía.
  • Compose no tiene un valor en la barra, así que, ante un valor que no nombra ningún destino, no hay nada que notificar: no marques ningún elemento como seleccionado para no seleccionar ninguno.
  • La semántica de Flutter no tiene posición en el conjunto, así que «1 de 4» no se anuncia.
  • SwiftUI, Flutter y React Native cambian la familia y el relleno del icono de inmediato en lugar de hacerlo a lo largo del movimiento de selección.
  • React Native no tiene visor de contenido grande. Un destino lleva su posición como texto de valor («1 of 4»), en inglés salvo que pases positionText.
  • React Native: el movimiento de selección, los márgenes interiores y lo que anuncian TalkBack y VoiceOver aún no se han comprobado en un dispositivo.
  • Todas las plataformas, sistema predeterminado: el indicador tonal tiene un contraste de aproximadamente 1.15:1 frente a la barra, y la auditoría de accesibilidad de iOS lo señala. El icono relleno y el color de la etiqueta también marcan la selección, y filled da un indicador de al menos 3:1 donde eso importa.

Qué debes hacer tú

  • Da una etiqueta a todos los destinos, también en una barra que no dibuja ninguna.
  • Da nombre a la barra con label, como «Principal», sobre todo cuando la pantalla tiene otra región de navegación.
  • Da a cada insignia un badgeLabel que diga qué cuenta, y siempre uno a un punto.
  • Mantén el valor de cada destino único y estable.
  • En un sitio de páginas separadas, define value en cada página como el destino de esa página. La barra no selecciona un enlace por sí sola.
  • Da a un 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.
  • En Compose, marca exactamente un elemento como selected, o ninguno. En Flutter, compara el índice que recibes con selectedIndex para distinguir un reselect de un change.
  • En React Native, pasa positionText en el idioma de tu producto y da a TanreiProvider los márgenes interiores de la ventana.