Glassmorphism com CSS

(É esperado que esteja rodando num navegador novo. Para ver os exemplos use a feature do browser “Ver código fonte” como um bom old schooler :) )

Estava vendo um vídeo de como fazer Glassmorphism no Photoshop e pensei: dá para fazer com CSS?

Sim, dá:

Como funciona?

Há várias níveis diferentes de Glassmorphism. Imagem via The Nielsen Norman Group

O mais básico consiste em apenas um blur no fundo, que é trivial de se implementar usando backdrop-filter: blur. A dificuldade mesmo fica na borda.

Se for possível, a solução mais fácil é usar esse Glassmorphism Generator:

No meu caso, gerou o seguinte CSS:

1
2
3
4
5
6
7
8
.via-generator {
  background: rgba( 255, 255, 255, 0.25 );
  box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 );
  backdrop-filter: blur( 1.5px );
  -webkit-backdrop-filter: blur( 1.5px );
  border-radius: 10px;
  border: 1px solid rgba( 255, 255, 255, 0.18 );
}

Como podemos perceber, há 2 problemas:

O primeiro é essa box-shadow que tá mais pro azulado. É facilmente corrigível só mudando a cor para algo mais para o lado do preto. Tipo 0 8px 32px 0 rgba(22, 25, 57, 0.37).

O segundo é referente a borda. Ela é uniforme, então não dá aquela impressão de profundidade.

Aí que mora o perigo.

Primeiro, precisamos fazer um gradiente com borda. Que é possível usando border-image.

Porém nesse caso, border-radius não funciona. Ou melhor, funciona, mas ignora que deveria ser em volta da imagem.

Uma técnica comum para este caso é adicionar uma borda falsa usando background-image.

Obviamente fica um lixo, porque requer que o centro seja uma cor sólida (o primeiro linear-gradient).

Mas estamos no caminho certo. A ideia é ter um elemento que crie a borda usando background-image e gradiente. Porém para tal, será um elemento INTEIRO com gradiente. Então precisamos de outro elemento que DESFAÇA esse gradiente.

Parece complicado (e é), mas podemos fazer isso usando CSS Mask.

CSS Mask

Só ilustrando, a mask funciona similar ao Photoshop e outras ferramentas. Basicamente só mostra o que está dentro dessa máscara.

Por exemplo:

Aqui ainda estamos usando aquele mesmo retângulo de vidro, mas aplicando uma máscara com essa forma estranha (btw gerada usando SVG Shape Generator).

Também podemos aplicar contra o próprio background:

Mas acho que um dos efeitos mais legais é usar contra uma máscara com gradiente:

BTW a sintaxe do linear gradiente é um pouco enjoada. Então vou deixar mais um exemplo:

1
2
3
.mask-4 {
  mask-image: linear-gradient(to top, transparent 5%, black 50%);
}

Isso diz que:

  • o linear gradiente começa de baixo para cima
  • só inicia a transparência a partir dos 5% iniciais da imagem (ie. joga fora os 5% iniciais)
  • na metade final da imagem ela é preta, aka a máscara está full

Enfim, esse caso do gradiente é útil para ter imagens E um texto embaixo, tipo um título ou algo parecido.

Mas voltando ao problema original, vamos criar, dentro da div glass-box uma div wrap. Essa div wrap também tem um pseudo elemento com um gradiente de background que será a borda. Aí o pulo do gato: usamos mask com mask-composite: exclude que funciona com um xor, para EXCLUIR tudo que não for a falsa borda. Para isso, usamos duas “layers” na máscara: uma referente ao content-box, e outra referente a tudo.

Para ilustração (não é o caso real), imagine que tem 2 divs: uma vermelha, ligeiramente maior, e outra verde. E onde há overlap, as cores se misturam, então onde está roxo é porque tem verde + vermelho:

Agora adaptando, o purple é removido, já que está na máscara. E a borda vermelha agora é um linear-gradient.

Final

Ficará assim:

Lembrando que para ver os códigos clique em Visualizar Código fonte no seu browser :)

Referências