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
InicioComponentesPopover

Popover

Muestra contenido que una persona puede leer y manejar en una superficie pequeña que se abre junto al control que pulsó y se queda con él, sin oscurecer ni bloquear el resto de la página; en una ventana compacta, en una superficie modal con el aspecto de la hoja inferior.

content_copyVer en Markdown
Categoría
Contención
Tokens
comp.popover
Nivel
Base

Descripción general

Web
tanrei-popover
schedule
SwiftUI
tanreiPopover
radio_button_unchecked
Jetpack Compose
TanreiPopover
radio_button_unchecked
Flutter
TanreiPopover
radio_button_unchecked
React Native
Popover
radio_button_unchecked

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

Pulsa un botón: una nota con un enlace y un formulario breve con acciones

Almacenamiento

Los archivos de la papelera siguen ocupando espacio de almacenamiento hasta que se eliminan. Cómo se calcula el almacenamiento

Cambiar nombreCancelarCambiar nombre

Usa un popover para el contenido con el que una persona trabaja junto al control que lo abrió: unos pocos ajustes, un formulario breve, un panel para compartir, una nota que necesita un enlace o un botón. El resto de la página se puede seguir usando a su alrededor. En una ventana compacta toma en su lugar el aspecto de la hoja inferior, en el borde inferior de la ventana, como lo hacen ahí todos los popovers y diálogos de Tanrei.

Uso

El control es tuyo. En la web lo indicas con anchor, el id del elemento junto al que se sitúa el popover. Ese elemento es también el activador: el popover comunica a la tecnología de apoyo que ese elemento abre un diálogo y si el diálogo está abierto, y le devuelve el foco. Donde un campo sirve de ancla a la superficie y la abre un botón situado dentro del campo, indica el botón con opener.

Un popover es un componente controlado, como el menú. Defines open cuando se pulsa el activador y lo vuelves a poner en false cuando el popover emite close. Nunca se abre solo: ni al pasar el cursor, ni con el foco, ni al cabo de un tiempo. close lleva un motivo: escape, outside, focus-out, close (su botón de cierre) o anchor-hidden. Una acción nunca cierra el popover; lo cierra tu controlador. Como hoja en una ventana compacta, envía outside cuando se pulsa su velo, y nunca focus-out ni anchor-hidden.

Dale un nombre: headline donde dibuja un titular, label donde no lo dibuja.

SwiftUI, Compose, Flutter y React Native están planificados: los nombres que figuran junto a cada plataforma al principio de esta página son los que les da la especificación, y todavía no se distribuye nada para ellos.

<tanrei-button id="share" variant="outlined">Compartir</tanrei-button>

<!-- anchor es el id del control junto al que se sitúa. -->
<tanrei-popover id="share-panel" anchor="share" headline="Compartir esta página" closable close-label="Cerrar el panel para compartir">
  Cualquier persona que tenga el enlace puede leerla.
  <tanrei-button slot="actions" dismissive>Dejar de compartir</tanrei-button>
  <tanrei-button slot="actions" confirming>Copiar enlace</tanrei-button>
</tanrei-popover>

<!-- Una superficie bajo un campo: tan ancha como el campo, y el foco se queda en el campo. -->
<tanrei-text-field id="city" label="Ciudad"></tanrei-text-field>
<tanrei-popover anchor="city" label="Sugerencias" width="anchor" initial-focus="anchor" no-inset>…</tanrei-popover>

<script type="module">
  import '@tanrei/web/components/popover';

  const opener = document.querySelector('#share');
  const panel = document.querySelector('#share-panel');
  // El activador lo abre y lo cierra. Una pulsación en el activador nunca es "outside", así que una sola pulsación nunca lo cierra y lo vuelve a abrir.
  opener.addEventListener('click', () => (panel.open = !panel.open));
  // Escape, una pulsación fuera, el foco que sale, el botón de cierre, el ancla fuera de la vista.
  panel.addEventListener('close', (e) => {
    console.log(e.detail.reason);
    panel.open = false;
  });
  // Una acción no lo cierra: lo cierra el controlador, cuando el trabajo está hecho.
  panel.querySelector('[confirming]').addEventListener('click', async () => {
    await navigator.clipboard.writeText(location.href);
    panel.open = false;
  });
</script>
import { useState } from 'react';
import { Button, Popover } from '@tanrei/react';

const [open, setOpen] = useState(false);

<Button id="share" variant="outlined" onClick={() => setOpen(!open)}>Compartir</Button>
<Popover anchor="share" open={open} headline="Compartir esta página" closable closeLabel="Cerrar el panel para compartir" onClose={() => setOpen(false)}>
  Cualquier persona que tenga el enlace puede leerla.
  <Button slot="actions" dismissive onClick={stopSharing}>Dejar de compartir</Button>
  <Button slot="actions" confirming onClick={copyLink}>Copiar enlace</Button>
</Popover>

Especificaciones

La colocación, una flecha y un popover tan ancho como su ancla

Encima, centradoLa papelera sigue ocupando almacenamiento.Al lado, al finalLa papelera sigue ocupando almacenamiento.Tan ancho como este botónLa papelera sigue ocupando almacenamiento.

Dónde se sitúa

placement indica un lado del ancla y cómo se alinea el popover a lo largo de ese lado. Los trece valores son los del tooltip y se leen igual.

Valor Dónde
bottom-start (el predeterminado), bottom, bottom-end Debajo del ancla: con los bordes de inicio juntos, centrado o con los bordes de fin juntos
top-start, top, top-end Encima del ancla, de las mismas tres maneras
start, start-top, start-bottom Al lado del ancla, en su inicio: centrado, con los bordes superiores juntos o con los bordes inferiores juntos
end, end-top, end-bottom Al lado del ancla, en su fin, de las mismas tres maneras
auto El primero de bottom, top, end y start que tenga sitio

El inicio y el fin siguen la dirección del diseño. offset es la distancia al ancla en las unidades de diseño de la plataforma, y toma comp.popover.offset cuando no lo indicas. crossOffset mueve el popover a lo largo de su lado, y nunca lo saca del lado del ancla.

El popover se mantiene dentro de la ventana, a comp.popover.window-margin de sus bordes, con estos pasos, en este orden:

  1. Pasa al lado opuesto cuando el lado preferido no tiene sitio, y conserva la alineación.
  2. Donde ninguno de los dos lados tiene sitio, toma el que tiene más y se limita a ese sitio. Su contenido entonces se desplaza.
  3. Se desliza a lo largo de su lado tanto como pida el margen.
  4. En una ventana más pequeña que su tamaño mínimo, cubre el ancla antes que salirse de la ventana.

Sigue al ancla cuando el ancla se mueve, y conserva el lado en el que está mientras ese lado tenga sitio. Cuando ya no se ve ninguna parte del ancla, emite close con el motivo anchor-hidden.

Tamaño

Un popover es tan ancho como su contenido, entre comp.popover.container.min-width (160) y container.max-width (400). Con width="anchor" es tan ancho como el ancla, para una superficie que se despliega desde un campo. Nunca es más ancho que la ventana menos los márgenes.

Es tan alto como sus partes, hasta container.max-height (480) y el sitio que tiene su lado. A partir de ahí el contenido se desplaza entre la cabecera y las acciones, que se quedan en su lugar. Mientras el contenido se desplaza, se dibuja un divisor bajo la cabecera y otro sobre las acciones. Define dividerVisibility como scroll (divider-visibility en la web) para dibujar el de la cabecera solo cuando el contenido se ha desplazado, y el de las acciones solo mientras queda más contenido debajo; cupertino y m3-like lo eligen para todos los popovers. SwiftUI, Compose, Flutter y React Native lo dibujan así hasta que se pongan al día.

Pon inset en false (no-inset en la web) para un contenido que trae su propio margen interior o que llega hasta los bordes, como una lista.

Partes

Parte Qué es
Cabecera El titular, y el botón de cierre mientras closable es true. Se queda en su lugar mientras el contenido se desplaza
Contenido El slot predeterminado. Cualquier cosa, incluidos enlaces, campos y otros controles
Acciones Botones de Tanrei en el slot actions, cada uno marcado como confirming o dismissive donde lo sea. Se colocan como los coloca un grupo de botones, y se apilan cuando una etiqueta ya no cabe
Flecha Un triángulo opcional que apunta al ancla. arrow la activa para un solo popover; comp.popover.arrow.visible la activa para todo un sistema de diseño

Una acción sin variant toma comp.popover.actions.variant (tonal en el sistema predeterminado). El botón de cierre es un botón small de solo icono con la variante que indica comp.popover.close-button.variant: tonal en el sistema predeterminado, como todos los botones de cierre de Tanrei, para que se lea como un control sobre cualquier contenido. Los presets m3-like y cupertino conservan su glifo sin fondo (text).

Movimiento

El popover aparece como aparece toda superficie en Tanrei: con un fundido mientras crece un poco (desde el 96 % en el sistema predeterminado) desde el centro del borde que da al ancla, y se va por el mismo camino, algo más rápido. Un movimiento que se interrumpe da la vuelta desde donde está. Donde el movimiento está reducido, solo se funde. Con el preset m3-like, en cambio, se despliega desde ese borde, como los menús de Material. Un popover escrito con open en el marcado de la página está ahí desde el primer fotograma, sin movimiento.

En una ventana compacta

En una ventana más estrecha que comp.popover.compact.window-width (600 en el sistema predeterminado), un popover toma el aspecto de la hoja inferior. Es la regla de Tanrei para todos los popovers y todos los diálogos en una ventana compacta. Sigues abriendo el mismo popover con las mismas props y los mismos eventos; solo cambia cómo se dibuja.

  • Se sitúa en el borde inferior de la ventana, tan ancho como la ventana hasta comp.bottom-sheet.container.max-width, con el color y la elevación de la hoja inferior. Sus esquinas superiores toman comp.bottom-sheet.container.shape y las inferiores son rectas. Con el teclado en pantalla, se apoya sobre él.
  • Es tan alto como sus partes, y nunca más que la ventana menos comp.bottom-sheet.container.margin-block-start arriba. Más allá, el contenido se desplaza entre la cabecera y las acciones.
  • No tiene flecha ni tirador de arrastre, y no sigue un arrastre. placement, offset, crossOffset y width no se aplican.
  • Es modal: el velo de la hoja inferior atenúa la página, todo lo que queda detrás se vuelve inerte y el foco se queda dentro. Lo cierran Escape, la acción de retroceso, una pulsación en el velo y el botón de cierre.
  • Sube desde el borde inferior con el movimiento de la hoja inferior y vuelve a bajar al cerrarse. Donde el movimiento está reducido, aparece y desaparece con un fundido en su sitio.
  • Si el ancho de la ventana cruza ese valor mientras el popover está abierto, cambia de forma de inmediato, sin movimiento, y el foco se queda donde estaba.

Pon compactPresentation en own (compact-presentation="own" en la web) para que un popover se quede junto a su ancla en cualquier ventana, en una superficie que debe estar al lado de aquello a lo que pertenece. Un popover con initialFocus="anchor" se queda siempre junto a su ancla, porque el campo en el que la persona escribe debe seguir siendo utilizable. Un sistema de diseño que no quiera hojas nunca pone comp.popover.compact.window-width en 0.

Aquí los menús no son popovers: un menú, la lista de un select o de un combobox y el menú de un botón se quedan junto a su control en cualquier ventana.

Anatomía

Parte Descripción
1 anchor The element the popover stands beside: the product's own control (a Button, an icon-only Button, a chip, a field). The popover reads its place, never draws or restyles it. It is also the opener — the element a person presses to open the popover, which carries the open state and takes focus back — unless the product names another element as the opener (a field that anchors the surface, with a button inside it that opens it).
2 scrim · opcional In a compact window only, while the popover is presented as a sheet: the bottom sheet's scrim over everything behind it (POP-40). It blocks presses and asks the popover to close when pressed.
3 container The raised surface that holds everything else, placed on one side of the anchor and aligned along it; in a compact window, with the bottom sheet's appearance at the window's bottom edge (POP-39). It answers no pointer and no press itself and has no states: what is operated is inside it.
4 arrow · opcional A triangle on the container's edge that faces the anchor, pointing at it; one shape with the container.
5 header · opcional The row at the top that holds the headline and the close button. It stays in place while the content scrolls.
6 headline · opcional What the popover is about ("Filters", "Share this page"). It is the popover's accessible name where it is given.
7 close-button · opcional An icon-only Button of the small size showing the close glyph at the end of the header, in the variant comp.popover.close-button.variant names (tonal in the default system, as every host's close button is). The popover places it and chooses its variant; everything else it reads from Button's own tokens.
8 content Whatever the product puts in the popover: text, links, fields, a list, a few settings. The popover insets it, hands it a content color and scrolls it between the header and the actions; it does not lay it out.
9 divider · opcional Hairlines across the container below the header and above the actions, drawn only while the content scrolls: for as long as it does in the default system, or only while content lies beyond each line under dividerVisibility scroll.
10 actions · opcional Tanrei Buttons at the foot of the popover, each marked confirming or dismissive where it is one, laid out as a Button group. A Button whose variant the product did not set is tonal in the default system. Optional: a popover whose content acts at once (a setting, a link) needs none.

Propiedades

Propiedad Tipo Valor predeterminado Descripción
open boolean false Whether the popover is shown. The product opens it when its opener is pressed, and never on hover or on focus. Where open is a plain value (web, Compose, React Native) the product closes the popover by setting it to false when the popover emits close; where it is a binding or a controller (SwiftUI, Flutter) the popover closes itself through it (POP-21).
placement top | top-start | top-end | bottom | bottom-start | bottom-end | start | start-top | start-bottom | end | end-top | end-bottom | auto "bottom-start" Where the popover prefers to stand: a side of the anchor, and how it lines up along that side (POP-2). The thirteen values are the tooltip's, read the same way; the menu's four are among them. top and bottom centre it above or below the anchor; with -start or -end its start or end edge meets the anchor's. start and end centre it beside the anchor; with -top or -bottom its top or bottom edge meets the anchor's. auto takes the first side with room (POP-3). Start and end follow the layout direction. The popover flips to the opposite side when the preferred one has no room, keeping the alignment (POP-5). The default is the menu's: below the anchor, start edges aligned.
offset number null The distance between the anchor's edge and the popover, in the platform's layout units; 0 lets them touch. Null takes comp.popover.offset, the system's answer for every popover. Set it only where one anchor's box is not what the eye takes for its edge.
crossOffset number 0 How far the popover moves along the side it stands on, in the platform's layout units, from where placement puts it: positive towards the end for a popover above or below, towards the bottom for one beside. It never moves the popover off the anchor's side (POP-4).
arrow boolean null Whether the popover draws an arrow that points at the anchor. Null, the default, takes comp.popover.arrow.visible, the system's choice, which is off in the default system (POP-10).
width content | anchor "content" What decides the container's width. content: as wide as its content, between comp.popover.container.min-width and comp.popover.container.max-width. anchor: as wide as the anchor, for a surface that drops from a field and should read as its continuation (POP-7). A structural choice drawn by code, not a token axis.
compactPresentation sheet | own "sheet" How the popover is presented in a compact window, one narrower than comp.popover.compact.window-width. sheet: with the bottom sheet's appearance at the window's bottom edge, as a modal dialog (POP-39 to POP-41), as every popover and every dialog of Tanrei is there. own: as a popover in every window, beside its anchor and not modal, narrowing to the window (POP-43), for a surface that must stay beside what it belongs to (a note beside the word it explains). initialFocus anchor keeps the popover's own presentation whatever this says. A structural choice drawn by code, not a token axis.
inset boolean true Insets the content by the container's paddings. Set it to false for content that pads itself or reaches the edges (a list whose rows carry their own padding, an image); the header and the actions keep their insets.
initialFocus popover | anchor "popover" Where focus is when the popover has opened. popover: focus moves into it (POP-19), which is right whenever the opener is a button. anchor: focus stays where it is, for a popover that opens under a field the person is typing in, whose keys the field keeps; Tab then moves into the popover (POP-20). It also keeps the popover beside its anchor in a compact window (POP-43): a modal sheet would make the field inert.
headline string "" What the popover is about, drawn in its header and used as its accessible name. Empty draws no headline; the popover is then named by label.
label string "" Names the popover for assistive technology where it draws no headline ("Text formatting"). Never drawn. Required while headline is empty; ignored while headline is given.
closable boolean false Draws the close button at the end of the header. Escape and a press outside already close a popover, so the button is for a popover large enough to cover what a person would press outside it, and for touch, where there is no Escape.
closeLabel string "" The close button's accessible name ("Close filters"). Required while closable is true.
dividerVisibility auto | always | scroll "auto" When the dividers below the header and above the actions are drawn while the content scrolls between them. always: both, for as long as the content scrolls, wherever it is scrolled to. scroll: each only while content lies beyond it — below the header while the content is not at its top, above the actions while more of it lies below. auto takes comp.popover.divider.visibility, the design system's answer (always in the default system). Under every choice a popover whose parts all fit draws none.

Slots

Slot Descripción
default The content, below the header. A fixture's content string is plain text, which takes sys.typescale.body in the content color (POP-14).
actions The action items: Tanrei Buttons, each marked confirming or dismissive where it is one. The popover places them (POP-15) and gives those without a variant its own (POP-16). A fixture fills it through its items.

Eventos

Evento Descripción
close The popover asks to be closed, every overlay opened from inside it with it: Escape or the platform's back, a press outside, focus leaving it, its close button, or its anchor going out of sight. Carries which of these it was, named escape, back, outside, focus-out, close or anchor-hidden (in the platform's casing). An action never causes it (POP-17). A popover presented as a sheet sends escape, back, outside (a press on its scrim) or close, never focus-out or anchor-hidden (POP-40).

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.

action

One action at the foot of the popover: a Tanrei Button, placed by the popover as a Button group places its items and given the popover's variant unless the product set one.

Propiedad Tipo Valor predeterminado Descripción
variant string "" The Button's variant (filled, tonal, elevated, outlined, text, or one a system added). Empty, the default: the popover gives it the variant comp.popover.actions.variant names, tonal in the default system (POP-16). Set it to give one action another look.
disabled boolean false As the Button's disabled. A disabled action keeps its place and its share of the width.
confirming boolean false The action completes what the popover is for (Apply, Save, Share). Placed at the end of a row and at the top of a column (BGR-13, BGR-14). It decides the place only, never the look (BGR-19).
dismissive boolean false The action backs out (Cancel, Reset). Placed at the start of a row and at the bottom of a column (BGR-13, BGR-14). It decides the place only, never the look (BGR-19).
Slot Descripción
label The Button's label, as its default slot. A fixture item's content is this label.
Evento Descripción
click The action was activated, as the Button's click. The popover does not close by itself (POP-17).
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento tanrei-button TanreiButton TanreiButton TanreiButton Button
variant · Propiedad variant variant variant variant variant
disabled · Propiedad disabled disabled enabled onPressed disabled
confirming · Propiedad confirming tanreiGroupPlacement(.confirming) Modifier.confirming() TanreiButtonGroupItem.placement confirming
dismissive · Propiedad dismissive tanreiGroupPlacement(.dismissive) Modifier.dismissive() TanreiButtonGroupItem.placement dismissive
click · Evento click action onClick onPressed onPress
Notas A tanrei-button child with slot="actions": its label is its content. confirming and dismissive are boolean attributes that the popover reads, as the button group does. A Button without a variant attribute takes the popover's variant through the internal controller the group already gives its items (POP-16); the attribute, once written, wins. A TanreiButton in the actions: builder. The flags are the button group's .tanreiGroupPlacement(_:) modifier (one enum: both cannot be set). A Button created with variant: nil reads the popover's variant from an environment value the popover sets (internal to the package). A TanreiButton in the actions lambda, whose receiver is the button group's scope, so the flags are its Modifier.confirming() and Modifier.dismissive(). enabled is disabled's inverse. variant: TanreiButtonVariant? = null takes the popover's from a CompositionLocal (internal to the library). A TanreiButton in actions:; a null onPressed disables it. The flags are the button group's TanreiButtonGroupItem(placement:, child:) (one enum: both cannot be set). A Button with variant: null takes the popover's from an InheritedWidget (internal to the package). A Button in the actions prop. confirming and dismissive are props the popover reads from its actions, as the button group reads them. A Button with no variant takes the popover's from a React context (internal to the package).

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
POP-1 WHEN open becomes true THE popover SHALL present its container in the platform's overlay layer, the one the menu's surface uses (the web's top layer through the popover API, an overlay SwiftUI draws above its anchor, Compose's Popup, Flutter's Overlay, React Native's overlay layer that TanreiProvider renders), above the other content of its window at comp.popover.container.elevation; WHILE it is open beside its anchor — in every window but a compact one where it is a sheet (POP-39, POP-40) — THE popover SHALL NOT trap focus, SHALL NOT make any other part of the window inert, unscrollable or hidden from assistive technology and SHALL draw no scrim: it is an operated overlay that is not modal. WHEN open becomes false THE popover SHALL remove the container.
POP-2 THE popover SHALL read placement as a side and an alignment along that side, as the tooltip reads the same thirteen values (TIP-22): top and bottom SHALL put the container above or below the anchor with its centre on the anchor's, top-start and bottom-start with its start edge on the anchor's start edge, and top-end and bottom-end with its end edge on the anchor's end edge; start and end SHALL put it beside the anchor, on the side the layout direction gives that name, with its centre on the anchor's, start-top and end-top with its top edge on the anchor's top edge, and start-bottom and end-bottom with its bottom edge on the anchor's bottom edge. WHEN placement changes while the popover is shown THE popover SHALL take its new place at once, without a motion, keeping its focus.
POP-3 WHILE placement is auto THE popover SHALL try the sides in the order bottom, top, end, start and SHALL take the first that has room for the container within comp.popover.window-margin, or the one with the most room WHEN none has, with its start edge on the anchor's start edge for bottom and top and its top edge on the anchor's top edge for end and start, and SHALL then shift, hold and follow as POP-5 says, keeping the side it took for as long as that side has room.
POP-4 THE popover SHALL take the distance between the anchor's edge and the nearest part of itself — the container's edge, or the arrow's tip while an arrow is drawn — from offset, a number of the platform's layout units used as given, so that 0 lets the two touch, and from comp.popover.offset WHILE offset is null; it SHALL then move the container along its side by crossOffset, before the shift of POP-5, and by no more than leaves comp.popover.arrow.width of the container beside the anchor, so that the popover still stands on the anchor's side and an arrow can still reach the anchor. IF offset is negative or not a finite number THE popover SHALL use comp.popover.offset in its place, IF crossOffset is not a finite number THE popover SHALL use 0, and in debug builds it SHALL report the value it ignored.
POP-5 THE popover SHALL keep its container at least comp.popover.window-margin inside the window's safe area by these steps, in this order: WHEN the preferred side has no room for the container and the opposite side has, it SHALL flip to the opposite side, keeping the alignment; WHEN neither has room it SHALL use the one of the two with more room and limit the container to that room (POP-7 for its width, POP-8 for its height); it SHALL then shift along the side as far as the margin asks and no farther; and WHERE the container is still larger than the room on its side (a window smaller than the popover's least size) it SHALL move the container back into the margin over the anchor, its top and start edges first, rather than leave the window. It SHALL follow the anchor when the anchor moves (scrolling, resizing, layout), taking these steps again from the side it is on, which it SHALL keep for as long as that side has room, so that a page that scrolls does not throw the popover from side to side. WHEN the anchor is removed, hidden, scrolled out of the window, or scrolled out of sight inside a scrolling ancestor so that no part of it can be seen THE popover SHALL emit close once with the reason anchor-hidden and be closed as POP-21 says, because a surface with nothing beside it no longer says what it belongs to.
POP-6 THE popover SHALL fill its container with comp.popover.container.color, raise it by comp.popover.container.elevation, round it by comp.popover.container.shape eased 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 clip its header, content and actions to that shape. THE popover SHALL NOT change anything the container draws under a pointer, a press or focus: the container has no hovered, focused or pressed state and draws no state layer.
POP-7 WHILE width is content THE popover SHALL make its container as wide as its widest part (its header, its content, or its actions in one row at their own widths) and its paddings, at least comp.popover.container.min-width and at most comp.popover.container.max-width, wrapping content that would be wider; WHILE width is anchor it SHALL make the container as wide as the anchor, and at least comp.popover.container.min-width where the anchor is narrower, comp.popover.container.max-width not applying, and SHALL follow the anchor's width as it changes. In either case the container SHALL never be wider than the window's safe area less comp.popover.window-margin on each side, narrowing below comp.popover.container.min-width where the window asks it to.
POP-8 THE popover SHALL make its container as tall as its parts and its paddings, at most comp.popover.container.max-height and at most the room its side leaves within comp.popover.window-margin (POP-5). WHEN its parts do not fit in that height THE popover SHALL keep the header and the actions in place and scroll the content between them, SHALL let keyboard focus and assistive technology scroll it, and SHALL draw a divider of comp.popover.divider.width in comp.popover.divider.color across the container below the header and another above the actions: WHILE its divider visibility (POP-38) is always, both for as long as the content scrolls, wherever it is scrolled to, at its top included; WHILE it is scroll, the one below the header only WHILE the content is scrolled away from its top and the one above the actions only WHILE more of it lies below; WHILE everything fits, or there is no header or no action on that side of the content, THE popover SHALL draw no divider there. WHEN the header and the actions alone leave no room for the content (large text in a short window) THE popover SHALL scroll all of its parts together instead, so that nothing is cut off or unreachable. The dividers SHALL be hidden from assistive technology and SHALL show and hide at once, without motion.
POP-9 THE popover SHALL lay out, top to bottom, inside comp.popover.container.padding-block at the top and bottom edges of its container: WHILE headline is not empty or closable is true, the header (POP-11); then the content, comp.popover.container.gap below the header where there is one; then, WHILE the popover has at least one action, the actions, comp.popover.actions.margin-block-start below the content, or below the header where there is no content. WHILE inset is true THE popover SHALL inset the content by comp.popover.container.padding-inline from the container's start and end edges; WHILE inset is false it SHALL let the content reach the container's start and end edges — and its top and bottom edges where no header or no action stands there, the block padding then not applying on that side — clipped by the container's shape, the header and the actions keeping their own insets.
POP-10 THE popover SHALL draw an arrow WHILE arrow is true, and WHILE arrow is null and comp.popover.arrow.visible is on, and none otherwise. WHILE it draws one THE popover SHALL draw a triangle comp.popover.arrow.width wide at its base and comp.popover.arrow.height tall on the container's edge that faces the anchor, as one shape with the container — the container's color and elevation, with no seam and no shadow between the two — its tip pointing at the middle of the part of the anchor's edge that the container stands beside, and SHALL keep its base at least comp.popover.arrow.inset, and at least the corner's radius, from each end of that edge, standing at the edge's middle where the edge is too short for both; the container SHALL stand comp.popover.arrow.height farther from the anchor, so that the tip keeps the offset (POP-4). WHEN the container flips, shifts or follows the anchor (POP-5) the arrow SHALL move with it to the edge that now faces the anchor and point at the anchor again, and WHILE the container covers the anchor (POP-5) THE popover SHALL draw no arrow. The arrow SHALL take no press of its own and SHALL be hidden from assistive technology.
POP-11 WHILE headline is not empty or closable is true THE popover SHALL draw the header as one row: the headline from comp.popover.container.padding-inline after the container's start edge, taking the width the close button leaves, and, WHILE closable is true, the close button (POP-13) at the row's end, comp.popover.header.gap after the headline and with its edge comp.popover.container.padding-inline before the container's end edge, both aligned to the row's first line; the header SHALL be as tall as the taller of the two and SHALL stay in place while the content scrolls (POP-8).
POP-12 WHILE headline is not empty THE popover SHALL draw it in comp.popover.headline.typescale and comp.popover.headline.color, wrapping onto more lines rather than truncating and growing the header with it, and SHALL expose it as a heading. THE popover SHALL set the headline's letter-spacing by the script of its text, as every title is set (PRS-20): no negative letter-spacing WHERE its language is Chinese, Japanese or Korean, and none WHERE its script joins its letters or stacks marks on them.
POP-13 WHILE closable is true THE popover SHALL draw at the end of its header an icon-only Button of the small size showing the glyph close of the system's icon set and named by closeLabel, in the variant comp.popover.close-button.variant names, given through the channel a Button reads from the component that places it, and otherwise unrestyled: it reads Button's own tokens, answers hover, focus and press as any Button does and keeps its own minimum target (BTN-5); WHEN the user activates it THE popover SHALL emit close exactly once with the reason close. WHILE closable is true and closeLabel is empty it SHALL still draw the button, and in debug builds report that the button has no accessible name. THE popover SHALL draw no close mark of its own (no path, shape or character of its own).
POP-14 THE popover SHALL hand its content comp.popover.content.color as the platform's inherited foreground (the web's inherited color, SwiftUI's foreground style, Compose's content color, Flutter's default text and icon style, React Native's Tanrei text context); plain text given as content, where the platform must style it, SHALL take sys.typescale.body. THE popover SHALL NOT lay out, restyle or mirror the content itself, and SHALL let it hold any element that takes input — links, Buttons, fields, a list, a menu's or another popover's opener.
POP-15 WHILE the popover has at least one action THE popover SHALL lay out its actions as a standard Button group lays out its items (BGR-12 to BGR-19), comp.popover.actions.gap apart and comp.popover.container.padding-inline from the container's start and end edges, in the order the group gives them — dismissive at the start and confirming at the end of a row, confirming on top and dismissive at the bottom of a column, unflagged actions between them in the order given — with focus and reading order following that order (BGR-15). WHILE comp.popover.actions.arrangement is fill it SHALL make them a full-width group across the container's inner width, the actions sharing a row equally (BGR-17) or each as wide as the column; WHILE it is end it SHALL keep them at their own widths, the row aligned to the end, and a column as wide as its widest action and aligned to the end. It SHALL place them in a row WHILE every action's label fits its share on one line (fill) or the row at its own widths fits the inner width (end), and otherwise in a column, re-deciding when the container's width or the text size changes.
POP-16 WHILE an action's variant is empty THE popover SHALL give that Button the variant comp.popover.actions.variant names, through the channel a Button reads from the component that places it, so that the Button renders from that variant's tokens and states as it would with the variant set; WHILE an action's variant is set THE popover SHALL leave it as set. THE popover SHALL NOT change an action's size or tokens, and SHALL NOT change its look or emphasis for its confirming or dismissive (BGR-19).
POP-17 WHEN the user activates an action THE popover SHALL let the Button emit its own click and SHALL NOT close, emit close or otherwise answer it itself, whether the action is confirming, dismissive or neither; the product closes the popover from the action's handler by setting open to false, and focus then returns as POP-22 says.
POP-18 THE popover SHALL be shown exactly while open is true: it SHALL NOT open or close itself when a pointer enters or leaves its anchor or its container, when focus arrives at its anchor, on a long-press or after a time, and SHALL have no show or hide delay; the product sets open when the opener is activated by pointer, touch, Enter or Space, and sets it to false when the opener is activated again. THE popover SHALL expose on its opener that it opens a dialog, whether that dialog is open and which element it is, where the platform has those properties (aria-haspopup with the value dialog, aria-expanded and aria-controls on the web), and SHALL NOT change the opener's accessible name, role or look. The opener is the anchor unless the product names another element as the opener, in which case the container is placed against the anchor (POP-2 to POP-5) and everything this spec says of the opener holds for that element.
POP-19 WHEN open becomes true WHILE initialFocus is popover THE popover SHALL move focus to the element in it that the product marked for initial focus (the platform's own mark, named in the platform notes) or, without one, to its container, so that assistive technology reads its name first and the next Tab reaches its first focusable element; it SHALL show a focus indicator on the container only when the popover was opened from the keyboard, as sys.behavior.focus-style names. WHEN open becomes true WHILE initialFocus is anchor THE popover SHALL leave focus where it is and SHALL take no key from the element that has it except Escape (POP-23). Focus SHALL move when the popover opens, not when its motion ends; a popover mounted with open true SHALL NOT move focus.
POP-20 WHILE the popover is open THE popover SHALL stand in the focus order and in the reading order of assistive technology directly after its opener, wherever the platform draws it: Tab from the opener SHALL move to the popover's first focusable element, Shift+Tab from that element SHALL move back to the opener, Tab and Shift+Tab inside the popover SHALL move through its close button, its content's focusable elements and its actions in the order they are placed, and Tab from its last focusable element SHALL move to the element that follows the opener in the page's order, which closes the popover as focus leaving it does (POP-21). Focus on the opener SHALL count as focus that has not left the popover, so that Shift+Tab to the opener leaves the popover open. THE popover SHALL NOT wrap focus from its last element to its first.
POP-21 WHEN the user presses Escape or the platform's back (Android's back gesture or button, the iOS accessibility escape gesture), presses outside the container, its arrow, its anchor, its opener and every overlay opened from inside it (POP-24), moves focus to an element outside all of those, or activates the close button (POP-13), or WHEN the anchor goes out of sight (POP-5) THE popover SHALL emit close once with that reason (escape, back, outside, focus-out, close or anchor-hidden) and SHALL be closed: through its binding or controller where open is one (SwiftUI, Flutter), and by the product setting open to false where open is a plain value (web, Compose, React Native). A press on the opener or the anchor is never a press outside: the product's own handler closes the popover from it (POP-18), so that one press never closes and opens it again. Focus leaving the window (another window or application taking it) SHALL NOT count as focus leaving the popover, and neither SHALL a move of an assistive technology's reading cursor that moves no input focus. Whether a press outside also reaches the element under it SHALL follow the platform and be stated in its README, as for the menu (MNU-5).
POP-22 WHEN the popover closes THE popover SHALL return focus to its opener IF focus was inside the popover or an overlay opened from inside it, or was lost with it (a press outside that landed on nothing focusable), and SHALL leave focus where the person put it IF the close came from focus moving to, or a press landing on, another focusable element; WHERE the opener no longer exists or cannot take focus it SHALL fall to the platform's default for a closed overlay. It SHALL show the opener's focus indicator only when the close came from the keyboard. Focus SHALL move when the popover closes, not when its motion ends.
POP-23 WHILE the popover is open, WHEN the user presses Escape or the platform's back and neither an overlay opened from inside it nor an element inside it has taken the key for itself (an open menu, a tooltip, a field composing text or clearing its value) THE popover SHALL take the key and close as POP-21 says, and SHALL let it go no further, so that it does not also close a dialog or a sheet the popover stands in; WHILE several overlays are open Escape SHALL close only the topmost.
POP-24 WHEN an overlay opens from an element inside the popover's container — a menu, a tooltip, a docked picker or a select's surface, another popover, a dialog or a modal sheet — THE popover SHALL treat it as its own: a press inside it SHALL NOT count as a press outside, focus moving into it SHALL NOT count as focus leaving, and the popover SHALL stay open under it, inert with the rest of the window WHILE that overlay is modal. WHEN that overlay closes THE popover SHALL receive back the focus it returns. WHEN the popover is closed, for a reason POP-21 names or because the product sets open to false, THE popover SHALL close every overlay opened from inside it that is not modal with it at once, their own close events carrying no reason of the person's, and SHALL return focus to its own opener as POP-22 says, not to an opener inside it. WHEN a popover is opened from inside a popover that was itself opened from inside a popover THE popover SHALL still work, and in debug builds SHALL report the depth.
POP-25 WHERE the popover's anchor is inside a modal overlay (a dialog, a modal sheet) THE popover SHALL be presented above that overlay and belong to it: operable while the rest of the window is inert, and in that overlay's focus loop directly after its opener (POP-20). WHEN a popover opens while another popover that it was not opened from is open THE popover SHALL be drawn above it; THE popover SHALL NOT close another popover by opening — the press or the focus that opened it closes the other as POP-21 says — so that the product that sets two open at once has two.
POP-26 WHEN the popover opens beside its anchor THE popover SHALL make its container appear as every anchored surface appears: from sys.motion.appear.opacity, sys.motion.appear.scale, sys.motion.appear.distance toward the anchor and sys.motion.appear.blur to whole, grown from the middle of the edge that faces the anchor — from its own centre WHERE sys.motion.appear.origin is center, and WHILE it covers its anchor (POP-5) — over comp.popover.motion.open-duration with comp.popover.motion.open-easing, and WHEN it closes THE popover SHALL take it back the same way to where it started, over comp.popover.motion.close-duration with comp.popover.motion.close-easing. WHERE sys.behavior.appearance is unfold the scale SHALL apply along the axis that leaves the anchor only, so that the container unfolds from that edge at its full width. WHERE motion is reduced THE popover SHALL only fade it in and out, over sys.motion.role.reduced, with no scale, distance or blur. WHEN it is mounted with open true THE popover SHALL appear without its motion while the document has not yet been first painted, and with it when it is mounted after that. The motion SHALL be drawn only: the place POP-5 gives and the room the popover is measured for are those of the popover at rest.
POP-27 WHEN the popover is told to close while it is still opening, or to open while it is still closing THE popover SHALL turn round from the opacity and the size it has reached, without first jumping to the start or the end of either motion, and SHALL take no longer over the rest than the whole duration of the motion it turns to. WHILE the popover is closed or closing THE popover SHALL take no pointer input and no focus — a press reaches what lies under it — and WHILE it is closed it SHALL take no room in the layout, draw nothing and expose nothing to assistive technology.
POP-28 THE popover SHALL expose its container as a dialog named by headline or, WHILE headline is empty, by label: one that is not modal, hiding nothing outside it from assistive technology, WHILE it stands beside its anchor, and a modal dialog WHILE it is presented as a sheet (POP-40); the close button SHALL be named by closeLabel, each action by its own label, and the arrow and the dividers SHALL be hidden. THE popover SHALL NOT expose its text as its anchor's description: what describes an anchor is a tooltip.
POP-29 IF headline and label are both empty THE popover SHALL still open, and in debug builds SHALL report that it has no accessible name. IF the popover opens with no content, no headline and no action THE popover SHALL draw nothing, take no focus and expose nothing, and in debug builds SHALL report an empty popover. IF no anchor can be found when open becomes true THE popover SHALL stay closed, emit nothing, and in debug builds report the missing anchor.
POP-30 WHILE the text size is enlarged THE popover SHALL keep its width limits (POP-7), wrap its headline and let its content wrap, grow its container's height with them up to the limits of POP-8 and scroll beyond them, stack its actions once a label no longer fits its share (POP-15), and SHALL NOT scale the close button, the arrow, the offset or the window margin beyond what the platform's layout units do. Nothing in the header or the actions SHALL be clipped or truncated.
POP-32 WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE popover SHALL draw an edge sys.border.hairline wide in the platform's text color round its container and its arrow, as one outline, because the mode removes the shadow and may give the container the page's color; its headline and plain text SHALL take the platform's text color.
POP-33 THE popover 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 popover is shown THE popover SHALL draw itself for the new direction at once, without a motion of its own, keeping its open state and its focus, the container placed again by the new direction (POP-34).
POP-34 WHEN the layout direction is right-to-left THE popover SHALL take the side start as the anchor's right side and the side end as its left, SHALL take the alignments -start and -end of a popover above or below as the right and the left edges, and a positive crossOffset of such a popover as a move towards the left, flipping and shifting from there as POP-5 says; it SHALL leave the sides top and bottom, the alignments -top and -bottom, a crossOffset beside the anchor and the centring on the anchor as they are, and SHALL keep auto's order of sides by their names (POP-3). It SHALL mirror its header — the headline at the right, the close button at the left — apply its paddings by logical start and end, and place its actions as a Button group places them right to left (BGR-8); it SHALL NOT mirror the glyph close, the arrow's shape, or its content, which follows the layout direction itself.
POP-35 THE popover SHALL lay out its headline as text of its own direction, isolated from the text around it: it 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 popover SHALL still place the headline and align its lines by the layout direction, not by the text's own.
POP-36 WHILE the on-screen keyboard is shown THE popover SHALL take the keyboard's top edge as an edge of the window's safe area (POP-5), and SHALL keep to the visible part of the window wherever that is smaller than the window: it SHALL flip, shift or shorten so that no part of it is under the keyboard, and SHALL scroll the focused element into the room left; WHEN the keyboard hides it SHALL take its place again by POP-5. It SHALL move as the platform moves content for its keyboard, and at once where motion is reduced.
POP-37 WHEN placement, width or initialFocus names a value the popover does not define THE popover SHALL render with the default value, and in debug builds report the unknown value.
POP-38 THE popover SHALL draw its dividers (POP-8) as its dividerVisibility names: WHILE dividerVisibility is auto, as comp.popover.divider.visibility names, which follows sys.behavior.divider-visibility unless a design system sets the popover apart; WHILE dividerVisibility is always or scroll, as that value names, whatever the tokens say. WHEN dividerVisibility, or the token it follows, changes THE popover SHALL draw the other form at once, moving no part and keeping the scroll position and focus; WHEN dividerVisibility names a value the popover does not define THE popover SHALL treat it as auto and in debug builds report the unknown value. A platform that cannot draw a choice SHALL fall back to the first value, always, and state that in its README.
POP-39 WHILE the window is narrower than comp.popover.compact.window-width (a compact window), compactPresentation is sheet and initialFocus is popover THE popover SHALL present its container with the bottom sheet's appearance (BSH-7) instead of beside its anchor: at the window's bottom edge, as wide as the window but no wider than comp.bottom-sheet.container.max-width and centred in it, filled with comp.bottom-sheet.container.color, raised by comp.bottom-sheet.container.elevation, its top corners rounded by comp.bottom-sheet.container.shape eased by sys.shape.corner-smoothing and its bottom corners square, its color reaching under the bottom system inset and its parts above it, and sitting on the on-screen keyboard while it is shown; as tall as its parts, but never taller than the window below its top system inset less comp.bottom-sheet.container.margin-block-start, its content scrolling beyond that as POP-8 says. It SHALL draw no arrow and no drag handle, follow no drag, close on no swipe, and ignore placement, offset, crossOffset and width there; its header, content and actions SHALL keep their own tokens and layout (POP-9 to POP-17, POP-38) across the sheet's width. WHERE comp.popover.compact.window-width is 0 no window is compact.
POP-40 WHILE the popover is presented as a sheet (POP-39) THE popover SHALL be a modal overlay, as a modal bottom sheet is (BSH-2): it SHALL draw comp.bottom-sheet.scrim.color with comp.bottom-sheet.scrim.blur over the whole window behind it (MDL-1), make everything outside it and the overlays opened from inside it inert, keep the content behind it from scrolling, keep focus inside it — Tab and Shift+Tab wrap between its last and first focusable elements — and expose its container as a modal dialog (POP-28). WHEN it opens it SHALL move focus into it as POP-19 says for initialFocus popover — mounted with open true as well, because everything behind it is then inert (as a modal sheet does, BSH-3) — and WHEN it closes it SHALL return focus to its opener as POP-22 says. A press on the scrim SHALL ask to close with the reason outside, Escape with escape (POP-23) and the platform's back with back, and the close button with close (POP-21); since focus cannot leave it and it does not stand beside its anchor, it SHALL NOT ask with focus-out or anchor-hidden and SHALL NOT follow its anchor. Its opener SHALL still say that it opens a dialog and whether it is open (POP-18).
POP-41 WHEN the popover opens as a sheet (POP-39) THE popover SHALL slide its container up from below the window's bottom edge into its place while fading the scrim in, as a modal bottom sheet opens (BSH-1), over comp.bottom-sheet.motion.enter-duration with comp.bottom-sheet.motion.enter-easing, and WHEN it closes there it SHALL slide the container down out of view while fading the scrim out, over comp.bottom-sheet.motion.exit-duration with comp.bottom-sheet.motion.exit-easing, turning round from where it has got to as POP-27 says. WHERE motion is reduced it SHALL fade in and out in place without sliding, or appear and disappear at once on a platform whose reduced-motion setting removes every animation (Android's). Focus SHALL move when it opens or closes, not when its motion ends.
POP-42 WHEN the window's width crosses comp.popover.compact.window-width, or compactPresentation or initialFocus changes, while the popover is open THE popover SHALL take the other presentation at once, without a motion — beside its anchor and not modal (POP-1 to POP-5), or as a sheet (POP-39, POP-40) — keeping its open state, its scroll position and the element in it that has focus, and SHALL emit nothing; IF focus is outside it (on its opener) as it becomes a sheet THE popover SHALL move focus into it as POP-19 says, because the opener is then inert.
POP-43 WHILE compactPresentation is own, or initialFocus is anchor, THE popover SHALL stay an anchored popover that is not modal in every window, compact ones included: WHERE the window is narrower or shorter than the container wants THE popover SHALL narrow and shorten the container as POP-7 and POP-8 say, cover its anchor as POP-5 says where it must, and scroll, and SHALL NOT draw a scrim or trap focus. A popover whose initialFocus is anchor stays beside its anchor because focus stays in the element the person is typing in, which a modal sheet would make inert. WHEN compactPresentation names a value the popover does not define THE popover SHALL treat it as sheet and in debug builds report the unknown value.
POP-44 WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE popover 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 its dialog (POP-28), beside its anchor and as a sheet alike — an id reference by the elements it names in the host's tree or a tree around it, a description after its own. WHILE headline or label is not empty it is the dialog's name (POP-28), over a name given so; WHILE both are empty, a name given so is the dialog's name and SHALL NOT be reported as having none in debug builds. 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 popover SHALL take it off that element.

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-popover tanreiPopover TanreiPopover TanreiPopover Popover
open · Propiedad open isPresented visible controller visible
placement · Propiedad placement placement placement placement placement
offset · Propiedad offset offset offset offset offset
crossOffset · Propiedad crossOffset crossOffset crossOffset crossOffset crossOffset
arrow · Propiedad arrow arrow arrow arrow arrow
width · Propiedad width width width width width
compactPresentation · Propiedad compactPresentation compactPresentation compactPresentation compactPresentation compactPresentation
inset · Propiedad inset inset inset inset inset
initialFocus · Propiedad initialFocus initialFocus initialFocus initialFocus initialFocus
headline · Propiedad headline headline headline headline headline
label · Propiedad label accessibilityLabel contentDescription semanticLabel accessibilityLabel
closable · Propiedad closable closable closable showCloseButton closable
closeLabel · Propiedad closeLabel closeLabel closeLabel closeButtonSemanticLabel closeLabel
dividerVisibility · Propiedad dividerVisibility dividerVisibility dividerVisibility dividerVisibility dividerVisibility
close · Evento close onClose onDismissRequest onClose onClose
Notas <tanrei-popover anchor="…"> names its anchor as the menu does: anchor is the id of the anchor element in the popover's own tree, and anchorElement a property for an element that an id cannot reach (another shadow root); opener and openerElement name a separate opener, as the docked picker's do (POP-18). The popover sets aria-haspopup="dialog", aria-controls and aria-expanded on the opener and removes them when it is disconnected. Attributes are kebab-case (cross-offset, initial-focus, close-label, compact-presentation); offset and crossOffset are number properties in CSS pixels parsed as the tooltip's are, so an empty attribute is not a number; arrow is true, false or null — absent for null, present for true and ="false" for false; inset is a boolean property that defaults to true, whose attribute is the inverse no-inset. close is a CustomEvent whose detail.reason is escape, back, outside, focus-out, close or anchor-hidden; the product sets open to false from it. Slots: default and actions. The container is a popover="manual" element with role="dialog" and no aria-modal in the shadow root, so it is in the top layer (above an open modal <dialog> when the element is a descendant of that dialog, which it must be to be operable there, POP-25); manual and not auto, because the popover implements its own light dismiss so that close carries a reason and so that an overlay opened from inside it is not closed by the browser's own rule. It is placed with CSS anchor positioning where the browser has it and by measuring otherwise, and follows its anchor with the shared FollowController. The container and the arrow are one painted shape, so the elevation's ring and shadows run round both. Focus order (POP-20): the top layer does not change the tab order, so where the element does not directly follow its opener in the document the popover moves focus itself — Tab on the opener while open goes to the first tabbable element of the popover, Shift+Tab from that element to the opener, and Tab from the last to the element after the opener. The autofocus attribute on a slotted element marks initial focus; the container has tabindex="-1". A press outside also reaches the element under it. Under forced-colors: active the painted shape takes a CanvasText outline (POP-32). The inline attribute, for tests, draws the open container in place. Snapshot harness: a stage of 480 × 480 with a medium outlined tanrei-button labelled "Storage" at its centre and the popover open against it; width-anchor gives the button a width of 240; the screenshot is the stage. dividerVisibility is the divider-visibility attribute (auto, always, scroll); auto reads --tanrei-comp-popover-divider-visibility from the cascade with a style query, and where the browser has none the popover writes the name it read on its clip when it lays out. Compact (POP-39 to POP-42): a media query cannot read a custom property, so the popover reads the resolved comp.popover.compact.window-width through a probe and matches it with matchMedia('(width < …px)'), presenting again at once on change. As a sheet the container is a native <dialog> opened with showModal() in the shadow root, in place of the popover="manual" element: in the top layer over ::backdrop (the scrim), the rest of the page inert and held from scrolling. The slotted content is the same, and the element that had focus is focused again across the change. The keyboard's cover is read from visualViewport. The inline attribute keeps the anchored presentation in every window. Snapshot harness: the compact- fixtures are the real overlay in a 360 × 640 window, and the screenshot is the window. A modifier on the anchor: .tanreiPopover(isPresented: Binding<Bool>, headline:, accessibilityLabel:, placement:, offset: CGFloat? = nil, crossOffset:, arrow: Bool? = nil, width:, inset:, initialFocus:, closable:, closeLabel:, onClose:, actions:, content:); a separate opener is named with .tanreiPopoverOpener(_:) on that view. Dismissal writes false to the binding and then calls onClose with the reason. Drawn in an overlay above the window's content, anchored with anchor preferences, as the tooltip is, not with .popover: the system's is modal at compact widths and its arrow, placement and motion cannot be given by tokens. Initial focus is .defaultFocus or an @AccessibilityFocusState the product passes; the container is an accessibility container with the header trait on the headline, placed after the opener with accessibilitySortPriority; the iOS escape gesture is .accessibilityAction(.escape). A press outside does not reach what is under it. Snapshot harness: the anchor and the open popover in a fixed 480 × 480 frame. Not built on this platform yet: dividerVisibility (auto, always, scroll; POP-38) — the dividers still follow the scroll position, as scroll does. compactPresentation is compactPresentation: TanreiPopoverCompactPresentation = .sheet; in a horizontally compact size class the sheet form is a sheet the popover draws with the bottom sheet's tokens, as SwiftUI's own popover adapts (presentationCompactAdaptation), not .popover. Compose idiom, as the menu: TanreiPopover(visible, onDismissRequest, headline, contentDescription, placement, offset: Dp? = null, crossOffset, arrow: Boolean? = null, width, inset, initialFocus: TanreiPopoverInitialFocus, initialFocusRequester: FocusRequester? = null, closable, closeLabel, actions, content) inside the anchor's Box, shown in a Popup with a PopupPositionProvider of its own. visible rather than calling it only while open, so that the close motion can run. The caller closes it, so close is onDismissRequest and it carries the reason. The Popup is focusable while initialFocus is popover, so back reaches it; because a focusable popup is a window of its own, the popover moves focus itself at its ends (POP-20): Tab from its last element clears the popup's focus, asks the anchor's FocusManager to move next and reports focus-out. paneTitle is the headline or contentDescription. A press outside does not reach what is under it. Snapshot harness: the anchor and TanreiPopoverContent in place in a 480 × 480 dp box; the popup itself is covered by instrumented tests. Not built on this platform yet: dividerVisibility (auto, always, scroll; POP-38) — the dividers still follow the scroll position, as scroll does. compactPresentation is compactPresentation: TanreiPopoverCompactPresentation = Sheet; the sheet form is shown in a Dialog window whose own dim is off, Tanrei drawing the bottom sheet's scrim. Flutter idiom, as TanreiMenuAnchor: TanreiPopover(controller:, placement:, offset:, crossOffset:, arrow:, width:, inset:, initialFocus:, headline:, semanticLabel:, showCloseButton:, closeButtonSemanticLabel:, onClose:, actions: […], content:, builder:) wraps the anchor that builder returns; TanreiPopoverController.open() and .close() are open, and onClose carries the reason. Shown with an OverlayPortal and a CompositedTransformFollower: the portal's child stays where it is declared in the widget, focus and semantics trees, so the popover follows its anchor in traversal and reading order without more (POP-20). Outside presses are a TapRegion whose group the overlays opened from inside it join (POP-24); a press outside also reaches what is under it. autofocus: true on a descendant marks initial focus; Semantics(scopesRoute: false, namesRoute: true, label:) names the container. Snapshot harness: the anchor and the open popover in a 480 × 480 box. Not built on this platform yet: dividerVisibility (auto, always, scroll; POP-38) — the dividers still follow the scroll position, as scroll does. compactPresentation is compactPresentation: TanreiPopoverCompactPresentation.sheet; the sheet form is a modal route drawn with the bottom sheet's tokens. <Popover visible anchorRef openerRef? onClose headline …>{content}</Popover>, presented on the overlay layer that TanreiProvider renders (not a Modal, which would make the rest of the screen unreachable) and placed by measuring the anchor (measureInWindow), again on layout, scroll and keyboard events; Android's back button (BackHandler) is close with the reason back, and the iOS escape gesture is onAccessibilityEscape. Controlled: the caller sets visible to false from onClose. accessibilityViewIsModal stays false; the container is given accessibility focus when it opens and the opener when it closes. A hardware keyboard's Tab order follows the native view order, which the overlay layer puts last: the README states that the popover is reached from its opener by the focus it takes on opening, and returns to the opener on leaving. A press outside closes the popover and does not reach what is under it. Follows the app's direction where an ancestor's is not known. Not built on this platform yet: dividerVisibility (auto, always, scroll; POP-38) — the dividers still follow the scroll position, as scroll does. compactPresentation is a string prop ('sheet' | 'own'); the sheet form is presented in a Modal whose own dim and transition are off, with accessibilityViewIsModal true.

Tokens

Token Alias de Valor resuelto (claro)
--tanrei-comp-popover-actions-arrangement — 0
--tanrei-comp-popover-actions-gap sys.space.space200 16px
--tanrei-comp-popover-actions-margin-block-start sys.space.space200 16px
--tanrei-comp-popover-actions-variant — 1
--tanrei-comp-popover-arrow-height sys.space.space75 6px
--tanrei-comp-popover-arrow-inset sys.space.space100 8px
--tanrei-comp-popover-arrow-visible — 0
--tanrei-comp-popover-arrow-width sys.space.space150 12px
--tanrei-comp-popover-close-button-variant — 1
--tanrei-comp-popover-compact-window-width sys.breakpoint.medium 600px
--tanrei-comp-popover-container-color sys.color.surface-bright #ffffff
--tanrei-comp-popover-container-elevation sys.elevation.level2 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 2px 0px rgb(0 0 0 / 0.04)
--tanrei-comp-popover-container-gap sys.space.space150 12px
--tanrei-comp-popover-container-max-height — 480px
--tanrei-comp-popover-container-max-width — 400px
--tanrei-comp-popover-container-min-width — 160px
--tanrei-comp-popover-container-padding-block sys.space.space200 16px
--tanrei-comp-popover-container-padding-inline sys.space.space200 16px
--tanrei-comp-popover-container-shape sys.shape.corner.medium 8px
--tanrei-comp-popover-content-color sys.color.on-surface #1b1b1b
--tanrei-comp-popover-divider-color sys.color.outline-variant #000000 · 8%
--tanrei-comp-popover-divider-visibility sys.behavior.divider-visibility 0
--tanrei-comp-popover-divider-width sys.border.hairline 1px
--tanrei-comp-popover-header-gap sys.space.space100 8px
--tanrei-comp-popover-headline-color sys.color.on-surface #1b1b1b
--tanrei-comp-popover-motion-close-duration sys.motion.role.appear.anchored.exit 250ms
--tanrei-comp-popover-motion-close-easing sys.motion.easing.exit cubic-bezier(0.3, 0, 1, 1)
--tanrei-comp-popover-motion-open-duration sys.motion.role.appear.anchored.enter 300ms
--tanrei-comp-popover-motion-open-easing sys.motion.easing.enter cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-popover-offset sys.space.space50 4px
--tanrei-comp-popover-window-margin sys.space.space100 8px
--tanrei-comp-popover-headline-typescale sys.typescale.heading-6 500 14px/1.4286

Fixtures

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

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

Qué dibuja la web de otra manera

  • El popover es un elemento popover="manual" en la capa superior. Dentro de un <dialog> modal debe ser descendiente de ese diálogo para que se pueda manejar ahí.
  • En una ventana compacta, la hoja es un <dialog> nativo abierto con showModal(), y su velo es el ::backdrop del diálogo.
  • Una pulsación fuera cierra el popover y además llega al elemento que hay debajo.
  • La web no tiene un gesto de retroceso propio, así que close nunca lleva el motivo back.
  • anchorElement y openerElement son propiedades para un elemento al que un id no puede llegar, como uno que está en otro shadow root.
  • arrow no está definido, es true o es false: el atributo falta, es arrow o es arrow="false".
  • Marca con autofocus el elemento que recibe el foco al abrirse.
  • Con colores forzados, el contenedor y su flecha toman un contorno del color del texto.

Pautas

Aspecto Tooltip Popover Menú Diálogo
Lo abre El paso del cursor, el foco del teclado, una pulsación larga Una pulsación en su activador Una pulsación en su ancla El producto
Contiene Una descripción de su ancla Contenido que una persona maneja Solo elementos Una decisión
Recibe el foco Nunca Sí, y lo devuelve Sí, y lo devuelve Sí, y lo retiene
Bloquea la página No No (sí, como hoja en una ventana compacta) No Sí
  1. Si solo dice qué es el control y nada de lo que contiene se puede pulsar, es un tooltip.
  2. Si es una lista de comandos o de opciones, es un menú.
  3. Si la persona no puede continuar sin responder, es un diálogo.
  4. En cualquier otro caso es un popover.

Recomendado

  • Ábrelo con una pulsación en un control, y ciérralo cuando ese control se vuelva a pulsar.
  • Mantén breve el contenido. Una sola tarea y, como mucho, unos pocos campos.
  • Añade closable cuando el popover sea lo bastante grande para tapar lo que una persona pulsaría fuera de él, y para el tacto, donde no hay Escape.
  • Usa width="anchor" e initialFocus="anchor" para una superficie situada bajo un campo en el que la persona está escribiendo.
  • Pon compactPresentation en own solo para un popover que en una ventana compacta deba quedarse junto a aquello a lo que pertenece, como una nota junto a la palabra que explica.
  • Mantén cortas las etiquetas de las acciones. Una etiqueta más ancha que el popover no salta de línea.

Evita

  • No abras un popover al pasar el cursor ni con el foco. Así no se puede llegar a él con el tacto, y una superficie que toma el foco porque el puntero pasó por encima es una trampa.
  • No pongas en un popover lo que una persona perdería con una sola pulsación fuera por descuido. Un formulario largo va en un diálogo o en una hoja.
  • No anides más de un popover dentro de un popover. Cada nivel es una pulsación más de Escape.
  • No uses un popover para algo que la persona debe responder antes de continuar. Eso es un diálogo.

Accesibilidad

Rol
dialog
Etiquetado
The popover is a dialog that is not modal — a modal one while it is a sheet in a compact window (POP-40) — named by its headline, or by label where it draws none. The headline is also exposed as a heading. The opener keeps its own name and role and exposes that it opens a dialog and whether it is open. The close button is named by closeLabel and each action by its own label; confirming and dismissive are not announced, and the actions are read in the order they are placed (BGR-15). The arrow and the dividers are decorative. The popover is not its anchor's description.
Objetivo mínimo
El área de pulsación cumple el tamaño mínimo de objetivo aunque el tamaño visual sea menor. minimum 32px · pointer 44px · spacing 8px · touch 48px

Teclado

Teclas Acción
Enter / Space on the opener The product opens the popover; focus moves into it (to the element the product marked, otherwise to the container). Pressed again while it is open, the product closes it.
Tab / Shift+Tab Move through the popover's close button, its content and its actions; the popover comes directly after its opener in the order. Shift+Tab from its first element returns to the opener and leaves it open; Tab from its last element moves on to what follows the opener and closes it. Focus is never kept in. As a sheet in a compact window (POP-40) it keeps focus in: Tab and Shift+Tab wrap.
Escape Closes the popover and returns focus to its opener; with overlays opened from inside it, only the topmost. It goes no further: a dialog the popover stands in stays open. As a sheet, a press on the scrim closes it too.
Enter / Space Activate the focused close button (close, reason close) or action (its click; the popover does not close by itself, POP-17). The popover adds no default action to Enter.

Notas

  • The popover has no target of its own; its close button and its actions are Tanrei Buttons, which meet the minimum target themselves (BTN-5). comp.popover.actions.gap keeps each action's whole target in a column in the default system. The opener is the product's control and keeps its own target.
  • Tooltip, popover, menu, dialog — which one. A tooltip describes its anchor, opens on hover and focus, never takes focus and holds nothing a person can operate (TIP-13). A popover is opened by a press, takes focus and holds content a person operates. A menu is a list of commands or choices, moved through with the arrow keys; it holds only its items. A dialog stops the task until it is answered. A note that needs a link or a button is a popover, never a tooltip.
  • Never open a popover on hover or on focus (POP-18): what appears that way must be dismissible, hoverable and persistent (WCAG 1.4.13) and cannot be reached by touch, and a surface that takes focus because the pointer passed is a trap. Open it from a press.
  • Focus: the popover is an operated overlay that is not modal. It moves focus in when it opens and returns it to the opener when it closes, but does not keep it: Tab leaves it, which closes it. An open surface nobody is in would cover content the keyboard user has moved on to, and a pointer user closes it by pressing elsewhere; focus leaving is the keyboard's form of the same thing. This is the menu's rule, the docked picker's and the docked search view's.
  • Initial focus goes to the container unless the product marks an element (POP-19), as for the dialog: assistive technology reads the popover's name first, and a field that takes focus by itself would raise the on-screen keyboard over a popover the person has not read. Mark the first field of a short form.
  • initialFocus anchor is for a surface under a field the person is typing in (suggestions, a format hint with a link). Focus stays in the field, so the field's own keys keep working; the popover is still next in the Tab order, and still closes when focus leaves both.
  • Reading order: the popover follows its opener for a screen reader as it does for Tab (POP-20). On the web, place the element directly after its anchor in the document where the layout allows; where it cannot be, the opener's aria-controls and the focus that moves in carry a person there.
  • A press outside closes the popover. Do not put in a popover what a person would lose by one stray press: a long form belongs in a dialog or a sheet. Where open is a plain value the product may also answer close with the reason outside by leaving the popover open; do that only with a visible way to close it (closable, or a dismissive action).
  • Keep nesting shallow: an overlay opened from a popover works at any depth (POP-24), but each level is one more thing Escape must be pressed for. One popover inside a popover is the most a product should need; a debug build reports a third.
  • Not modal means the rest of the page can still be read and scrolled, and a screen reader's cursor can leave the popover without closing it (POP-21). Nothing a person must answer before going on belongs in a popover.
  • Text size and small windows: at 200% text the popover wraps, grows to its limits and scrolls. In a compact window it is a sheet across the window's width (POP-39); one that keeps its own presentation is, at a window 320 wide, the window's width less the margins (POP-43). It never needs scrolling in two directions.
  • A compact window (POP-39 to POP-42): there a popover takes the bottom sheet's appearance, as every popover and every dialog of Tanrei does, and becomes a modal dialog — a scrim, everything behind it inert, focus kept in, Escape, back or a press on the scrim to close it. Modal because the sheet form is what the platforms make of a popover on a phone, and they make it modal: iOS adapts a popover to a sheet in a compact size class (UIPopoverPresentationController's adaptive sheet; SwiftUI's presentationCompactAdaptation), and Apple's guidelines present a popover's content in a compact view as a modal sheet; React Spectrum presents a popover as a modal on mobile; Material's bottom sheet that stands in for a menu or a dialog on mobile is a modal one. A surface over a scrim that left the page operable would show one thing and do another (WAI-ARIA APG, dialog: a modal dialog makes what is behind it inert). The opener still says it opens a dialog. Set compactPresentation to own for a popover that must stay beside what it belongs to; initialFocus anchor always keeps it there, because the field being typed in must stay usable.
  • Forced colors (POP-32): the container gains an edge in the text color, because its shadow is removed. Contrast in the default system: on-surface on surface-bright is held to 4.5:1 by the token invariants; the container is told from the page by its elevation's ring and shadow, which are not held to a contrast of their own, as for the menu.

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

De qué se encarga el componente

  • El contenedor es un diálogo no modal que recibe su nombre de headline o de label. Nada de lo que queda fuera se oculta ni se deja inerte.
  • Como hoja en una ventana compacta, el contenedor es un diálogo modal: todo lo que queda fuera se vuelve inerte, y Tab y Mayús+Tab dan la vuelta dentro de él. Al cerrarse, el foco vuelve al activador.
  • El activador expone que abre un diálogo y si ese diálogo está abierto. Su nombre y su rol no cambian.
  • Al abrirse, el foco pasa al elemento que marcaste, o al contenedor, de modo que lo primero que se lee es el nombre del popover. El indicador de foco se muestra en el contenedor solo cuando el popover se abrió con el teclado.
  • El popover va justo después de su activador en el orden de tabulación, esté donde esté en el documento. Tab desde su último elemento sigue hacia lo que viene después del activador, y eso lo cierra. El foco nunca queda retenido dentro.
  • Escape cierra solo la superposición que está más arriba y no va más allá: un diálogo en el que está el popover sigue abierto.
  • Al cerrarse, el foco vuelve al activador si estaba dentro del popover. Si la persona llevó el foco a otro lugar, se queda ahí.
  • Una superposición abierta desde dentro del popover, como un menú o un tooltip, le pertenece: una pulsación en ella, o el foco en ella, no cierra el popover.
  • Con el texto al 200%, el contenido del popover salta de línea, y el popover crece hasta sus límites y se desplaza. En una ventana compacta es una hoja a todo el ancho de la ventana. Un popover que conserva su propia presentación tiene, en una ventana de 320 de ancho, la anchura de la ventana menos los márgenes.

Qué debes hacer tú

  • Da al popover un headline o un label, y un closeLabel mientras sea closable.
  • Define open cuando se pulse el activador y ponlo en false al recibir close.
  • En la web, pon el elemento del popover justo después de su activador en el documento siempre que puedas, para que el orden de lectura de un lector de pantalla siga el orden de tabulación.
  • Marca el primer campo de un formulario breve para que reciba el foco inicial. Deja sin marca un popover de texto.