Overlay com cor sobre imagem no CSS: pseudo-element ou element? Analisando os métodos
Tags: css, html
Published: 2019-10-27 21:07:56

Como fazer um ***overlay sobre uma imagem, mas por baixo de um texto, e que altere a opacidade no hover***? Talvez com [pseudo-elements](https://developer.mozilla.org/pt-BR/docs/Web/CSS/Pseudo-elementos), se o HTML já estiver montado, sendo necessário apenas alterar o CSS ou usando elementos básicos.

A primeira vista, usar :pseudo-elements me parece mais prático, já que preciso mexer apenas no CSS do projeto, evitando qualquer modificação no HTML. Mas sabemos que é possível obter o mesmo resultado usando [elementos básicos](https://developer.mozilla.org/pt-BR/docs/Web/HTML/Element), e com o mesmo (quase o mesmo) CSS obter visualmente o mesmo resultado.

Mira:
![resultado final](https://media.giphy.com/media/dCRfG9DMrN8O4JcnIU/giphy.gif)


Vale ressaltar que esse post não faz um comparativo de performance *(mas poderia)*, tão pouco irá apontar a melhor maneira ou a correta de resolver esse problema. Esse comparativo é apenas para fins didáticos.

---

## Ponto de partida
A partir de dois _cards_ idênticos, onde o _card_ recebe uma imagem de fundo e comporta apenas duas _[div](https://developer.mozilla.org/pt-BR/docs/Web/HTML/Element/div)_, sendo respectivamente título e subtitulo.
*O texto no rodapé do card com a cor branca é apenas para ficar elegante* :)

{% codepen https://codepen.io/quilamcz/pen/ExxXGXx ?default-tab=html,result %}

Veremos então como fazer um ***overlay sobre uma imagem, mas por baixo de um texto, e que altere a opacidade no hover...***

---

## Pseudo-element
O pseudo-element [:before](https://developer.mozilla.org/pt-BR/docs/Web/CSS/::before) e [:after](https://developer.mozilla.org/pt-BR/docs/Web/CSS/::after) para ser visível é necessário especificar um conteúdo, sendo essa a dificuldade mais corriqueira, já que é bem fácil esquece-lo.

Nesse caso, como não precisamos de conteúdo, adicionamos uma _string_ vazia apenas para o mesmo ser inserido no DOM:
```css
content: '';
```

Para estilizar e posicionar afim de obter o overlay, é necessário aplicar o pseudo-element no próprio card, além de alterar a posição do mesmo para *relative*, dessa maneira:

Acrescentei uma classe modificadora no primeiro card chamada **card--pseudo-elements**, para diferencia-lo do padrão.

```css
.card--pseudo-elements {
  position: relative;
}

.card--pseudo-elements:before {
  /* requerido */
  content: '';

  /* estilo de preto quase transparente */
  background-color: black;
  opacity: 0.5;

  /* posiciona nos limites do elemento pai */
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;

  /* suavização básica */
  transition: 200ms all;
}
```

Feito isso, para fazer o efeito de escurecer o overlay, é necessário apenas aplicar o hover no card, e manipular o pseudo-element, assim:

```css
.card--pseudo-elements:hover:before {
  opacity: 1;
}
```

Estaria pronto se não fosse por um problema: o overlay cobre todo o conteúdo do card. Isso ocorre por quê *(de modo resumido)* o elemento com posição absoluta fica numa camada superior [(z-index)](https://www.devmedia.com.br/css-z-index-entendendo-sobre-o-eixo-z-na-web/28698).

![aplicando :pseudo-element](https://media.giphy.com/media/Z9u4gq0eVwBQ9x9fhz/giphy.gif)

Contornamos isso apenas definindo o z-index dos elementos, dentro do card:
```css
.card--pseudo-elements .card__title,
.card--pseudo-elements .card__subtitle {
  z-index: 1;
}
```

*Nesse estágio você pode ter se questionado ou tentado aplicar um z-index negativo para o seu :pseudo-element, afim de empurra-lo para trás do texto, mas isso não funciona. Veja mais sobre [z-index](https://www.google.com/search?q=z-index) para entender melhor.*

O resultado final com :pseudo-elements você confere abaixo:
{% codepen https://codepen.io/quilamcz/pen/MWWoxeE %}

---

## Elemento
Neste método, precisamos acrescentar um **elemento** no HTML. Além da classe módificadora *card--element* para diferenciar do outro método. O código fica assim:

```html
<div class="card">
  <div class="card__title">
    Maceió
  </div>
  <div class="card__subtitle">
    Alagoas
  </div>
  <span class="card__overlay">
    <!-- div overlay vazia -->
  </span>
  </div>
```
_Você pode inserir no início ou no final do card, **pessoalmente** prefiro inserir no final e como span visando reduzir o risco de interferência no layout atual._

No CSS o código é quase idêntico, mudando apenas os seletores, e a não obrigatoriedade do content. Veja:
```css
.card--element .card__overlay {
  background-color: black;
  opacity: 0.5;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  transition: 200ms all;
}

/* aplicando o hover */
.card--element:hover .card__overlay {
  opacity: 0.9;
}
```

Aqui encontramos o mesmo problema do :pseudo-element, o overlay sobrepõe todo o conteúdo do card, sendo assim a solução é a mesma:

```css
.card--element .card__title,
.card--element .card__subtitle {
  z-index: 1;
}
```

Abaixo você confere o resultado final com elemento básico:
{% codepen https://codepen.io/quilamcz/pen/YzzQgOP %}

## Conclusão
*Nota-se que a diferença entre os métodos é mínima, como o seletor diferente para o CSS e, de modo geral, uma linha a mais de código para ambos.*

Se com o :pseudo-element é necessário adicionar o content à sua regra de estilo e dispensa a marcação HTML, com elemento básico não precisamos do content, mas é necessário adicionar o elemento ao HTML. **O estilo e posicionamento do overlay é feito exatamente da mesma maneira.**

Abaixo deixo o resultado final com os dois métodos:
{% codepen https://codepen.io/quilamcz/pen/ExxXOme %}

---
**Não esqueça de comentar com suas dúvidas e/ou sugestões. Obrigado.**
*Talvez continue com a parte 2, abordando acessibilidade e performance.*