Em muitos projetos, é preciso usar o CSS para fazer elementos de cobertura (cobrir um elemento com outro). Veja algumas técnicas possível para alcançar este efeito através de utilitários CSS.
Aqui está algo que é bastante comum de se fazer repetidamente em CSS: “cobrir” completamente um elemento com outro — daí o nome elemento de cobertura. :)
É sempre o mesmo CSS: o primeiro elemento (aquele que precisa ser coberto) tem position: relative
aplicada a ele; o segundo, position: absolute
e é posicionado de forma que todos os quatro lados se alinhem às bordas do primeiro elemento.
O CSS completo se parece com:
1 2 3 4 5 6 7 8 9 10 11 |
.original-element { position: relative; } .covering-element { position: absolute; top: 0; right: 0; bottom: 0; left: 0; } |
Freqüentemente, é um título (title) ou legenda (caption) que precisa cobrir uma imagem; às vezes, com uma cor de fundo translúcida ou gradiente — isso também é chamado de sobreposição. Normalmente, é um filho direto do primeiro elemento que está sendo sobreposto, mas nem sempre.
Às vezes, é necessário sobrepor um pseudo-elemento, talvez até fazer a transição ou animá-lo para um estado de foco. De qualquer maneira, isso é feito com tanta frequência, que é totalmente legítimo criar uma classe de utilitário para ajudar, em vez de escrever as propriedades CSS à mão todas as vezes.
Classes de utilidade (ou utilitárias) para elementos de cobertura
Já citamos algumas vezes classes utilitário aqui no dpw — por exemplo, em nossa live sobre Namespaces CSS. Tratam-se classes que devem ter um e somente um propósito e que podem ser aplicadas em diversos elementos.
Por exemplo, é possível ter uma classe de alinhamento centralizado de texto:
1 2 3 |
.text-center { text-aling: center; } |
Então, sempre que for necessário que um elemento tenha texto centralizado, ao invés de colocar a propriedade e o valor, utiliza-se essa classe — ou, caso a stack do projeto conte com pré-processador de CSS, ela é estendida.
É possível usar classes de utilitários de diversas maneiras, desde as mais insanas (como é feito no Tailwind), até as mais normais, com parcimônia e na medida em que o projeto as demanda.
E uma utilty class não precisa, necessariamente, se ater a somente uma propriedade CSS. Pensando em uma classe de utilitário para elemento sobreposto, esta incluiria várias propriedades.
Por exemplo, um utilitário desta natureza poderia ser algo como:
1 2 3 4 5 6 7 8 |
.overlay { position: absolute; content: ''; top: 0; right: 0; bottom: 0; left: 0; } |
Embora seja muito flexível como um utilitário, usar position: absolute
define a posição do elemento em relação ao ancestral de posição relativa mais próximo, então é preciso lembrar de definir position: relative
ao elemento que se quer cobrir (e garantir que seja um ancestral do elemento de cobertura).
Na maioria dos casos, o elemento que se usa como uma sobreposição é um filho direto ou um pseudo-elemento, portanto, pode fazer sentido criar mais utilitários para aplicar nesses casos, o que significa que não é preciso definir position: relative
.
Qualquer uma dessas 3 classes pode, ao invés disso, ser aplicada ao elemento-pai (ou seja, o elemento original que é preciso cobrir):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
.overlay-child, .overlay-before, .overlay-after { position: relative; } .overlay-child > *, .overlay-before::before, .overlay-after::after { position: absolute; content: ''; top: 0; right: 0; bottom: 0; left: 0; } |
Agora existem 3 utilitários para se aplicar adicionalmente quando é necessário direcionar o elemento que está fazendo a cobertura:
overlay-child
(para posicionar um elemento-filho direto)overlay-before
(para posicionar um pseudo-elemento::before
)overlay-after
(para posicionar um pseudo-elemento::after
)
Um exemplo de quando é possível usar a sobreposição em vez de uma das classes de segmentação de pais é se o elemento de cobertura não for um filho direto, mas um descendente mais abaixo na árvore DOM.
See the Pen Overlay utility classes by Michelle Barker (@michellebarker) on CodePen.
Dica bônus: pegar o elemento-pai mais próximo com JavaScript
Já que estamos no assunto de diferentes técnicas CSS para elementos de cobertura, quando é preciso lidar com componentes complexos e posicionamento absoluto, um position: relative
maroto às vezes pode ser a causa de bugs de layout.
Para debugar, geralmente é necessário encontrar o ancestral em posição relativa mais próximo do elemento em posição absoluta que está causando problemas. Felizmente, é possível usar um pouquinho de JavaScript para ajudar.
Nas DevTools do Chrome e Firefox, digitar $0
no console retornará o elemento atualmente selecionado. Se usar $0.offsetParent
, o ancestral posicionado mais próximo do elemento atualmente selecionado será retornado.
Então, é possível verificar se ele tem position: relative
pelo painel Styles — ou através de JavaScript, com getComputedStyle($0.offsetParent).position
.
CSS Logical Properties
Posicionar um elemento dessa maneira está prestes a se tornar mais conciso com a ajuda da nova propriedade CSS inset
. Parte da especificação CSS Logical Properties, inset
é efetivamente uma abreviação para as propriedades de posicionamento top
, right
, bottom
, left
.
Também há novas propriedades lógicas, que permitem alcançar algo parecido: inset-block-start
, inset-block-end
, inset-inline-start
e inset-inline-end
. Para o modo de escrita (writing mode) padrão da esquerda para a direita (left-to-right), isso será mapeado para top
, bottom
, left
, right
, nessa ordem.
Mas outros modos de escrita (bem como a propriedade direction
) farão com que os valores sejam mapeados de forma diferente. No momento, porém, é preciso apenas da propriedade inset
para o utilitário do elemento de cobertura.
Voltando ao código, ele poderia ser refatorado de:
1 2 3 4 5 6 |
.overlay { top: 0; bottom: 0; right: 0; left: 0; } |
Para:
1 2 3 |
.overlay { inset: 0; } |
Fazendo do código menor e igualmente eficiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.overlay-child, .overlay-before, .overlay-after { position: relative; } .overlay-child > *, .overlay-before::before, .overlay-after::after { position: absolute; content: ''; inset: 0; } |
Suporte cross-browser
No momento da publicação deste artigo, inset
já tem um suporte cross-browser (moderno) bastante amplo, portanto, você já pode começar a pensar em usar a nova propriedade CSS em produção — dependendo do caso, forneça um fallback para navegadores sem suporte.
Método alternativo para elementos de cobertura: CSS Grid
Algo interessante é que não necessariamente é preciso usar posicionamento absoluto: o utilitário CSS para cobertura de elementos também pode ser feito ao usar CSS Grid para cobrir um elemento com outro!
O código (mais enxuto, inclusive) é parecido com:
1 2 3 4 5 6 7 |
.element { display: grid; } .overlay { grid-area: 1 / 1 / -1 / -1; } |
Se a sobreposição for o único elemento filho, não é preciso nem definir a grid area, mas nada impede prevenir para o caso de ser preciso adicionar outros elementos-filho — caso contrário, seria arriscado acontecer algum efeito colateral quando os itens são colocados automaticamente na grid.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.overlay-child, .overlay-before, .overlay-after { display: grid; } .overlay, .overlay-child > *, .overlay-before::before, .overlay-after::after { content: ''; grid-area: 1 / 1 / -1 / -1; } |
Eis um demo com a técnica usando CSS Grid:
See the Pen Overlay utility classes with Grid by Michelle Barker (@michellebarker) on CodePen.
Conclusão
Usar utilitário CSS é uma técnica já conhecida. Existem maneiras de otimizar seu entendimento e eficiência, mas sua intenção de uso fica bem clara.
Quando se deparar com casos de CSS em que seja necessário repetir muitas vezes as mesmas propriedades, é interessante ponderar a respeito do uso de utilitários CSS.
Um dos casos, abordados no artigo, é sobre criar utilitários que tratam elementos de cobertura usando diferentes técnicas. A depender do projeto, pode ser uma bela adição às folhas de estilo.