O objetivo da iniciativa Open UI é facilitar a criação de ótimas experiências do usuário para os desenvolvedores. Para isso, estamos tentando resolver os padrões mais problemáticos que os desenvolvedores enfrentam. Podemos fazer isso oferecendo APIs e componentes integrados à plataforma melhores.
Um desses problemas são os pop-ups, descritos na Open UI como "Popovers".
As janelas pop-over têm uma reputação bastante polarizada há muito tempo. Isso se deve, em parte, à maneira como eles são criados e implantados. Não é um padrão fácil de criar, mas pode gerar muito valor ao direcionar os usuários para determinadas coisas ou informá-los sobre o conteúdo do seu site, principalmente quando usado de maneira adequada.
Geralmente, há duas principais preocupações ao criar popovers:
- Como garantir que ele seja colocado acima do restante do conteúdo em um local adequado.
- Como torná-lo acessível (compatível com teclado, focalizável etc.).
A API Popover integrada tem uma variedade de objetivos, todos com o mesmo objetivo geral de facilitar a criação desse padrão para os desenvolvedores. Algumas dessas metas são:
- Facilita a exibição de um elemento e seus descendentes acima do restante do documento.
- Deixe acessível.
- Não exigir JavaScript para os comportamentos mais comuns (dispensa leve, singleton, empilhamento etc.).
Confira a especificação completa de pop-ups no site OpenUI.
Compatibilidade com navegadores
Onde você pode usar a API Popover integrada agora? No momento da redação deste artigo, ele é compatível com o Chrome Canary por trás da flag "Recursos experimentais da plataforma da Web".
Para ativar essa flag, abra o Chrome Canary e acesse chrome://flags. Em seguida, ative a flag "Recursos experimentais da plataforma da Web".
Há também um teste de origem para desenvolvedores que querem testar isso em um ambiente de produção.
Por fim, há um polyfill em desenvolvimento para a API. Confira o repositório em github.com/oddbird/popup-polyfill.
Para verificar se há suporte a pop-ups, use:
const supported = HTMLElement.prototype.hasOwnProperty("popover");
Soluções atuais
O que você pode fazer para promover seu conteúdo acima de tudo? Se o navegador for compatível, use o elemento de caixa de diálogo HTML. Você precisaria usá-lo na forma "Modal". Isso exige o uso de JavaScript.
Dialog.showModal();
Há algumas considerações sobre acessibilidade. É recomendável usar a11y-dialog, por exemplo, se você atender usuários do Safari com versões anteriores à 15.4.
Você também pode usar uma das muitas bibliotecas baseadas em popover, alerta ou dica. Muitos deles funcionam de maneira semelhante.
- Adicione um contêiner ao corpo para mostrar popovers.
- Estilize para que ele fique acima de tudo.
- Crie um elemento e adicione-o ao contêiner para mostrar um popover.
- Oculte-o removendo o elemento popover do DOM.
Isso exige uma dependência extra e mais decisões para os desenvolvedores. Também é necessário pesquisar para encontrar uma oferta que ofereça tudo o que você precisa. A API Popover foi criada para atender a muitos cenários, incluindo dicas. O objetivo é abranger todos esses cenários comuns, evitando que os desenvolvedores tenham que tomar mais uma decisão para que possam se concentrar na criação das experiências.
Seu primeiro pop-up
Isso é tudo o que você precisa.
<div id="my-first-popover" popover>Popover Content!</div>
<button popovertoggletarget="my-first-popover">Toggle Popover</button>
Mas o que está acontecendo aqui?
- Não é necessário colocar o elemento popover em um contêiner ou algo assim. Ele fica oculto por padrão.
- Não é necessário escrever JavaScript para que ele apareça. Isso é processado pelo atributo
popovertoggletarget. - Quando ele aparece, é promovido para a camada superior. Isso significa que ele é promovido acima do
documentna janela de visualização. Você não precisa gerenciarz-indexnem se preocupar com a posição do popover no DOM. Ele pode estar aninhado no DOM, com ancestrais de corte. Também é possível conferir quais elementos estão na camada superior usando o DevTools. Para mais informações sobre a camada superior, confira este artigo.

- Você recebe o "Light Dismiss" pronto para uso. Isso significa que você pode fechar o popover com um sinal de fechamento, como clicar fora dele, navegar com o teclado para outro elemento ou pressionar a tecla Esc. Abra de novo e teste!
O que mais você recebe com o popover? Vamos continuar com o exemplo. Considere esta demonstração com algum conteúdo na página.
Esse botão de ação flutuante tem posicionamento fixo com um z-index alto.
.fab {
position: fixed;
z-index: 99999;
}
O conteúdo do popover fica aninhado no DOM, mas, quando você abre o popover, ele é promovido acima desse elemento de posição fixa. Não é necessário definir estilos.
Você também pode notar que o popover agora tem um pseudoelemento ::backdrop. Todos os elementos na camada superior recebem um pseudoelemento ::backdrop estilizado. Este exemplo estiliza ::backdrop com uma cor de segundo plano alfa reduzida e um filtro de plano de fundo, que desfoca o conteúdo subjacente.
Estilizar um popover
Vamos nos concentrar em estilizar o popover. Por padrão, um popover tem uma posição fixa e um padding aplicado. Ele também tem display: none. Você pode substituir isso para mostrar um popover. Mas isso não o promoveria para a camada superior.
[popover] { display: block; }
Independente de como você promove o popover, depois de promovê-lo para a camada superior, talvez seja necessário organizá-lo ou posicioná-lo. Não é possível segmentar a camada superior e fazer algo como
:open {
display: grid;
place-items: center;
}
Por padrão, um popover é exibido no centro da janela de visualização usando margin: auto. Mas, em alguns casos, talvez você queira ser explícito sobre o posicionamento. Exemplo:
[popover] {
top: 50%;
left: 50%;
translate: -50%;
}
Se você quiser dispor o conteúdo dentro do popover usando a grade CSS ou o flexbox, é recomendável envolver isso em um elemento. Caso contrário, você precisará declarar uma regra separada que mude o display quando o popover estiver na camada superior. Definir como padrão faria com que ele fosse mostrado por padrão, substituindo display: none.
[popover]:open {
display: flex;
}
Se você testou essa demonstração, vai notar que o popover agora está entrando e saindo. É possível fazer a transição de popovers para dentro e para fora usando o pseudoseletor :open. O pseudoseletor :open corresponde a popovers que estão sendo mostrados (e, portanto, na camada superior).
Este exemplo usa uma propriedade personalizada para impulsionar a transição. Você também pode aplicar uma transição ao ::backdrop do popover.
[popover] {
--hide: 1;
transition: transform 0.2s;
transform: translateY(calc(var(--hide) * -100vh))
scale(calc(1 - var(--hide)));
}
[popover]::backdrop {
transition: opacity 0.2s;
opacity: calc(1 - var(--hide, 1));
}
[popover]:open::backdrop {
--hide: 0;
}
Uma dica é agrupar transições e animações em uma consulta de mídia para movimento. Isso também pode ajudar a manter seus horários. Isso ocorre porque não é possível compartilhar valores entre popover e ::backdrop usando uma propriedade personalizada.
@media(prefers-reduced-motion: no-preference) {
[popover] { transition: transform 0.2s; }
[popover]::backdrop { transition: opacity 0.2s; }
}
Até agora, você viu o uso de popovertoggletarget para mostrar um popover. Para dispensar, usamos a opção "Dispensar leve". Mas você também recebe os atributos popovershowtarget e popoverhidetarget, que podem ser usados. Vamos adicionar um botão a um popover que o oculta e mudar o botão de alternância para usar popovershowtarget.
<div id="code-popover" popover>
<button popoverhidetarget="code-popover">Hide Code</button>
</div>
<button popovershowtarget="code-popover">Reveal Code</button>
Como mencionado anteriormente, a API Popover abrange mais do que apenas nossa noção histórica de pop-ups. Você pode criar para todos os tipos de cenários, como notificações, menus, dicas etc.
Alguns desses cenários precisam de padrões de interação diferentes. Interações como passar o cursor. O uso de um atributo popoverhovertarget foi testado, mas não está implementado no momento.
<div popoverhovertarget="hover-popover">Hover for Code</div>
A ideia é passar o cursor sobre um elemento para mostrar o destino. Esse comportamento pode ser configurado por propriedades de CSS. Essas propriedades de CSS definem o período de tempo para passar o cursor sobre um elemento e sair dele, ao que um popover reage. O comportamento padrão testado tinha um popover exibido após um 0.5s explícito de :hover. Em seguida, ele precisaria de uma dispensa leve ou da abertura de outro popover para dispensar (mais sobre isso em breve). Isso ocorreu porque a duração do ocultamento do popover foi definida como Infinity.
Enquanto isso, você pode usar JavaScript para fazer um polyfill dessa funcionalidade.
let hoverTimer;
const HOVER_TRIGGERS = document.querySelectorAll("[popoverhovertarget]");
const tearDown = () => {
if (hoverTimer) clearTimeout(hoverTimer);
};
HOVER_TRIGGERS.forEach((trigger) => {
const popover = document.querySelector(
`#${trigger.getAttribute("popoverhovertarget")}`
);
trigger.addEventListener("pointerenter", () => {
hoverTimer = setTimeout(() => {
if (!popover.matches(":open")) popover.showPopover();
}, 500);
trigger.addEventListener("pointerleave", tearDown);
});
});
O benefício de definir uma janela de passar o cursor explícita é que ela garante que a ação do usuário seja intencional (por exemplo, um usuário passa o ponteiro sobre um destino). Não queremos mostrar o pop-up a menos que essa seja a intenção do usuário.
Teste esta demonstração em que você pode passar o cursor sobre o destino com a janela definida como 0.5s.
Antes de analisar alguns exemplos e casos de uso comuns, vamos falar sobre algumas coisas.
Tipos de popover
Já abordamos o comportamento de interação sem JavaScript. Mas e o comportamento do popover como um todo? E se você não quiser o recurso "Descartar com um toque"? Ou você quer aplicar um padrão Singleton aos seus popovers?
A API Popover permite especificar três tipos de popover que diferem no comportamento.
[popover=auto]/[popover]:
- Suporte para aninhamento. Isso não significa apenas aninhado no DOM. Um popover ancestral é aquele que:
- relacionados por posição no DOM (filho).
- relacionados por atributos de acionamento em elementos filhos, como
popovertoggletarget,popovershowtargete assim por diante. - relacionados pelo atributo
anchor(API de ancoragem CSS em desenvolvimento).
- Dispensar com um toque.
- A abertura dispensa outros popovers que não são ancestrais. Teste a demonstração abaixo, que destaca como o aninhamento com popovers ancestrais funciona. Veja como mudar algumas das instâncias
popoverhidetarget/popovershowtargetparapopovertoggletargetmuda as coisas. - Ao dispensar uma notificação leve, todas são dispensadas, mas ao dispensar uma notificação na pilha, apenas as que estão acima dela são dispensadas.
[popover=manual]:
- Não fecha outros popovers.
- Sem descarte leve.
- Requer dispensa explícita por elemento de acionamento ou JavaScript.
JavaScript API
Quando você precisa de mais controle sobre os popovers, pode usar JavaScript. Você recebe um método showPopover e um hidePopover. Você também tem os eventos popovershow e popoverhide para detectar:
Mostrar um pop-up
js
popoverElement.showPopover()
Ocultar um pop-up:
popoverElement.hidePopover()
Ouça um popover sendo mostrado:
popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)
Aguarde a exibição de um popover e cancele:
popoverElement.addEventListener('popovershow',event => {
event.preventDefault();
console.warn(‘We blocked a popover from being shown’);
})
Detectar um popover sendo ocultado:
popoverElement.addEventListener('popoverhide', doSomethingWhenPopoverHides)
Não é possível cancelar um popover que está sendo ocultado:
popoverElement.addEventListener('popoverhide',event => {
event.preventDefault();
console.warn("You aren't allowed to cancel the hiding of a popover");
})
Verifique se um popover está na camada superior:
popoverElement.matches(':open')
Isso oferece mais potência para alguns cenários menos comuns. Por exemplo, mostrar um popover após um período de inatividade.
Esta demonstração tem popovers com estalos audíveis. Portanto, vamos precisar de JavaScript para reproduzir o áudio. Ao clicar, ocultamos o popover, reproduzimos o áudio e o mostramos novamente.
Acessibilidade
A acessibilidade é uma prioridade na API Popover. Os mapeamentos de acessibilidade associam o popover ao elemento de acionamento, conforme necessário. Isso significa que não é necessário declarar atributos aria-*, como aria-haspopup, desde que você use um dos atributos de acionamento, como popovertoggletarget.
Para o gerenciamento de foco, use o atributo de foco automático para mover o foco para um elemento dentro de um popover. Isso é igual a uma caixa de diálogo, mas a diferença aparece ao retornar o foco, e isso ocorre devido à dispensa leve. Na maioria dos casos, fechar um popover retorna o foco para o elemento focado anteriormente. Mas o foco é movido para um elemento clicado na dispensa leve, se ele puder receber o foco. Confira a seção sobre gerenciamento de foco na explicação.
Abra a versão em tela cheia desta demonstração para ver como ela funciona.
Nesta demonstração, o elemento em foco recebe um contorno verde. Tente usar a tecla Tab para navegar pela interface com o teclado. Observe onde o foco é retornado quando um popover é fechado. Você também pode notar que, se você pressionar a tecla Tab, o popover será fechado. Isso ocorre por design. Embora os popovers tenham gerenciamento de foco, eles não o prendem. A navegação por teclado identifica um sinal de fechamento quando o foco sai do popover.
Fixação (em desenvolvimento)
No caso dos popovers, um padrão complicado de atender é a ancoragem do elemento ao gatilho. Por exemplo, se uma dica for definida para aparecer acima do gatilho, mas o documento for rolado. A dica pode ser cortada pela janela de visualização. Há ofertas atuais de JavaScript para lidar com isso, como a Interface flutuante (link em inglês). Ela vai reposicionar a dica para evitar que isso aconteça e depender de uma ordem de posição desejada.
Mas queremos que você possa definir isso com seus estilos. Há uma API complementar em desenvolvimento junto com a API Popover para resolver isso. A API Posicionamento de âncora CSS permite vincular elementos a outros elementos, e isso é feito de maneira que os elementos sejam reposicionados para não serem cortados pela janela de visualização.
Esta demonstração usa a API Anchoring no estado atual. A posição do barco responde à posição da âncora na janela de visualização.
Confira um snippet do CSS que faz essa demonstração funcionar. Não é necessário JavaScript.
.anchor {
--anchor-name: --anchor;
}
.anchored {
position: absolute;
position-fallback: --compass;
}
@position-fallback --compass {
@try {
bottom: anchor(--anchor top);
left: anchor(--anchor right);
}
@try {
top: anchor(--anchor bottom);
left: anchor(--anchor right);
}
}
Confira a especificação aqui. Também haverá um polyfill para essa API.
Exemplos
Agora que você já sabe o que o popover tem a oferecer e como, vamos analisar alguns exemplos.
Notificações
Esta demonstração mostra uma notificação "Copiar para a área de transferência".
- Usa o fuso horário
[popover=manual] - Na ação, mostre o popover com
showPopover. - Após um tempo limite de
2000ms, oculte-o comhidePopover.
Avisos
Esta demonstração usa a camada superior para mostrar notificações no estilo toast.
- Um popover do tipo
manualatua como contêiner. - As novas notificações são anexadas ao popover, que é mostrado.
- Eles são removidos com a API de animações da Web ao clicar e removidos do DOM.
- Se não houver notificações para mostrar, o popover será ocultado.
Menu aninhado
Esta demonstração mostra como um menu de navegação aninhado pode funcionar.
- Use
[popover=auto], porque ele permite popovers aninhados. - Use
autofocusno primeiro link de cada menu suspenso para navegar com o teclado. - Esse é um candidato perfeito para a API CSS Anchoring. Mas, para esta demonstração, você pode usar uma pequena quantidade de JavaScript para atualizar as posições usando propriedades personalizadas.
const ANCHOR = (anchor, anchored) => () => {
const { top, bottom, left, right } = anchor.getBoundingClientRect();
anchored.style.setProperty("--top", top);
anchored.style.setProperty("--right", right);
anchored.style.setProperty("--bottom", bottom);
anchored.style.setProperty("--left", left);
};
PRODUCTS_MENU.addEventListener("popovershow", ANCHOR(PRODUCT_TARGET, PRODUCTS_MENU));
Lembre-se, como esta demonstração usa autofocus, ela precisará ser aberta na exibição de tela cheia para navegação por teclado.
Pop-up de mídia
Esta demonstração mostra como você pode abrir mídia.
- Usa
[popover=auto]para dispensar de forma leve. - O JavaScript detecta o evento
playdo vídeo e o abre. - O evento de popovers
popoverhidepausa o vídeo.
Popovers no estilo Wiki
Esta demonstração mostra como criar dicas de ferramentas de conteúdo inline que contêm mídia.
- Usa o fuso horário
[popover=auto]Mostrar um oculta os outros porque eles não são ancestrais. - Mostrado em
pointerentercom JavaScript. - Outro candidato perfeito para a API CSS Anchoring.
Gaveta de navegação
Esta demonstração cria uma gaveta de navegação usando um popover.
- Usa
[popover=auto]para dispensar de forma leve. - Usa
autofocuspara focar o primeiro item de navegação.
Como gerenciar planos de fundo
Esta demonstração mostra como gerenciar planos de fundo para vários popovers em que você quer que apenas um ::backdrop fique visível.
- Use JavaScript para manter uma lista dos popovers visíveis.
- Aplique um nome de classe ao popover mais baixo na camada superior.
Popover de cursor personalizado
Esta demonstração mostra como usar popover para promover um canvas para a camada superior e usá-lo para mostrar um cursor personalizado.
- Promova
canvaspara a camada superior comshowPopovere[popover=manual]. - Quando outros popovers forem abertos, oculte e mostre o popover
canvaspara garantir que ele esteja na parte de cima.
Popover da página de ações
Esta demonstração mostra como usar um popover como uma folha de ação.
- Mostrar o popover por padrão, substituindo
display. - A ActionSheet é aberta com o gatilho do popover.
- Quando o popover é mostrado, ele é promovido para a camada superior e traduzido para a visualização.
- É possível usar a rejeição leve para retornar.
Popover ativado pelo teclado
Esta demonstração mostra como usar o popover para uma interface de estilo de paleta de comandos.
- Use cmd + j para mostrar o popover.
- O
inputestá em foco comautofocus. - A caixa de combinação é um segundo
popoverposicionado abaixo da entrada principal. - A dispensa leve fecha a paleta se o menu suspenso não estiver presente.
- Outro candidato para a API Anchoring
Popover com tempo determinado
Esta demonstração mostra um popover de inatividade após quatro segundos. Um padrão de interface usado com frequência em apps que contêm informações seguras sobre um usuário para mostrar um modal de logout.
- Use JavaScript para mostrar o popover após um período de inatividade.
- Ao mostrar o popover, redefina o timer.
Protetor de tela
Assim como na demonstração anterior, você pode adicionar um toque de fantasia ao seu site e incluir um protetor de tela.
- Use JavaScript para mostrar o popover após um período de inatividade.
- Toque para dispensar e redefinir o timer.
Seguir cursor de texto
Esta demonstração mostra como um popover pode seguir um cursor de entrada.
- Mostre o popover com base na seleção, no evento de tecla ou na entrada de caracteres especiais.
- Use JavaScript para atualizar a posição do popover com propriedades personalizadas no escopo.
- Esse padrão exige uma consideração cuidadosa sobre o conteúdo mostrado e a acessibilidade.
- Ele é usado com frequência em interfaces de edição de texto e apps em que é possível adicionar tags.
Menu do botão de ação flutuante
Esta demonstração mostra como usar o popover para implementar um menu de botão de ação flutuante sem JavaScript.
- Promova um popover do tipo
manualcom o métodoshowPopover. Esse é o botão principal. - O menu é outro popover que é o destino do botão principal.
- O menu é aberto com
popovertoggletarget. - Use
autofocuspara focar o primeiro item do menu em exibição. - O toque fora do menu fecha o menu.
- A rotação do ícone usa
:has(). Saiba mais sobre:has()neste artigo.
Pronto!
Essa é uma introdução ao popover, que será lançado como parte da iniciativa Open UI. Usado com sensatez, ele será uma adição fantástica à plataforma da Web.
Não deixe de conferir Abrir UI. O popover explicativo é atualizado à medida que a API evolui. E aqui está a coleção de todas as demonstrações.
Agradecemos a visita!