O objetivo da iniciativa Open UI é 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. Podemos fazer isso oferecendo melhores APIs e componentes integrados à 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. Em parte, isso se deve à maneira 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 para certas coisas ou informá-los sobre o conteúdo do site, especialmente quando usados com bom gosto.
Muitas vezes, há duas grandes preocupações ao criar popovers:
- Como garantir que ele seja colocado acima do restante do seu conteúdo em um local adequado.
- Como torná-la acessível (fácil de usar para teclado, focalizável e assim por diante).
A API Popover integrada tem diversos objetivos (em inglês), todos com o mesmo objetivo geral de facilitar a criação desse padrão pelos desenvolvedores. Alguns desses objetivos 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 de pop-ups no site da OpenUI.
Compatibilidade com navegadores
Onde você pode usar a API Popover integrada agora? Ela é compatível com o Chrome Canary com os "Recursos experimentais da plataforma da Web" no momento da gravação.
Para ativar essa sinalização, abra o Chrome Canary e acesse chrome://flags
. Depois, ative "Recursos experimentais da plataforma da Web". .
Também existe um teste de origem para desenvolvedores que gostariam de fazer 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.
Verifique o suporte via pop-up com:
const supported = HTMLElement.prototype.hasOwnProperty("popover");
Soluções atuais
O que você pode fazer atualmente para promover seu conteúdo acima de tudo? Se for compatível com seu navegador, você poderá usar o elemento da caixa de diálogo HTML. Use-o em "Modal". forma E isso requer JavaScript para ser usado.
Dialog.showModal();
Há algumas considerações sobre acessibilidade. É recomendável usar a11y-dialog, por exemplo, ao atender usuários do Safari com versões anteriores à 15.4.
Você também pode usar uma das muitas bibliotecas baseadas em pop-up, alerta ou dica por aí. Muitos deles tendem a funcionar de maneira semelhante.
- Adicione um contêiner ao corpo para mostrar pop-ups.
- Defina um estilo para que fique acima de todo o resto.
- Crie um elemento e anexe-o ao contêiner para mostrar um pop-up.
- Oculte-o removendo o elemento pop-over do DOM.
Isso exige uma dependência extra e mais decisões para os desenvolvedores. Ela também exige pesquisa para encontrar uma oferta que forneça tudo o que você precisa. O objetivo da API Popover é atender a muitos cenários, incluindo dicas. O objetivo é abordar todos esses cenários comuns, evitando que os desenvolvedores tenham que tomar outras decisões para se concentrarem 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?
- Você não precisa colocar o elemento pop-over em um contêiner ou qualquer outro item. Ele está oculto por padrão.
- Você não precisa escrever código JavaScript para que eles apareçam. Isso é processado pelo atributo
popovertoggletarget
. - Quando aparece, ela é promovida para a camada superior. Isso significa que ela é promovida acima de
document
na janela de visualização. Você não precisa gerenciarz-index
nem se preocupar com onde seu pop-over está 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 mais informações sobre a camada superior, consulte este artigo.
- Você recebe a opção "Dispensa de luz" prontos para uso. Com isso, queremos dizer que você pode fechar o pop-up com um sinal fechado, como clicar fora do pop-up, navegar para outro elemento usando o teclado ou pressionar a tecla Esc. Abra-o novamente e experimente!
O que mais o pop-up oferece? Vamos conferir o exemplo. Considere esta demonstração com algum conteúdo na página.
Esse botão de ação flutuante tem um posicionamento fixo com um z-index
alto.
.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 superior recebem um pseudoelemento ::backdrop
estilizado. Este exemplo define o estilo ::backdrop
com uma cor de fundo Alfa reduzida e um filtro de pano de fundo, que desfoca o conteúdo subjacente.
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 substituí-lo para mostrar um pop-over. No entanto, isso não o 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-over será posicionado 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ê deseja posicionar o conteúdo dentro do pop-over usando grade CSS ou flexbox, pode ser aconselhável envolvê-lo em um elemento. Caso contrário, será necessário declarar uma regra separada que mude a display
quando o pop-up estiver na camada de cima. Se ela for definida por padrão, ela será mostrada, substituindo display: none
.
[popover]:open {
display: flex;
}
Se você testou essa demonstração, perceberá que o pop-up agora está fazendo a transição para dentro e para fora. Você pode fazer a transição de pop-ups para dentro e para fora usando o pseudosseletor :open
. O pseudosseletor :open
corresponde aos pop-ups que estão sendo mostrados (e, portanto, na camada superior).
Este exemplo usa uma propriedade personalizada para conduzir 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 controlar o tempo. 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ê viu o uso de popovertoggletarget
para mostrar um pop-up. Para isso, usamos a opção "Dispensa leve". No entanto, você também recebe os atributos popovershowtarget
e popoverhidetarget
que podem ser usados. Vamos adicionar um botão a um pop-over que o oculta e mudar o botão 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 diferentes padrões de interação. 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 alvo. Esse comportamento pode ser configurado pelas propriedades do CSS. Essas propriedades de CSS definiriam a janela de tempo para passar o cursor sobre um elemento ao qual um pop-over reage. O comportamento padrão testado apresentou um pop-over após um 0.5s
explícito de :hover
. Em seguida, seria necessário dispensar uma luz ou abrir outro pop-up (mais sobre isso em breve). Isso aconteceu porque a duração para ocultar o pop-over 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);
});
});
A vantagem de definir algo explícito como uma janela ao passar o cursor é que isso garante que a ação do usuário seja intencional (por exemplo, o usuário passa o ponteiro sobre um alvo). Não queremos mostrar o pop-up, a menos que essa seja a intenção da pessoa.
Teste esta demonstração em que você pode passar o cursor sobre o destino 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
Abordamos o comportamento de interação não JavaScript. Mas e o comportamento dos pop-ups como um todo? E se você não quiser a opção "Dispensa leve"? Ou quer aplicar um padrão de singleton aos seus popovers?
A API Popover permite especificar três tipos de pop-over, que diferem em comportamento.
[popover=auto]/[popover]
:
- Suporte de aninhamento. Isso não significa apenas aninhado no DOM. A definição de um popover ancestral é:
- relacionada por posição do DOM (filho).
- relacionados acionando atributos em elementos filhos, como
popovertoggletarget
,popovershowtarget
e assim por diante. - relacionada pelo atributo
anchor
(em API de ancoragem CSS em desenvolvimento).
- Luz dispensada.
- A abertura dispensa outros pop-ups que não são popovers ancestral. Confira a demonstração abaixo que destaca como funciona o aninhamento com popovers ancestrais. Veja como mudar algumas das instâncias de
popoverhidetarget
/popovershowtarget
parapopovertoggletarget
muda o cenário. - Quando a luz dispensa uma, todas as outras na pilha são descartadas, mas apenas aquelas que estão acima dela são descartadas.
[popover=manual]
:
- Não fecha outros pop-ups.
- Nenhuma luz dispensada.
- Exige a dispensa explícita por meio de um elemento acionador ou do JavaScript.
JavaScript API
Quando precisar de mais controle sobre os pop-ups, use 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()
Ouça um pop-up ser exibido:
popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)
Ouça e cancele a exibição de um pop-up:
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 a ocultação de um pop-up:
popoverElement.addEventListener('popoverhide',event => {
event.preventDefault();
console.warn("You aren't allowed to cancel the hiding of a popover");
})
Verifique se um pop-over está na camada superior:
popoverElement.matches(':open')
Isso fornece mais energia para alguns cenários menos comuns. Por exemplo, mostre um pop-up após um período de inatividade.
Esta demonstração tem popovers com pop-ups audíveis, portanto, precisaremos do JavaScript para reproduzir o áudio. Ao clicar, ocultamos o pop-over, reproduzimos o áudio e o exibimos novamente.
Acessibilidade
A acessibilidade está na vanguarda 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
.
No gerenciamento de foco, você pode usar o atributo autofocus para mover o foco para um elemento dentro de um pop-over. É o mesmo que em uma caixa de diálogo, mas a diferença ocorre ao retornar o foco, devido à dispensa da luz. Na maioria dos casos, fechar um pop-over retorna o foco para o elemento anteriormente em foco. No entanto, o foco é movido para um elemento clicado na dispensa de luz, se ele puder receber o foco. Confira a seção sobre gerenciamento de foco na explicação.
Será necessário abrir a versão em tela cheia desta demonstração para ver como ele funciona.
Nesta demonstração, o elemento em foco recebe um contorno verde. Navegue pela interface com o teclado. Observe onde o foco é retornado quando um pop-over é fechado. Você também deve notar que, ao percorrer as guias, o pop-up fechou. Isso é feito por projeto. 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 estiver definida para aparecer acima do acionador, mas o documento for rolado. Essa dica pode ser cortada pela janela de visualização. Existem ofertas atuais de JavaScript para lidar com isso, como a interface flutuante. Eles vão reposicionar a dica para que você impeça que isso aconteça e dependa de uma ordem de posicionamento desejada.
No entanto, queremos que você possa definir isso com seus estilos. Há uma API complementar em desenvolvimento junto com a API Popover para lidar com isso. O módulo "Posicionamento âncora do CSS" A API permitirá que você conecte elementos a outros elementos e faça isso de uma maneira que reposiciona os elementos para que não sejam cortados pela janela de visualização.
Esta demonstração usa a API de ancoragem no estado atual. A posição do barco responde à posição da âncora na janela de visualização.
Veja um snippet do CSS que faz esta 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 as especificações 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 o recurso "Copiar para a área de transferência" notificação.
- Usa
[popover=manual]
- Na 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-over com o tipo
manual
atua como contêiner. - As novas notificações são anexadas ao pop-up, que é exibido.
- 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 ficará oculto.
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 pelo teclado. - Ele é um candidato perfeito para a API de ancoragem CSS. Porém, 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));
Como esta demonstração usa autofocus
, será necessário abri-la na "visualização em tela cheia" para navegação pelo teclado.
Pop-up de mídia
Esta demonstração mostra como abrir mídias.
- Usa
[popover=auto]
para dispensar luzes. - O JavaScript detecta o evento
play
do vídeo e exibe o vídeo. - O evento
popoverhide
de pop-ups pausa o vídeo.
Pop-ups no estilo Wiki
Esta demonstração mostra como você pode criar dicas de conteúdo inline que contêm mídia.
- Usa
[popover=auto]
Mostrar um oculta os outros porque eles não são ancestral. - Exibido em
pointerenter
com JavaScript. - Outro candidato perfeito para a API de ancoragem do CSS.
Gaveta de navegação
Esta demonstração cria uma gaveta de navegação usando um pop-over.
- Usa
[popover=auto]
para dispensar luzes. - 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 JavaScript para manter uma lista dos pop-ups visíveis.
- Aplique um nome de classe ao pop-up mais baixo na camada superior.
Pop-up personalizado do cursor
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 fique na parte de cima.
Pop-up de actionsheet
Esta demonstração mostra como usar um pop-over como uma planilha de ações.
- Mostrar o pop-up por padrão substituindo
display
. - A planilha de ações é aberta com o acionador pop-over.
- Quando o pop-up é exibido, ele é promovido para a camada superior e traduzido para visualização.
- A função de dispensar a luz pode ser usada para fazer o retorno.
Pop-up ativado pelo teclado
Esta demonstração mostra como você pode usar o popover na interface de 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. - Quando a luz dispensa, a paleta é fechada se o menu suspenso não estiver presente.
- Outro candidato à API Anchoring
Pop-up com marcação de tempo
Esta demonstração mostra um pop-up de inatividade após quatro segundos. Um padrão de IU frequentemente usado em apps que contêm informações seguras sobre um usuário para mostrar um modal de logout.
- 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 pouco 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.
Acento circunflexo seguido de acento circunflexo
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 principal 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 exigiria um pensamento cuidadoso em relação ao conteúdo mostrado e à acessibilidade.
- Muitas vezes, ela é vista na interface de edição de texto e em apps em que é possível marcar.
Menu de botão de ação flutuante
Esta demonstração mostra como usar o pop-over para implementar um menu de botão de ação flutuante sem JavaScript.
- Promova um pop-up do tipo
manual
com o métodoshowPopover
. Este é o botão principal. - O menu é outro pop-up que é alvo do botão principal.
- O menu é aberto com
popovertoggletarget
. - Use
autofocus
para focar o primeiro item do menu na exibição. - "Dispensar luz" fecha o menu.
- A rotação de ícone usa
:has()
. Leia mais sobre:has()
neste artigo.
Pronto!
Essa é uma introdução ao pop-up que será lançada como parte da iniciativa Open UI. Se usado de maneira coerente, será uma adição fantástica à plataforma da web.
Confira a opção Abrir interface. A explicação pop-up é atualizada conforme a API evolui. Esta é a coleção de todas as demonstrações.
Valeu por aparecer!
Foto de Madison Oren no Unsplash