Modais e :target
Um modal é um elemento exibido acima de outros elementos em uma página da web, impedindo a interação com o restante da página até que o modal seja descartado. Os modais geralmente são usados para exibir informações pertinentes ao usuário ou chamar a atenção para uma ação necessária.
Como os modais exigem a interação do usuário para serem exibidos ou dispensados, eles geralmente são criados usando JavaScript. Existe até uma API JavaScript integrada especificamente para lidar com modais sem a necessidade de escrever nenhum código JavaScript.
Neste tutorial, entretanto, veremos como construir modais sem o uso de nenhum JavaScript. Em vez disso, construiremos nosso componente modal totalmente funcional usando apenas CSS.
A pseudoclasse CSS :target representa um elemento único (o elemento alvo) com um id correspondente ao fragmento da URL. -MDN
1. Construindo os recursos modais
Estaremos construindo um modal que
- abre quando um botão é clicado e
- fecha quando a área fora do modal ou o botão Fechar é clicado.
Então precisaremos dos seguintes elementos:
- Um elemento para controlar a abertura do modal
- Um elemento de contêiner modal para armazenar o conteúdo modal
- Uma sobreposição modal para lidar quando alguém clica fora do elemento modal
- Um botão Fechar para lidar com o fechamento do elemento modal
Abrindo o Modal
Para lidar com a abertura da classe modal, definiremos o ID modal no fragmento de URL usando um elemento âncora. Os elementos âncora podem vincular-se a uma determinada seção de uma página da web usando um # e, em seguida, o ID do elemento.
Quando a tag âncora define um fragmento de URL, o URL da página fica assim: https://tutsplus.com/authors/jemima-abu#tutorials
Ao usar fragmentos, o histórico de localização da janela também é alterado. Isso significa que se um usuário estiver ligado https://tutsplus.com/authors/jemima-abu#tutorials e eles pressionarem o botão Voltar no navegador, serão redirecionados para https://tutsplus.com/authors/jemima-abu que é a mesma página da web.
O fragmento é o que o CSS :target
a pseudoclasse usa para aplicar estilo ao elemento com o ID correspondente. Depois que o ID modal for definido como o fragmento de URL, podemos usar :target
para exibir o elemento modal.
Fechando o Modal
Pela mesma lógica, se alterarmos o id no fragmento de URL para outra coisa, o :target
o estilo não será mais aplicado ao modal. Definiremos nosso botão Fechar e elementos de sobreposição modal como tags âncora que redefinem o fragmento de URL, removendo o estilo de destino do modal e ocultando-o no processo.
Para fechar o modal, podemos definir o href de nossa sobreposição e fechar os elementos âncora do botão para um fragmento em branco, por exemplo href=”#”defina-o como outro ID de seção ou use um ID que não exista #em branco.
A vantagem de usar um fragmento em branco é que o URL não conterá texto desnecessário, mas fará com que a página role para o topo quando o modal for fechado.
A vantagem de usar outro ID de seção é que podemos decidir qual elemento receberá o foco quando a seção modal for fechada, mas isso pode causar um pequeno salto na página, pois o elemento será rolado para visualização.
A vantagem de usar um ID que não existe é que a página manterá a mesma posição de rolagem de quando o modal é fechado, mas o texto do fragmento será exibido na URL para alguns navegadores e pode parecer um pouco fora do lugar .
Marcação HTML para o Modal
O layout HTML do nosso modal é assim:
1 |
|
2 |
class="modal-btn" href="#modal">Click here to open modal
|
3 |
|
4 |
|
5 |
|
6 |
class="modal-overlay" href="#" tabindex="-1">
|
7 |
|
8 |
|
9 |
title="Close modal" aria-label="Close modal" href="#" class="modal-close">×
|
10 |
|
11 |
|
12 |
|
Usamos os seguintes atributos para lidar com rotulagem acessível para nossos modais:
-
role="dialog"
: Isso informa à tecnologia assistiva que o elemento é um modal -
aria-labelledby="modal-title"
: fornece um título para o contêiner modal, usando o texto no elemento ‘modal-title’ -
aria-label="Close modal"
: fornece um texto descritivo para o botão fechar modal -
tabindex="-1"
: isso evita que a sobreposição modal ganhe foco ao usar um teclado. Isso ocorre porque a sobreposição serve principalmente como uma dica visual e não como uma função necessária.
2. Aplicando estilo ao modal
Depois de configurar nosso layout, podemos aplicar alguns estilos básicos às tags âncora e aos elementos modais. Queremos que a tag âncora se pareça com um botão e que o elemento modal seja colocado em cima de outros elementos com um fundo semitransparente para a sobreposição modal.
1 |
.modal-btn { |
2 |
transition: background 250ms; |
3 |
padding: 16px 24px; |
4 |
border-radius: 4px; |
5 |
background-color: #0f0f0f; |
6 |
color: #fcfcfc; |
7 |
text-transform: uppercase; |
8 |
font-size: 12px; |
9 |
letter-spacing: 0.1em; |
10 |
margin-top: 32px; |
11 |
display: inline-block; |
12 |
text-decoration: none; |
13 |
}
|
14 |
|
15 |
.modal-btn:hover, |
16 |
.modal-btn:focus { |
17 |
background-color: #0f0f0fdd; |
18 |
}
|
19 |
|
20 |
.modal { |
21 |
position: fixed; |
22 |
min-height: 100vh; |
23 |
width: 100%; |
24 |
top: 0; |
25 |
left: 0; |
26 |
display: flex; |
27 |
z-index: 2; |
28 |
}
|
29 |
|
30 |
.modal-overlay { |
31 |
width: 100%; |
32 |
height: 100%; |
33 |
position: absolute; |
34 |
background-color: rgba(0, 0, 0, 0.5); |
35 |
left: 0; |
36 |
}
|
37 |
|
38 |
.modal-content { |
39 |
transition: transform 1s; |
40 |
background: #fff; |
41 |
width: 75%; |
42 |
position: relative; |
43 |
margin: auto; |
44 |
height: 75%; |
45 |
padding: 48px 24px; |
46 |
border-radius: 4px; |
47 |
max-width: 1000px; |
48 |
}
|
49 |
|
50 |
.modal-close { |
51 |
font-size: 36px; |
52 |
text-decoration: none; |
53 |
color: inherit; |
54 |
position: absolute; |
55 |
right: 24px; |
56 |
top: 10px; |
57 |
}
|
Esta é a aparência do nosso modal até agora:
Temos nosso estilo modal para ser exibido por padrão, então só precisamos usar a pseudoclasse CSS :not para ocultar o modal se não for o elemento de destino.
1 |
.modal:not(:target) { |
2 |
display: none; |
3 |
}
|
Isso é tudo que precisamos para exibir e ocultar o elemento modal com base em qual tag âncora foi clicada, mas ainda há mais recursos a serem considerados.
3. Animando Modal com CSS
Como estamos trabalhando com CSS, também podemos aplicar transições e animações para fornecer uma entrada e saída modal mais suave. Este é um exemplo de modal com animação de fade e slide:
O código CSS fica assim:
1 |
.modal:not(:target) { |
2 |
visibility: hidden; |
3 |
transition-delay: 500ms; |
4 |
transition-property: visibility; |
5 |
}
|
6 |
|
7 |
.modal:target .modal-content { |
8 |
transform: translateY(100vh); |
9 |
animation: 500ms ease-in-out slideUp forwards; |
10 |
}
|
11 |
|
12 |
.modal:not(:target) .modal-content { |
13 |
transform: translateY(0); |
14 |
animation: 500ms ease-out slideDown forwards; |
15 |
}
|
16 |
|
17 |
.modal:target .modal-overlay { |
18 |
opacity: 0; |
19 |
animation: 500ms linear fadeIn forwards; |
20 |
}
|
21 |
|
22 |
@keyframes fadeOut { |
23 |
from { |
24 |
opacity: 1; |
25 |
}
|
26 |
|
27 |
to { |
28 |
opacity: 0; |
29 |
}
|
30 |
}
|
31 |
|
32 |
@keyframes fadeIn { |
33 |
from { |
34 |
opacity: 0; |
35 |
}
|
36 |
|
37 |
to { |
38 |
opacity: 1; |
39 |
}
|
40 |
}
|
41 |
|
42 |
@keyframes slideUp { |
43 |
from { |
44 |
transform: translateY(100vh); |
45 |
}
|
46 |
|
47 |
to { |
48 |
transform: translateY(0); |
49 |
}
|
50 |
}
|
51 |
|
52 |
@keyframes slideDown { |
53 |
from { |
54 |
transform: translateY(0); |
55 |
}
|
56 |
|
57 |
to { |
58 |
transform: translateY(100vh); |
59 |
}
|
60 |
}
|
Atrasaremos a transição de visibilidade do modal para permitir que as animações de saída sejam concluídas antes que ele fique completamente oculto.
4. Acessibilidade Modal
Ao construir um elemento modal com qualquer tecnologia, existem alguns requisitos de acessibilidade que precisam ser implementados:
- O modal deve ganhar foco quando aberto e mudar o foco de volta para a página principal quando fechado.
- O conteúdo modal deve ficar visível para leitores de tela, somente quando aberto.
- O modal pode ser fechado com o teclado.
Graças à forma como o fragmento de URL funciona, nosso modal ganha foco automaticamente quando a tag âncora é clicada. Isso ocorre porque os fragmentos de URL rolam para a seção com o ID correspondente, o que também faz com que o foco seja alterado.
Quanto à mudança de foco de volta para a página principal, o foco será colocado de volta na página principal se o href=”#” O atributo é usado no botão Fechar e na sobreposição.
Alternativamente, podemos definir o foco novamente para um elemento específico, como o botão modal, usando o id do elemento. Este método é preferido porque permite ao usuário manter o mesmo estado de foco em que estava antes de abrir o modal.
1 |
id="modal-btn" class="modal-btn" href="#modal">Click here to open modal
|
2 |
|
3 |
|
4 |
class="modal-overlay" href="#modal-btnn">
|
5 |
|
6 |
|
7 |
title="Close modal" aria-label="Close modal" href="#modal-btn" class="modal-close">×
|
8 |
|
9 |
|
10 |
|
Vamos precisar de algum JavaScript
Para o restante dos recursos, entretanto, não podemos aproveitar as vantagens do comportamento nativo, então precisaremos usar algum JavaScript para lidar com os requisitos de acessibilidade.
Escreveremos um script para alternar o aria-hidden
atributo para o modal e também inclui um ouvinte de evento para permitir que o modal seja fechado quando o Escapar tecla é pressionada.
Como estamos usando o fragmento de URL para alternar a exibição modal, também podemos usar o mesmo método em JavaScript com o ouvinte de evento popstate.
Este ouvinte de evento detecta quando o URL da janela é alterado. Então podemos obter o valor do fragmento de URL usando o valor window.location.hash. Esta é a aparência do nosso código JavaScript:
1 |
const modal = document.getElementById('modal') |
2 |
|
3 |
window.addEventListener('popstate', () => { |
4 |
if (window.location.hash === '#modal') { |
5 |
modal.focus(); |
6 |
modal.setAttribute('aria-hidden', false); |
7 |
return
|
8 |
}
|
9 |
|
10 |
modal.setAttribute('aria-hidden', true) |
11 |
});
|
Também usamos o ouvinte de evento keydown para detectar se a tecla Escape está pressionada e o modal está aberto. Se for, fecharemos o modal definindo o valor window.location.hash como vazio
1 |
window.addEventListener('keydown', (e) => { |
2 |
if (e.key == "Escape" && window.location.hash === '#modal') { |
3 |
window.location.hash = "" |
4 |
}
|
5 |
})
|
Este artigo sobre Diálogos modais acessíveis também fornece mais informações sobre como melhorar a acessibilidade dos modais.
Conclusão
E com isso, construímos um componente modal funcional usando CSS (e um pouco de JavaScript para acessibilidade).