Um assunto que está evoluindo bastante em web design responsivo é como lidar com imagens responsivas de maneira apropriada e eficiente — assunto inclusive abordado anteriormente aqui no blog. Neste artigo, conheça mais sobre srcset e sizes, seus novos amigos na hora de planejar e implementar imagens responsivas em web design responsivo.

O que há de errado com Media Queries?

Digamos que você está fazendo um site entre 23 de fevereiro de 1993 e 25 de maio de 2010. Trabalhar com imagens é simples:

  1. Olhe para seu layout de largura fixa
  2. Meça exatamente quantos pixels da tela de seus visitantes você vai invariavelmente preencher com a imagem
  3. Abra o Photoshop
  4. Salve “para a web” aquela imagem, naquele exato tamanho
  5. Crie sua tag img
  6. Sirva-se de uma boa cerveja (ou talvez abra uma lata de ervilhas frescas) para comemorar mais um trabalho bem feito!

Imagens responsivas com srcset e sizes: medindo o buraco

Imagens responsivas com srcset e sizes: medindo a 'caixa'

Imagens responsivas com srcset e sizes: martelando

Imagens responsivas com srcset e sizes: ervilhas!

Embora alguns sábios profetas tenham, ocasionalmente, falado uma ou outra verdade sobre os problemas inerentes a esta maneira de proceder, a verdade é que esta tem sido a abordagem padrão de web designers por 20 anos.

Mas estes são outros tempos.

Alguns anos atrás, Ethan Marcotte publicou um artigo; 13 dias depois, Steve Jobs anunciou um smartphone; de repente, imagens “fluidas” e “Retina” se tornaram “coisas” e; depois disso, tem havido muito ranger de dentes.

O primeiro instinto quando é preciso implementar imagens fluidas/responsivas é se valer da mesma ferramenta usada para layouts responsivos: media queries!

Imagens responsivas com srcset e sizes: media queries

Navegadores não conseguem saber tudo sobre um website que eles ainda sequer carregaram, mas têm algumas informações sobre o ambiente em que estão inseridos: o tamanho de viewport, a resolução de tela etc. A ideia de media queries é: deixar desenvolvedores web fazer coisas específicas em ambientes específicos — se a viewport é maior que mil pixels, mostre a barra lateral à esquerda; senão, mostre à direita; do contrário, empurre para baixo de todo o conteúdo.

Fácil como comer ervilhas!

Imagens responsivas com srcset e sizes: comendo ervilhas

Infelizmente, quando se trata de imagens responsivas, em muitos casos, na prática, usar media queries pode ser desagradavelmente terrível.

Imagens responsivas com srcset e sizes: comendo ervilhas

Imagens responsivas baseadas em media queries não são tão eficientes porque a maioria dos web designers/desenvolvedores altera o layout de uma web page se baseando apenas em 1 variável — largura da viewport –, quando, para lidar efetivamente com imagens responsivas, é preciso trabalhar com 3 variáveis:

  1. Tamanho renderizado (em CSS pixels) da imagem no layout
  2. Densidade de tela
  3. Dimensão da gama de imagens de vários tamanhos à disposição

Uma vez que se saiba essas 3 coisas, a solução é simples: dado um conjunto de fontes, escolha a menor delas, cujas dimensões são ainda maiores do que o tamanho renderizado x densidade tela.

Surpreendentemente, quando o navegador começa a carregar imagens, ele ainda não sabe qual é o tamanho renderizado!

Mas, infelizmente, tamanho renderizado é algo difícil de definir: desenvolvedores simplesmente não conseguimos saber qual é de antemão! Dentro de um layout responsivo, o tamanho renderizado de uma imagem pode ser qualquer um! E, surpreendentemente, quando o navegador começa a carregar imagens, ele ainda não sabe qual é o tamanho renderizado — o tamanho renderizado depende do CSS em ação, que browsers geralmente interpretam (parse) depois de já terem começado a carregar imagens.

Felizmente (aparentemente), ao usar media queries nos arquivos-fonte é possível quebrar o problema em 2 partes menores:

Exigindo aos desenvolvedores especificar somente o tamanho da viewport e densidade de tela, depois de ter completado muitos cálculos difíceis envolvendo todo o resto.

Mas que tipo de cálculos? Vamos a exemplos.

Imagens responsivas com srcset e sizes: calculando

Na verdade, a razão de ser desse exemplo é mostrar que o processo de cálculo de media queries é tedioso e propenso a erros. Então, se você está 100% convencido de que tudo o que se está dizendo sobre o assunto até agora é verdade, pule para as explicações seguintes.

Digamos que se tenha 3 versões de uma imagem:

E é preciso escolher uma e carregá-la dentro em uma grid flexível — uma grid que começa com uma coluna única, mas muda para mais colunas em viewports maiores, como neste exemplo. Levando em consideração, também, que se quer suportar 1x e 2x para device-pixel-ratio.

Neste cenário, como organizar e codificar as media queries? Comecemos do topo.

large.jpg deve ser carregado somente quando absolutamente necessário — quando small.jpg e medium.jpg forem pequenos demais. Mais precisamente, apenas se deve carregar large.jpg quando:

No exemplo, largura renderizada é somente uma porcentagem da largura de viewport. Assim:

O próximo menor arquivo é medium.jpg, então:

Colocando tudo junto:

Em que é possível ser reorganizado em termos de largura de viewport:

Para fazer as media queries, resolvemos para a largura de viewport para cada valor possível de largura da imagem em relação à viewport e densidade tela.

largura da imagem em relação à viewport pode ter 1 de 2 valores: 100vw antes de bater no breakpoint (36em) e 33.3vw depois disso.

Para densidade tela é possível fazer muita coisa, mas, para o exemplo, digamos que só se precise de suporte para device-pixel-ratio de 1x e 2x.

2 largura da imagem em relação à viewport possíveis x 2 densidade de tela = 4 cenários que precisam ser levados em consideração. Vejamos um a um.

1x, antes do breakpoint

Devido ao fato de o breakpoint ser em 36em, por definição se sabe que:

Juntando largura da imagem em relação à viewport = 100vw e densidade de tela = 1x:

Quando se combina os 2, obtém-se algo impossível:

Então, podemos descartar este cenário, quer dizer, nunca será preciso usar large.jpg em 1x com o layout de coluna única.

2x, antes do breakpoint

De novo:

Mas agora usando 2x:

Combinando os 2:

Então, carrega-se large.jpg em telas 2x quando a viewport estiver nesta faixa.

1x, depois do breakpoint

Agora maior que a largura do breakpoint:

Tendo em foco o layout com 3 colunas em telas 1x:

A viewport sempre será mais larga que 36em quando for maior que 120em, então é possível ignorar esses 36em. Queremos carregar large.jpg em telas 1x quando:

2x, depois do breakpoint

E…

Então, em telas de 2x, carregar large.jpg quando:

Colocando tudo junto numa media query:

Como exercício, faça estes mesmos cálculos para medium.jpg. ;-)

Usando a proposta original de <picture> para marcar a imagem:

Uma dor de cabeça!

E uma pilha de marcação de device-pixel-ratio não suportados por 2, ou sob um, e imperfeitamente suporta aqueles no meio. Se é preciso estender o suporte a device-pixel-ratio, o número de cenários a serem considerados aumenta vertiginosamente!

E a pior parte sobre essa marcação é que, se qualquer uma das variáveis subjacentes for alterada — o tamanho das imagens, resoluções dos dispositivos que precisam ser suportados ou qualquer aspecto do nosso layout que afeta o tamanho das imagens –, é preciso refazer toda a matemática novamente…

Imagens responsivas com srcset e sizes: chamando o Raul

srcset + sizes: ao resgate de imagens responsivas

Então, parece que media queries não são soluções mágicas para todos os problemas com imagens responsivas… E agora?

Voltando à lista de variáveis fundamentais de imagens responsivas, dessa vez pensando sobre quando elas variam e quem sabe o quê, temos:

Variável Conhecido pelo autor quando o código é escrito? Conhecido pelo browser quando a página é carregada?
Dimensões da viewport não sim
Tamanho da imagem relativo à viewport sim não
Densidade de tela não sim
Dimensões do arquivo sim não

Perceba que sempre que há um “sim” em uma coluna, há um “não” na outra: o autor e o navegador têm informações complementares direrentes!

Como melhorar essa situação?

Media queries são como um conjunto de planos de contingência. “Veja”, dizemos para o navegador, “Eu não sei o quão grande essa viewport será, mas se for desse tamanho, use este ficheiro; se for maior, use este. Além disso, use este outro caso seja uma tela com densidade de pixels maior, mas não se o layout estiver com 3 colunas”… Na verdade, o que se está fazendo é rotular arquivos dentro de uma gama de possibilidades, sempre levando em conta coisas que o navegador vai saber, mas que nós, desenvolvedores web, ao escrever o código, não sabemos.

E, como já foi visto, na prática isso se transforma em um grande volume de trabalho… Mas e se invertêssemos isso?

E se, ao invés de fornecer ao browser diversos planos de contingência, simplesmente informar as coisas que ele não sabe? Isto é: como a imagem será dimensionada em relação à viewport e a dimensão dos arquivos de imagem. Nós sabemos essas coisas. Se fosse possível compartilhar esse conhecimento com o navegador, ele não teria tudo o que necessário para escolher uma fonte? Teria!

De fato, isso é justamente o que o atributo sizes e o descritor w de srcset fazem, segundo a última especificação de <picture>. Com isso, nossa tabela fica assim:

Variável Conhecido pelo autor quando o código é escrito? Conhecido pelo browser quando a página é carregada?
Dimensões da viewport não sim
Tamanho da imagem relativo à viewport sim não sim! via sizes!
Densidade de tela não sim
Dimensões do arquivo sim não sim! via srcset!

Imagens responsivas com srcset e sizes: arco-íris

Antes de continuarmos, vamos deixar 3 coisas claras.

Primeiro de tudo, tudo o que se está tratando aqui ainda não possui suporte oficial por todos os principais navegadores. Então, cautela.

Segundo: era uma vez uma proposta para imagens responsivas chamada srcset. A proposta mais nova que está sendo tratada aqui tem o atributo que também se chama srcset. Ambos, o antigo e o novo srcset, usam o w para coisas completamente diferentes! O antigo w era uma maneira simplificada de se escrever media queries: a largura descrita era a largura da viewport; o novo w descreve a largura do arquivo.

O novo w será tratado a seguir, mas, antes, você só precisa olhar por um segundo para um destes apagadores de memória estilo MIB e esquecer tudo o que você sabia sobre srcset e w. Olhe aqui por um instante…

Imagens responsivas com srcset e sizes: MIB

Tudo certo? Muito bom!

Terceiro: a nova especificação de <picture> continua permitindo a alterar os arquivos usados com media queries e usar descritores de resolução às URLs. Se você está fazendo direção de arte ou alternância de resolução de tamanho fixo, você deve usar essas features. Mas se você quer que suas imagens simplesmente se alarguem e se contraiam, eis uma nova ferramenta à disposição.

Isto posto, voltemos ao exemplo, dessa vez usando <srcset> e <sizes>. Recapitulando, há 3 versões de imagem:

Além de um breakpoint em 36em que troca o grid de 1 para 3 colunas.

Eis a marcação:

Você deve ter percebido que essa marcação vem da especificação de “picture”, embora não haja nenhum elemento <picture> no exemplo. Os atributos <srcset> e <sizes> serão implementados em <img>, também e, para casos em que não é preciso direção de arte / alternância de tipos como este, é possível — e recomendado — usar uma instância única de nossa velha amiga <img> para fazer a marcação das imagens responsivas.

A mesma boa e velha <img> com novos atributos. Analisemos um a um.

Nenhuma novidade aqui. É um fallback src, tal como sempre foi, que será interpretado por qualquer navegador que não entenda <srcset> e <sizes>. Próximo!

Este é auto explicativo, também. srcset pega uma lista de URLs separados por vírgula para as versões disponíveis da imagem; cada largura de imagem é especificada usando o descritor w. Então, se você salvar uma imagem de 1024×769, coloque 1024w na marcação de srcset. Fácil.

Imagens responsivas com srcset e sizes: declarativo

Você deve ter notado que apenas a largura é informada. Por que não a altura, também? Imagens, em web design responsivo, são restringidas pela largura: suas larguras são explicitamente especificadas pelo CSS, mas suas alturas, não. A vasta maioria das imagens responsivas por aí são restringidas pela largura, também, então a especificação mantém as coisas simples ao lidar somente com larguras.

Olhando para o futuro, há algumas razões pelas quais o descritor h poderia ser usado — mas ainda não.

E, novamente, saiba que, enquanto você pode usar descritores de resolução como 1x e 2x ao invés de w, 1x/2x e w não se misturam! Não use ambos no mesmo srcset. Sério.

Imagens responsivas com srcset e sizes: relâmpago

O último pedacinho de informação que o browser precisa para escolher uma fonte é uma ideia de quão larga a imagem será quando for renderizada no layout. Para isso, usa-se sizes. No exemplo:

O formato aqui é:

Media queries ficam emparelhadas com comprimentos. O navegador passa por cada media query até encontrar um que sirva e usa seu comprimento correspondente como a última peça do quebra-cabeças: a largura da imagem renderizada em relação à viewport.

“Mas o que é isso?”, você pode estar se perguntando. “Media queries? Não havia sido informado que não eram ideais para fazer isso?”

O que foi informado é que as media queries, por si só, não eram um mecanismo ideal para realizar essa escolha de fonte do arquivo. Não é isso o que essas queries estão fazendo; elas estão simplesmente deixando o browser encontrar uma maneira mais eficiente de fazer essa escolha.

Acabaram-se as adivinhações sobre quais resoluções devem ou não serem suportadas.

Lembre-se de como as várias queries no primeiro exemplo não tinham nada a ver com o breakpoint de layout da página (que estava em 36em)? Quer dizer, 60em, 20em, 10em — eles estavam em todo lugar! O breakpoint em sizes deve espelhar exatamente os breakpoints da página. O comprimento de cada um deles especifica a largura da imagem no layout quando essa media query entra em ação.

Então o navegador, tendo toda a informação necessária, faz o mesmo tipo de cálculo maçante, cansativo e sujeito a erro humano que foi mostrado anteriormente.

E se lembra como o exemplo de media query apenas cobria as densidade 1x e 2x? Essa marcação funciona com qualquer device-pixel-ratio! Acabaram-se as adivinhações sobre quais resoluções devem ou não serem suportadas. Quando um smartwatch de 4.8625x for lançado, src e sizes já darão conta do recado!

Além disso, essa solução dá aos navegadores algum “espaço de manobra”. Uma media query vinculada a uma fonte pode ser verdadeira (true) ou falsa (false); se verdadeira, o navegador deve carregar a fonte associada. sizes e src não são tão rígidos: a especificação permite que os navegadores, opcionalmente, carreguem arquivos menores quando a largura de banda é lenta ou cara.

“Bem, tudo isso é maravilhoso”, você pode estar conversando consigo mesmo neste momento, agora que está começando a entender os benefícios de uma abordagem declarativa em detrimento a uma condicional. “Mas espere… O que é um ‘comprimento’?”

Um “comprimento” pode ser muitas coisas. Um comprimento pode ser absoluto (ex.: 99px, 16em) ou relativo (33.3vw). Você notará que, diferentemente do exemplo, existe um monte de layouts que combinam unidades absolutas e relativas. E é neste ponto que a surpreendentemente bem suportada função calc() entra em ação.

Vamos supor que foi adicionada uma barra lateral de 12em ao layout de 3 colunas. Isso acarretaria na necessidade de tal ajuste:

Feito!

Se está prestando bastante atenção, você deve estar se perguntando nesse momento: “Mas o que diabos é esse ‘100vw’ sozinho? Você se esqueceu da media query?”.

Na especificação, um comprimento sem uma media query correspondente é um “comprimento padrão”. Isso significa que, por exemplo, uma imagem gigante de um banner de largura total poderia ser marcado dessa maneira no seu código:

Conclusão sobre imagens responsivas com srcset e sizes

Web design responsivo e imagens responsivas já são assuntos corriqueiros para desenvolvedores web. Não raramente nossa vontade de desenvolvedores aparece com mais rapidez com que as tecnologias são criadas, desenvolvidas e suportadas nos principais navegadores.

Imagens responsivas com srcset e sizes já são uma realidade; e melhor, uma realidade com fallback para navegadores que não suportam as características mais atuais de web design responsivo.

Esteja na vanguarda!