Crie efeitos de imagem responsivos com gradientes CSS e proporção
Publicados: 2022-03-10aspect-ratio
recém-suportada em combinação com object-fit
fornece um remédio para essa dor de cabeça do passado! Vamos aprender a usar essas propriedades, além de criar um efeito de imagem gradiente responsivo para um toque extra.Para nos prepararmos para nossos futuros efeitos de imagem, vamos configurar um componente de cartão que tem uma imagem grande na parte superior seguida por um título e uma descrição. O problema comum com essa configuração é que nem sempre podemos ter controle perfeito sobre o que é a imagem e, mais importante, sobre nosso layout, quais são suas dimensões . E embora isso possa ser resolvido cortando com antecedência, ainda podemos encontrar problemas devido a contêineres de tamanho responsivo. Uma consequência são as posições desiguais do conteúdo do cartão que realmente se destaca quando você apresenta uma fileira de cartões.
Outra solução anterior além do recorte pode ter sido trocar de uma img
inline para uma div
em branco que existia apenas para apresentar a imagem via background-image
. Eu mesmo implementei essa solução muitas vezes no passado. Uma vantagem que isso tem é usar um truque mais antigo para proporção de aspecto que usa um elemento de altura zero e define um valor padding-bottom
. Definir um valor de preenchimento como uma porcentagem resulta em um valor calculado final relativo à largura do elemento. Você também pode ter usado essa ideia para manter uma proporção de 16:9 para incorporações de vídeo, caso em que o valor de preenchimento é encontrado com a fórmula: 9/16 = 0.5625 * 100% = 56.26%
. Mas vamos explorar duas propriedades CSS modernas que não envolvem matemática extra, nos dão mais flexibilidade e também permitem manter a semântica fornecida usando um img
real em vez de um div
vazio.
Primeiro, vamos definir a semântica do HTML, incluindo o uso de uma lista não ordenada como recipiente dos cartões:
<ul class="card-wrapper"> <li class="card"> <img src="" alt=""> <h3>A Super Wonderful Headline</h3> <p>Lorem ipsum sit dolor amit</p> </li> <!-- additional cards --> </ul>
Em seguida, criaremos um conjunto mínimo de estilos de linha de base para o componente .card
. Definiremos alguns estilos visuais básicos para o cartão em si, uma atualização rápida para o título h3
esperado e, em seguida, estilos essenciais para começar a estilizar a imagem do cartão.
.card { background-color: #fff; border-radius: 0.5rem; box-shadow: 0.05rem 0.1rem 0.3rem -0.03rem rgba(0, 0, 0, 0.45); padding-bottom: 1rem; } .card > :last-child { margin-bottom: 0; } .card h3 { margin-top: 1rem; font-size: 1.25rem; } img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; } img ~ * { margin-left: 1rem; margin-right: 1rem; }
A última regra usa o combinador irmão geral para adicionar uma margem horizontal a qualquer elemento que segue a img
, pois queremos que a imagem em si fique alinhada com os lados do cartão.
E nosso progresso até agora nos leva à seguinte aparência de cartão:
Por fim, criaremos os estilos .card-wrapper
para um layout responsivo rápido usando grade CSS. Isso também removerá os estilos de lista padrão.
.card-wrapper { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(30ch, 1fr)); grid-gap: 1.5rem; }
Nota : Se esta técnica de grade não for familiar para você, revise a explicação no meu tutorial sobre soluções modernas para a grade de 12 colunas.
Com isso aplicado e com todos os cartões contendo uma imagem com um caminho de origem válido, nossos estilos .card-wrapper
nos dão o seguinte layout:
Conforme demonstrado na imagem de visualização, esses estilos de linha de base não são suficientes para conter adequadamente as imagens devido às suas dimensões naturais variadas. Precisamos de um método para restringir essas imagens de maneira uniforme e consistente.
Ativar tamanhos de imagem uniformes com object-fit
Como observado anteriormente, você pode ter feito anteriormente uma atualização neste cenário para alterar as imagens a serem adicionadas por meio background-image
e usar background-size: cover
para lidar bem com o redimensionamento da imagem. Ou você pode ter tentado impor o corte antes do tempo (ainda uma meta válida, pois qualquer redução no tamanho da imagem melhorará o desempenho!).
Agora, temos disponível a propriedade object-fit
que permite que uma tag img
atue como o contêiner da imagem. E também vem com um valor de cover
que resulta em um efeito semelhante à solução de imagem de fundo, mas com o bônus de manter a semântica de uma imagem em linha. Vamos aplicá-lo e ver como ele funciona.
Precisamos emparelhá-lo com uma dimensão de height
para obter orientação extra sobre como queremos que o contêiner de imagem se comporte (lembre-se de que já adicionamos width: 100%
). E vamos usar a função max()
para selecionar 10rem
ou 30vh
dependendo de qual for maior em um determinado contexto, o que evita que a altura da imagem diminua muito em viewports menores ou quando o usuário tiver definido um zoom grande.
img { /* ...existing styles */ object-fit: cover; height: max(10rem, 30vh); }
Dica Bônus de Acessibilidade : Você deve sempre testar seus layouts com zoom de 200% e 400% na área de trabalho. Embora não haja atualmente uma consulta de mídia de zoom
, funções como max()
podem ajudar a resolver problemas de layout. Outro contexto em que esta técnica é útil é o espaçamento entre elementos.
Com esta atualização, definitivamente melhoramos as coisas, e o resultado visual é como se usássemos a técnica de imagem de fundo mais antiga:
Dimensionamento de imagem consistente e responsivo com aspect-ratio
Ao usar object-fit
por si só, uma desvantagem é que ainda precisamos definir algumas dicas de dimensão.
Uma propriedade futura (atualmente disponível nos navegadores Chromium) chamada aspect-ratio
aumentará nossa capacidade de dimensionar imagens de forma consistente.
Usando esta propriedade, podemos definir uma proporção para redimensionar a imagem em vez de definir dimensões explícitas. Continuaremos a usá-lo em combinação com object-fit
para garantir que essas dimensões afetem apenas a imagem como um contêiner, caso contrário, a imagem poderá parecer distorcida.
Aqui está nossa regra de imagem atualizada completa:
img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; object-fit: cover; aspect-ratio: 4/3; }
Vamos começar com uma proporção de imagem de 4 ⁄ 3 para nosso contexto de cartão, mas você pode escolher qualquer proporção. Por exemplo, 1 ⁄ 1 para um quadrado ou 16 ⁄ 9 para incorporações de vídeo padrão.
Aqui estão os cartões atualizados, embora provavelmente seja difícil notar a diferença visual nesta instância em particular, já que a proporção de aspecto coincide com a aparência que alcançamos definindo a height
apenas para o ajuste do object-fit
.
Definir uma `proporção de aspecto` resulta na manutenção da proporção à medida que os elementos aumentam ou diminuem, enquanto ao definir apenas `ajuste ao objeto` e `altura` a proporção da imagem estará constantemente em fluxo à medida que as dimensões do contêiner mudam.
“
Adicionando efeitos responsivos com gradientes e funções CSS
OK, agora que sabemos como configurar imagens de tamanho consistente, vamos nos divertir com elas adicionando um efeito gradiente!
Nosso objetivo com esse efeito é fazer parecer que a imagem está desaparecendo no conteúdo do cartão. Você pode ficar tentado a envolver a imagem em seu próprio contêiner para adicionar o gradiente, mas graças ao trabalho que já fizemos no dimensionamento da imagem, podemos descobrir como fazer isso com segurança no .card
principal.
O primeiro passo é definir um gradiente . Vamos usar uma propriedade personalizada CSS para adicionar as cores de gradiente para permitir a troca fácil do efeito de gradiente, começando com azul para rosa. A última cor do gradiente sempre será branca para manter a transição para o plano de fundo do conteúdo do cartão e criar a borda “emplumada”.
.card { --card-gradient: #5E9AD9, #E271AD; background-image: linear-gradient( var(--card-gradient), white max(9.5rem, 27vh) ); /* ...existing styles */ }
Mas espere - isso é uma função CSS max()
? Em um gradiente? Sim, é possível, e é a mágica que torna esse gradiente eficaz de forma responsiva!
No entanto, se eu adicionasse uma captura de tela, ainda não veríamos o gradiente tendo qualquer efeito na imagem. Para isso, precisamos trazer a propriedade mix-blend-mode
e, neste cenário, usaremos o valor de overlay
:
img { /* ...existing styles */ mix-blend-mode: overlay; }
A propriedade mix-blend-mode
é semelhante à aplicação dos estilos de mesclagem de camadas disponíveis em softwares de manipulação de fotos como o Photoshop. E o valor de overlay
terá o efeito de permitir que os tons médios da imagem se misturem com o gradiente por trás dela, levando ao seguinte resultado:
Agora, neste ponto, estamos contando apenas com o valor aspect-ratio
para redimensionar a imagem. E se redimensionarmos o contêiner e fizermos com que o layout do cartão reflua, a alteração da altura da imagem causará inconsistências no local onde o gradiente desbota para branco.
Portanto, adicionaremos também uma propriedade max-height
que também usa a função max()
e contém valores ligeiramente maiores que os do gradiente. O comportamento resultante é que o gradiente (quase sempre) se alinha corretamente com a parte inferior da imagem.
img { /* ...existing styles */ max-height: max(10rem, 30vh); }
É importante notar que adicionar um `max-height` altera o comportamento do `aspect-ratio`. Em vez de sempre usar a proporção exata, ela será usada apenas quando houver espaço suficiente, dada a nova restrição extra da `max-height`.
“
No entanto, aspect-ratio
ainda continuará a garantir que as imagens sejam redimensionadas de forma consistente, como foi o benefício em relação apenas object-fit
. Tente comentar a aspect-ratio
na demonstração final do CodePen para ver a diferença que está fazendo nos tamanhos de contêiner.
Como nosso objetivo original era permitir dimensões de imagem consistentemente responsivas , ainda atingimos o objetivo. Para seu próprio caso de uso, talvez seja necessário mexer nos valores de proporção e altura para obter o efeito desejado.
Alternativo: mix-blend-mode
e adicionando um filtro
Usar overlay
como o valor mix-blend-mode
foi a melhor escolha para o efeito fade-to-white que estávamos procurando, mas vamos tentar uma opção alternativa para um efeito mais dramático.
Vamos atualizar nossa solução para adicionar uma propriedade customizada CSS para o valor mix-blend-mode
e também atualizar os valores de cor para o gradiente:
.card { --card-gradient: tomato, orange; --card-blend-mode: multiply; } img { /* ...existing styles */ mix-blend-mode: var(--card-blend-mode); }
O valor de multiply
tem um efeito de escurecimento nos tons médios, mas mantém o branco e o preto como estão, resultando na seguinte aparência:
Embora tenhamos perdido o fade e agora tenhamos uma borda dura na parte inferior da imagem, a parte branca do nosso gradiente ainda é importante para garantir que o gradiente termine antes do conteúdo do cartão.
Uma modificação adicional que podemos adicionar é o uso de filter
e, em particular, usar a função grayscale()
para remover as cores da imagem e, portanto, fazer com que o gradiente seja a única fonte de coloração da imagem.
img { /* ...existing styles */ filter: grayscale(100); }
O uso do valor de grayscale(100)
resulta na remoção completa das cores naturais da imagem e na transformação em preto e branco. Aqui está a atualização para comparação com a captura de tela anterior de seu efeito ao usar nosso gradiente laranja com multiply
:
Use aspect-ratio
como um aprimoramento progressivo
Como mencionado anteriormente, atualmente aspect-ratio
é suportada apenas na versão mais recente dos navegadores Chromium (Chrome e Edge). No entanto, todos os navegadores suportam object-fit
e isso, juntamente com nossas restrições de height
, resulta em um resultado menos ideal, mas ainda aceitável, visto aqui para o Safari:
Sem o funcionamento da aspect-ratio
, o resultado aqui é que, em última análise, a altura da imagem é limitada, mas as dimensões naturais de cada imagem ainda levam a alguma variação entre as alturas da imagem do cartão. Você pode querer mudar para adicionar um max-height
ou fazer uso da função max()
novamente para ajudar a tornar um max-height
mais responsivo em vários tamanhos de cartão.
Estendendo os efeitos de gradiente
Como definimos as paradas de cor do gradiente como uma propriedade personalizada do CSS, temos acesso pronto para alterá-las em diferentes contextos. Por exemplo, podemos alterar o gradiente para apresentar mais fortemente uma das cores se o cartão estiver em foco ou tiver um de seus filhos em foco.
Primeiro, atualizaremos cada cartão h3
para conter um link, como:
<h3><a href="">A Super Wonderful Headline</a></h3>
Em seguida, podemos usar um de nossos seletores mais recentes disponíveis — :focus-within
— para alterar o gradiente do cartão quando o link estiver em foco. Para uma cobertura extra de possíveis interações, combinaremos isso com :hover
. E reutilizaremos nossa ideia max()
para atribuir uma única cor para assumir a cobertura da parte da imagem do cartão. A desvantagem desse efeito em particular é que as paradas de gradiente e as mudanças de cor não são animaveis de maneira confiável - mas serão em breve graças ao CSS Houdini.
Para atualizar a cor e adicionar a nova parada de cor, precisamos apenas reatribuir o valor de --card-gradient
dentro desta nova regra:
.card:focus-within, .card:hover { --card-gradient: #24a9d5 max(8.5rem, 20vh); }
Nossos valores max()
são menores que o original em uso para white
para manter a borda difusa. Se usássemos os mesmos valores, ele encontraria o white
e criaria uma separação claramente reta.
Ao criar esta demonstração, originalmente tentei um efeito que usava transform
com scale
para um efeito de zoom. Mas descobri que devido à aplicação mix-blend-mode
, o navegador não redesenhava consistentemente a imagem, o que causava uma tremulação desagradável. Sempre haverá desvantagens em solicitar que o navegador execute efeitos e animações somente CSS e, embora seja muito legal o que podemos fazer, é sempre melhor verificar o impacto de seus efeitos no desempenho .
Divirta-se experimentando!
O CSS moderno nos deu algumas ferramentas incríveis para atualizar nossos kits de ferramentas de web design, sendo a aspect-ratio
a mais recente adição. Então vá em frente e experimente com object-fit
, aspect-ratio
e adicione funções como max()
em seus gradientes para alguns efeitos responsivos divertidos! Apenas certifique-se de verificar as coisas em vários navegadores (por enquanto!) e em várias janelas de visualização e tamanhos de contêiner.
Aqui está o CodePen, incluindo os recursos e efeitos que analisamos hoje:
Procurando mais? Certifique-se de verificar nosso Guia CSS aqui em Smashing →