O objetivo da iniciativa de interface aberta é facilitar a criação de ótimas experiências do usuário para os desenvolvedores. Para fazer isso, estamos tentando lidar com os padrões mais problemáticos que os desenvolvedores enfrentam. Para isso, oferecemos APIs e componentes integrados melhores para a plataforma.
Uma dessas áreas problemáticas são os pop-ups, descritos na interface aberta como "Popovers".
Os popovers têm uma reputação bastante polarizada há muito tempo. Isso ocorre, em parte, devido à forma como eles são criados e implantados. Eles não são um padrão fácil de criar, mas podem gerar muito valor ao direcionar os usuários a determinadas coisas ou informar sobre o conteúdo do seu site, especialmente quando usados de maneira adequada.
Geralmente, há duas preocupações principais ao criar pop-ups:
- Como garantir que ele seja colocado acima do resto do conteúdo em um lugar adequado.
- Como tornar o conteúdo acessível (compatível com teclado, focalizável etc.).
A API Popover integrada tem várias finalidades, todas com o mesmo objetivo geral de facilitar a criação desse padrão para os desenvolvedores. As principais delas são:
- Facilite a exibição de um elemento e dos descendentes dele acima do restante do documento.
- Torne-o acessível.
- Não exigem JavaScript para os comportamentos mais comuns (dispensamento leve, singleton, empilhamento etc.).
Confira a especificação completa para pop-ups no site do OpenUI.
Compatibilidade com navegadores
Onde você pode usar a API Popover integrada agora? No momento, esse recurso é compatível com o Chrome Canary.
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 queiram 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.
É possível verificar o suporte para pop-ups com:
const supported = HTMLElement.prototype.hasOwnProperty("popover");
Soluções atuais
O que você pode fazer para promover seu conteúdo acima de tudo? Se ele tiver suporte no seu navegador, use o elemento de diálogo HTML. Você precisa usá-lo no formulário "Modal". E isso requer JavaScript para ser usado.
Dialog.showModal();
Há algumas considerações sobre acessibilidade. É recomendável usar a11y-dialog, por exemplo, se você estiver atendendo usuários do Safari abaixo da versão 15.4.
Você também pode usar uma das muitas bibliotecas baseadas em pop-up, alerta ou dica. Muitos deles tendem a funcionar de maneira semelhante.
- Anexe um contêiner ao corpo para mostrar pop-ups.
- Dê um estilo para que ele fique acima de tudo.
- Crie um elemento e anexe-o ao contêiner para mostrar um pop-up.
- Para ocultá-lo, remova o elemento de pop-up do DOM.
Isso exige uma dependência extra e mais decisões dos desenvolvedores. Também é necessário pesquisar para encontrar uma oferta que ofereça tudo o que você precisa. A API Popover tem como objetivo atender a vários cenários, incluindo dicas. O objetivo é cobrir todos esses cenários comuns, evitando que os desenvolvedores precisem tomar mais uma decisão para que possam se concentrar na criação das experiências.
Seu primeiro pop-up
Isso é tudo 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 de pop-up em um contêiner ou algo do tipo. Ele fica oculto por padrão.
- Não é necessário escrever JavaScript para que ele apareça. Isso é tratado pelo atributo
popovertoggletarget
. - Quando ele aparece, é promovido para a camada superior. Isso significa que ela é promovida acima de
document
na janela de visualização. Você não precisa gerenciarz-index
ou se preocupar com a posição do pop-up no DOM. Ela pode estar profundamente aninhada no DOM, com recortes de ancestrais. Também é possível conferir quais elementos estão na camada superior usando o DevTools. Para saber mais sobre a camada superior, confira este artigo.
- O recurso "Dispensa de luz" já vem pronto para uso. Isso significa que você pode fechar o pop-up com um sinal de fechamento, como clicar fora dele, navegar até outro elemento usando o teclado ou pressionar a tecla Esc. Abra de novo e teste.
O que mais o pop-up oferece? Vamos analisar melhor o exemplo. Considere esta demonstração com algum conteúdo na página.
Esse botão de ação flutuante tem posicionamento fixo com uma z-index
alta.
.fab {
position: fixed;
z-index: 99999;
}
O conteúdo do pop-up é aninhado no DOM, mas, quando você o abre, ele é promovido acima do elemento de posição fixa. Não é necessário definir estilos.
O pop-up agora tem um pseudoelemento ::backdrop
. Todos os elementos na camada de cima recebem um pseudoelemento ::backdrop
estilizável. Este exemplo estiliza ::backdrop
com uma cor de plano de fundo alfa reduzida e um filtro de plano de fundo, que desfoca o conteúdo.
Estilizar um pop-over
Vamos voltar nossa atenção para o estilo do pop-over. Por padrão, um pop-over tem uma posição fixa e algum padding é aplicado. Ele também tem display: none
. Você pode substituir isso para mostrar um pop-up. Mas isso não a promoveria para a camada superior.
[popover] { display: block; }
Independentemente de como você promove o pop-up, depois de promovê-lo na camada superior, pode ser necessário colocá-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 pop-up é exibido no centro da janela de visualização usando margin: auto
. Mas, em alguns casos, convém ser explícito sobre o posicionamento. Exemplo:
[popover] {
top: 50%;
left: 50%;
translate: -50%;
}
Se você quiser definir o layout do conteúdo dentro do popover usando a grade CSS ou o flexbox, é recomendável incluir isso em um elemento. Caso contrário, você vai precisar declarar uma regra separada que mude o display
quando o pop-up estiver na camada de cima. A configuração 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 pop-up agora está fazendo a transição para dentro e para fora. É possível fazer a transição de popovers usando o pseudoseletor :open
. O pseudoseletor :open
corresponde aos pop-ups que estão sendo mostrados (e, portanto, na camada superior).
Este exemplo usa uma propriedade personalizada para direcionar a transição. Você também pode aplicar uma transição ao ::backdrop
do pop-over.
[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 aqui é 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 a propriedade personalizada.
@media(prefers-reduced-motion: no-preference) {
[popover] { transition: transform 0.2s; }
[popover]::backdrop { transition: opacity 0.2s; }
}
Até agora, você aprendeu a usar popovertoggletarget
para mostrar um popover. Para dispensá-la, usamos a opção "Dispensa leve". Mas você também tem os atributos popovershowtarget
e popoverhidetarget
que pode usar. Vamos adicionar um botão a um pop-over que o oculta e mudar o botão para que use 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 experimentado, mas não está implementado no momento.
<div popoverhovertarget="hover-popover">Hover for Code</div>
A ideia é que você passe o cursor sobre um elemento para mostrar o alvo. Esse comportamento pode ser configurado por meio de propriedades CSS. Essas propriedades do CSS definem o período de tempo para passar o cursor sobre um elemento e o período de tempo para que um pop-up reaja. O comportamento padrão testado apresentou um pop-over após um 0.5s
explícito de :hover
. Em seguida, seria necessário uma dispensa leve ou a abertura de outro pop-up para dispensar (mais informações sobre isso em breve). Isso ocorreu porque a duração de ocultação do pop-up foi definida como Infinity
.
Enquanto isso, use JavaScript para criar um polyfill para essa 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 passagem explícita é que ela garante que a ação do usuário seja intencional (por exemplo, um usuário passa o cursor sobre um destino). Não queremos mostrar o pop-up, a menos que essa seja a intenção.
Teste esta demonstração em que você pode passar o cursor sobre o alvo com a janela definida como 0.5s
.
Antes de explorar alguns casos de uso e exemplos comuns, vamos ver algumas coisas.
Tipos de pop-over
Já abordamos o comportamento de interação que não é JavaScript. Mas e o comportamento dos pop-ups como um todo? E se você não quiser usar a "Descartar com leveza"? Ou você quer aplicar um padrão de singleton aos seus pop-ups?
A API Popover permite especificar três tipos de pop-over, que diferem em comportamento.
[popover=auto]/[popover]
:
- Suporte para aninhamento. Isso não significa apenas aninhamento no DOM. A definição de um pop-up ancestral é:
- relacionadas pela posição do DOM (filha).
- relacionados acionando atributos em elementos filhos, como
popovertoggletarget
,popovershowtarget
e assim por diante. - relacionados pelo atributo
anchor
(API de ancoragem CSS em desenvolvimento).
- Luz apagada.
- A abertura dispensa outros popovers que não são popovers ancestrais. Confira a demonstração abaixo que destaca como funciona o aninhamento com popovers ancestrais. Veja como mudar algumas das instâncias
popoverhidetarget
/popovershowtarget
parapopovertoggletarget
muda as coisas. - A luz de dispensar uma dispensa todas, mas a dispensa de uma na pilha dispensa apenas as que estão acima dela.
[popover=manual]
:
- Não fecha outros popovers.
- Não há luz de dispensa.
- Requer dispensa explícita por elemento de acionamento ou JavaScript.
JavaScript API
Quando você precisa de mais controle sobre os pop-ups, pode usar o JavaScript. Você recebe os métodos showPopover
e hidePopover
. Você também tem eventos popovershow
e popoverhide
para detectar:
Mostrar um pop-up
js
popoverElement.showPopover()
Ocultar um pop-up:
popoverElement.hidePopover()
Detecte um pop-up sendo mostrado:
popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)
Detecte um pop-up sendo mostrado e cancele a exibição dele:
popoverElement.addEventListener('popovershow',event => {
event.preventDefault();
console.warn(‘We blocked a popover from being shown’);
})
Ouça um pop-over ser oculto:
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 pop-up está na camada superior:
popoverElement.matches(':open')
Isso oferece mais potência para alguns cenários menos comuns. Por exemplo, mostrar um pop-up após um período de inatividade.
Esta demonstração tem pop-ups com sons audíveis. Portanto, vamos precisar do JavaScript para reproduzir o áudio. Ao clicar, ocultamos o popover, tocamos o áudio e o mostramos novamente.
Acessibilidade
A acessibilidade está no centro do pensamento com a API Popover. Os mapeamentos de acessibilidade associam o pop-up ao elemento acionador, conforme necessário. Isso significa que não é necessário declarar atributos aria-*
, como aria-haspopup
, supondo que você use um dos atributos de acionamento, como popovertoggletarget
.
Para gerenciar o foco, use o atributo autofocus para mover o foco para um elemento dentro de um pop-up. Isso é igual ao de uma caixa de diálogo, mas a diferença ocorre ao retornar o foco, e isso ocorre por causa da dispensa leve. Na maioria dos casos, fechar um pop-over retorna o foco para o elemento anteriormente em foco. Mas o foco é movido para um elemento clicado na dispensação leve, se ele puder receber o foco. Confira a seção sobre gerenciamento de foco na explicação.
Você precisa abrir a versão em tela cheia desta demonstração para que ela funcione.
Nesta demonstração, o elemento em foco recebe um contorno verde. Tente usar as teclas de tabulação na interface com o teclado. Observe onde o foco é retornado quando um pop-up é fechado. Você também pode notar que, se você clicar em "Sobre", o pop-up será fechado. Isso ocorre por design. Embora os popovers tenham gerenciamento de foco, eles não prendem o foco. A navegação pelo teclado identifica um sinal de fechamento quando o foco sai do pop-up.
Ancoragem (em desenvolvimento)
Quando se trata de popovers, um padrão complicado a ser atendido é a ancoragem do elemento no gatilho. Por exemplo, se uma dica de ferramenta for definida para aparecer acima do acionador, mas o documento for rolado. Essa dica pode ser cortada pela viewport. Há ofertas atuais de JavaScript para lidar com isso, como UI flutuante. Eles reposicionam a dica para que isso não aconteça e dependem de uma ordem de posição desejada.
No entanto, queremos que você possa definir isso com seus estilos. Há uma API complementar em desenvolvimento com a API Popover para resolver esse problema. Com a API "CSS Anchor Positioning", você pode vincular elementos a outros e reposicionar os elementos para que eles não sejam 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 usar 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á conhece o que o pop-up tem a oferecer e como, vamos nos aprofundar em alguns exemplos.
Notificações
Esta demonstração mostra uma notificação "Copiar para a área de transferência".
- Usa
[popover=manual]
- Em ação, mostre o pop-up com
showPopover
. - Após um tempo limite de
2000ms
, oculte-o comhidePopover
.
Avisos
Esta demonstração usa a camada superior para mostrar notificações de estilo de aviso.
- Um pop-up com o tipo
manual
atua como o contêiner. - As novas notificações são anexadas ao pop-up e ele é mostrado.
- Elas são removidas com a API de animações da Web ao ser clicado e removidas do DOM.
- Se não houver avisos para mostrar, o pop-up 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
autofocus
no primeiro link de cada menu suspenso para navegar com o teclado. - Ele é um candidato perfeito para a API de ancoragem CSS. No entanto, para esta demonstração, você pode usar um pouco 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));
Como esta demonstração usa autofocus
, ela precisa ser aberta na visualização em tela cheia para navegação com o teclado.
Pop-up de mídia
Esta demonstração mostra como você pode abrir a mídia.
- Usa
[popover=auto]
para dispensar a luz. - O JavaScript detecta o evento
play
do vídeo e o exibe. - O evento
popoverhide
de pop-ups pausa o vídeo.
Popovers no estilo Wiki
Esta demonstração mostra como criar dicas de conteúdo inline que contêm mídia.
- Usa
[popover=auto]
A exibição de um deles oculta os outros, porque eles não são ancestrais. - Mostrado em
pointerenter
com JavaScript. - Outro candidato perfeito para a API CSS Anchoring.
Gaveta de navegação
Esta demonstração cria uma gaveta de navegação usando um pop-up.
- Usa
[popover=auto]
para dispensar a luz. - Usa
autofocus
para focar o primeiro item de navegação.
Gerenciamento de panos de fundo
Esta demonstração mostra como gerenciar panos de fundo para vários popovers em que você quer que apenas uma ::backdrop
fique visível.
- Use o JavaScript para manter uma lista dos pop-ups que estão visíveis.
- Aplique um nome de classe ao pop-up mais baixo na camada de cima.
Pop-up de cursor personalizado
Esta demonstração mostra como usar popover
para promover um canvas
à camada superior e usá-lo para mostrar um cursor personalizado.
- Promova
canvas
para a camada superior comshowPopover
e[popover=manual]
. - Quando outros pop-ups forem abertos, oculte e mostre o pop-up
canvas
para garantir que ele esteja na parte de cima.
Popover da Actionsheet
Esta demonstração mostra como usar um pop-up como uma action sheet.
- O pop-up mostrado por padrão substitui
display
. - A Actionsheet é aberta com o gatilho do pop-up.
- Quando o pop-up é mostrado, ele é promovido para a camada superior e traduzido para a visualização.
- A dispensa leve pode ser usada para retornar.
Pop-up ativado pelo teclado
Esta demonstração mostra como usar o pop-up para a interface no estilo da paleta de comandos.
- Use cmd + j para mostrar o pop-up.
- O
input
está em foco comautofocus
. - A caixa de combinação é uma segunda
popover
posicionada abaixo da entrada principal. - A dispensa leve fecha a paleta se o menu suspenso não estiver presente.
- Outro candidato para a API Anchoring
Pop-up programado
Esta demonstração mostra um popover de inatividade após quatro segundos. Um padrão de interface usado com frequência em apps que armazenam informações seguras sobre um usuário para mostrar um modal de encerramento de sessão.
- Use JavaScript para mostrar o pop-up após um período de inatividade.
- Na exibição pop-over, redefina o timer.
Protetor de tela
Assim como na demonstração anterior, você pode adicionar um toque de fantasia ao seu site e um protetor de tela.
- Use JavaScript para mostrar o pop-up após um período de inatividade.
- Dispensar luz para ocultar e redefinir o timer.
Seguimento do cursor
Esta demonstração mostra como fazer com que um pop-over siga um cursor de entrada.
- Mostre o pop-up com base na seleção, no evento de tecla ou na entrada de caracteres especiais.
- Use o JavaScript para atualizar a posição do pop-up com propriedades personalizadas com escopo.
- Esse padrão exige uma reflexão cuidadosa sobre o conteúdo mostrado e a acessibilidade.
- Ele é usado com frequência na interface de edição de texto e em apps em que é possível adicionar tags.
Menu do botão de ação flutuante
Esta demonstração mostra como usar o pop-up para implementar um menu de botão de ação flutuante sem JavaScript.
- Promova um pop-up do tipo
manual
com o métodoshowPopover
. Esse é o botão principal. - O menu é outro pop-up que é o destino do botão principal.
- O menu é aberto com
popovertoggletarget
. - Use
autofocus
para focar o primeiro item de menu em exibição. - "Dispensar luz" fecha o menu.
- A torção do ícone usa
:has()
. Leia mais sobre:has()
neste artigo.
Pronto!
Essa é uma introdução ao pop-up, que será lançado em breve como parte da iniciativa de interface aberta. Se usado de forma sensata, será uma adição fantástica para a plataforma da Web.
Confira a opção Abrir interface. O explicador de pop-up é atualizado conforme a API evolui. Esta é a coleção de todas as demonstrações.
Agradecemos sua visita.
Foto de Madison Oren no Unsplash