Em Design Responsivo, aprendemos como utilizar media queries para criar layouts separados para dispositivos móveis e desktop. Agora, vamos adicionar imagens na página. Adicionar media queries só permitem apresentar diferentes regras CSS de forma condicional, queremos mostrar diferentes imagens baseado em dispositivos diferentes dos usuários.

Diagram: low-resolution image sent to standard screen desktop devices and all mobile devices versus high-resolution image sent to retina desktop devices

O problema é, o tamanho fixo das imagens. Não podemos ampliar uma imagem que tem 500×250 pixels para nada mais largo que 500 pixels porque a imagem ficará borrada (pixelada). Dispositivos móveis e as telas Retina complicam as coisas ainda mais. Para fazer nossas imagens responsivas, precisamos levar em consideração três coisas a partir de agora:

  • A dimensão dos dispositivos
  • As dimensões das imagens
  • A resolução da tela do dispositivo

Isso será mais difícil que media queries, que só se preocupava com a largura do dispositivo. Mas não se desespere, existem formas padrões de resolver todos esses problemas, e nós iremos ver todas essas formas, um passo de cada vez.

Configuração

Para experimentar as imagens responsivas, precisamos primeiro de uma página resposiva para trabalhar. Este capítulo utilizará o exemplo de página web que criamos no capítulo anterior. Vamos adicionar duas imagems a página, para que ela fique como o exemplo abaixo. Isso pode parecer simples, mas essas imagens irão mudar de acordo com o dispositivo do usuário (e isso é muito legal).

Web page with large photo in header and line art illustration in content

Se você continuar do último capítulo, só precisa fazer o download dessas imagens e adicionar ao seu projeto na pasta images/.

Caso esteja chegando agora aqui e não tem a página desenvolvida no capítulo anterior, é só fazer o download do projeto completo de exemplo, descompactar e abrir no seu editor favorito. Caso deseje, temos um tutorial completo de como utilizar o Atom, confira na introdução como instalar e configurá-lo.

Screenshot: Atom’s file browser after unzipping the project

Em qualquer um dos casos, os arquivos do seu projeto deverão se parecer como a imagem abaixo. Perceba como temos múltiplas cópias das nossas imagems PNG e JPG (ex., illustration-big.png e illustration-small.png). Vamos fazer com que o navegador escolha qual dessas deve carregar dependendo do tamanho do dispositivo e resolução da tela.

Telas Retina

Esta é primeira vez que vamos tratar sobre telas retina, então vamos falar um pouco sobre a resolução de tela. As telas retina tem duas vezes mais a quantidade de pixel por polegada que a maioria das telas de resolução padrão. Isso significa que, cada pixel retina é equivalente a 4 pixels padrão. Isso tem um grande impacto em como as imagens são apresentadas em um navegador web.

Diagram: standard-resolution screen with 4 pixels versus high-resolution screen with 16 pixels

Para a renderização acontecer corretamente em um tela retina, uma imagem precisa ser duas vezes maior que as dimensões da tela que será apresentada. Por exemplo, se você quiser adicionar uma imagem a página com 500×250 pixel, a imagem correspondente, precisa ser de 1000×500 pixels.

Diagram: High-resolution image shrunk to half size and displayed on retina screen

Isso na verdade é uma simplificação—nem todas as telas retina são criadas de forma idêntica. Por exemplo, o iPhone 6 Plus tem três vezes mais pixel por polegadas do que uma tela padrão. Este tutorial vai focar no caso de usar o dobro do tamanho, mas as mesmas técnicas se aplicam as telas retinas com 3x o tamanho, também.

O que mais, telas padrões e dispositivos menores não precisam de todos esses pixels extras em imagens de alta resolução, e enviar todo esse dado desnecessário normalmente resulta em uma má experiência para o usuário.

Imagens SVG Responsivas

A forma mais fácil de resolver todos esses problemas é com SVG images. Elas “simplesmente funcionam”. Uma vez que elas sejam baseadas em vetores, SVG evitam os problemas de resolução de tela que veremos na próxima seção. Vamos dar uma olhada adicionando uma ilustração a nossa páginas responsive.html. Substitua a imagem que já existe na div .content para o que estamos utilizando e fique igual abaixo:

<div class='section content'>
  <img class='illustration' src='images/illustration.svg' />
</div>

Os navegadores ajustarão automaticamente as SVGs para telas retina, então essa imagem SVG com 500×250 pixel será renderizada de forma adequada tanto em telas padrões como em telas retina.

As SVG fazem a gente esquecer dos problemas de resolução de tela, mas precisamos diminuir a figura para caber corretamente no nosso layout para tablets e smartphones. O Firefox irá fazer isso automaticamente, mas se você abrir esta página com o Chrome e deixar a tela do seu navegador ficar bem estreita, você vai achar que a imagem está no mesmo tamanho.

Para ter uma imagem fluída no Chrome, precisamos dizer para a figura sempre se ajustar a larguda do seu contêiner. No arquivo styles.css, coloque a seguinte regra com o resto do estilo base, fora das media queries:

.illustration {
  width: 100%;
}

Quando especificamos a largura da nossa imagem em 100% ela irá assumir que queremos manter a proporção do aspecto e calculará sua altura de forma automática. Isso corrige o layout para dispositivos móveis, mas agora a versão desktop está imensa:

SVG image shrinking to fit mobile and tablet widths, but very large in desktop layout

Esse comportamento é perfeito para alguns designs (como uma foto com full bleed que veremos na próxima seção), mas não agora. Queremos limitar a largura da figura a sua largura herdada, que é 500 pixels. Conseguimos fazer isso com um estilo em linha:

<div class='section content'>
  <img class='illustration' src='images/illustration.svg' style='max-width: 500px'/>
</div>

Essa é uma das raras vezes que um estilo em linha é aceitável, devido o fato que ela está descrevendo uma propriedade inata da imagem. Uma dimensão física da imagem é mais um conteúdo do que uma apresentação, então faz sentido ela aparecer na HTML ao invés de na folha de estilo.

Adding an inline style to limit the size of the SVG image

Imagens PNG, GIF, e
JPG responsivas

É claro, nem todas as imagens na web são SVGs. Algumas vezes você vai precisar incluir uma fotografia. Imagens PNG, GIF e JPG são “imagens cerrilhadas” (como as bitmap), isso quer dizer que elas são definidas pixel-por-pixel ao invés de vetores. Como resultado, elas são mais sensíveis a resolução das telas que a SVG.

Se vocẽ não estiver preocupada com otimização, imagens responsivas com bitmap não são muito mais difíceis de usar do que imagens SVG. Tente mudar a imagem illustration.svg por um arquivo PNG:

<div class='section content'>
  <div class='illustration'>
    <img src='images/illustration-big.png' style='max-width: 500px'/>
  </div>
</div>

Mudamos um pouco a estrutura HTML, aninhando nossa tag <img/> em outro contêiner. Sem isso, a imagem ficaria distorcida por que o flexbox tenta definir sua altura para o mesmo valor do contêiner do .content. Isso requer um pequeno hack na nossa regra CSS da .illustration também:

.illustration img {
  width: 100%;
  display: block;
}

Perceba o sufixo -big no nome do arquivo da imagem. Isso é uma versão de alta-resolução do PNG, que tem as dimensões de 1000×500. Telas retina precisam de “2x” o tamanho da imagem para apresentar de forma correta, sem serrilhados. Se usarmos uma versão de baixa resolução da imagem (por exemplo 500×250 pixels), vai ficar adequado para telas padrões, mas vai ficar estranho em uma tela retina.

Diagram: serving a high-resolution image to both standard screens and retina screens (which is wasteful)

Considere isso uma forma preguiçosa de criar imagens PGN, GIF ou JPG responsivas, assim como assuma que todos precisam de uma imagem de alta resolução, mesmo que não. Isso por que, uma imagem de 1000×500 pixel é um exagero para telas padrão (não retina). Vamos ver uma forma mais inteligente de resolver isso na próxima seção.

Otimizando Imagens Responsivas

Diferentes telas tem necessidades de imagens diferentes. Felizmente, a HTML provê um forma de escolher a melhor imagem para a tela do usuário. Nas próximas seções, vamos dar uma olhada em três cenários para otimizar as imagens responsivas:

  • Uma tela com resolução padrão que não precisa de uma imagem com qualidade de tela retina.
  • Uma tela retina de dispositivo móvel que pode utilizar uma umagem de qualidade padrão porque é muito pequena para perceber a diferença.
  • Um layout para desktop que usa uma imagem larga, e um layout para dispositivos móveis associada que usa uma imagem mais alta.

O primeiro método é o mais fácil, e é ótimo para imagens menores que 600 pixels por que elas não são grandes o suficiente para beneficiar o segundo cenário. O segundo método é muito importante para imagens maiores, especialmente para imagens utilizadas em layouts full-bleed. O terceiro é para quando você tá extravagante, tá podendo usufruir de todas as possibilidades.

Otimização de telas Retina usando srcset

Imagens de alta resolução são realmente grandes! Nosso arquivo com illustration-big.png ocupa duas vezes o tamanho em disco comparado com uma imagem em baixa resolução. Não faz sentido colocar uma imagem assim tão grande quando o usuário não precisa.

Adicionando o atributo srcset no nosso elemento <img/> permite que apresentemos nossa imagem de alta resolução somente para telas retina, e usando a versão de baixa resolução para telas padrões. Altere o elemento .illustration para ficar igual ao seguinte:

<div class='illustration'>
  <img src='illustration-small.png'
       srcset='images/illustration-small.png 1x,
               images/illustration-big.png 2x'
       style='max-width: 500px'/>
</div>

O atributo srcset aponta para uma lista de arquivos de imagens alternativo, através dessa propriedade podemos definir quando o navegador deve usar cada uma dessas imagens. O lx diz ao navegador para mostrar illustration-small.png nas telas padrões. O 2x indica que a illustration-png.png é para telas retina. navegadores mais antigos que não entendem o atributo srcset vão utilizar o atributo src como alternativa.

Diagram: serving a low-resolution image to a standard screen and a high-resolution image to retina screens

Normalmente as versões de baixa e alta resolução de uma imagem deveriam ser exatamente iguais (exceto pelas suas dimensões), mas nós utilizamos a illustration-small.png em amarelo para que você pudesse diferenciar facilmente da versão retina, que é azul.

É um pouco difícil de ver isso funcionando sem uma página real, então incluímos uma versão de exemplo nesta página. A imagem abaixo deve ser azul se você estiver vendo em uma tela retina. Caso contrário, será amarela em telas padrões.

Se você estiver fazendo esses exemplos em um computador com uma tela retina, você pode tentar mudar temporariamente o valor 2x para 1x para ver como uma tela não-retina se parece. Fica um pouco confuso (e amarelo).

Otimização da largura de tela usando srcset

Ótimo! Podemos salvar algum espaço extra para telas não-retina. Infelizmente, a técnica de usar o srcset não satisfaz um caso muito importante para imagens grande: se o usuário tem um smartphone, ele irá baixar a imagem em alta resolução, mesmo quando a versão padrão for suficiente.

Diagram: low-resolution image sent to standard screen desktop devices and all mobile devices versus high-resolution image sent to retina desktop devices

Imagine que nós queremos mostrar uma imagem grande no nosso elemento .header. O cabeçalho tem uma largura de 960 pixels no layout para desktop, então nossa imagem precisa ter pelo menos 1920 pixels de largura para ser utilizado em uma tela retina. Também iremos providenciar uma imagem com 960 pixels de largura para telas padrões. Agora, considere um smartphone com tela retina. Smartphones tem normalmente menos de 400 pixels de largura em modo retrato, o que significa que uma imagem correspondente com qualidade de retina deve ter até 800 pixels de largura.

Ou seja, podemos utilizar uma imagem com resolução padrão para smartphones com tela retina!

A lição aqui é que queremos otimizar imagens largas baseadas nas dimensões finais de apresentação, não somente na resolução do dispositivo. Vamos lá, adicione a imagem grande ao nosso elemento .header:

<div class='section header'>
  <div class='photo'>
    <img src='images/photo-small.jpg'
         srcset='images/photo-big.jpg 2000w,
                 images/photo-small.jpg 1000w'
         sizes='(min-width: 960px) 960px,
                100vw'/>
  </div>
</div>

Nós temos o mesmo elemento srcset como na última seção, mas ao invés de usar os descritores 1x ou 2x estamos deixando que a imagem herde a largura física. O valor 2000w diz ao navegador que o arquivo photo-big.jpg tem 2000 pixels de largura. Assim como, o valor 1000w diz que photo-small.jpg tem a largura de 1000 pixels. Se você está imaginando o que o caracter w significa, é uma unidade especial utilizada somente para esse tipo de cenário de otimização de imagens.

Diagram: srcset=1000w as width of the low-resolution image file, srcset=2000w as width of the high-resolution image file

A largura de uma imagem sozinha não é suficiente para um dispositivo determinar qual imagem deve ser carregada. Também precisamos dizer qual a largura final da imagem será usada. É aqui que o atributo sizes entra. Ele define uma série de media queries junto da largura renderizada da imagem quando aquela media query está ativa.

Diagram: sizes=100vw as width of the image in the mobile layout, sizes=960px as width of the image in the desktop layout

Aqui, estamos dizendo que quando a tela tem pelo menos 960px de largura, a imagem terá também 960 pixels de largura. Caso contrário, o valor padrão de 100vw será usado e dirá ao navegador que a largura da imagem será de 100% da largura da “viewport” (é um jeito chique de dizer a largura da tela). Você pode ler mais sobre a unidade vw na MDN. Tudo isso para casar com o comportamento de redimensionamento da imagem na nossa CSS.

Falando nisso, precisamos fazer algumas mudanças para posicionar nossa nova imagem no cabeçalho de forma correta. Adicione as duas regras abaixo aos nossos outros estilos base, logo acima do media query do estilo para dispositivos móveis:

.header {
  height: auto;
  justify-content: inherit;
  align-items: inherit;
}

.photo img {
  width: 100%;
  display: block;
}

Lembre-se que nossa imagem de baixa resolução tem a largura de 1000 pixels, quer dizer que dispositivos com tela retina podem utilizar uma vez que suas telas tenham menos que 500 pixels de largura. No Firefox, você poded agora redimensionar o navegador para vizualizar a versão com retina (“Grande”) quando a janela é maior que 500 pixels e a versão não-retina (“Pequena”) para telas mais estreitas.

Agora estamos ofertando uma imagem de 115KB para dispositivos móveis ao invés de forçá-los a utilizar uma imagem de alta resolução com 445KB. Isso é bastante coisa, especialmente para páginas que usam muitas imagens.

Testando com o Chrome

Essa técnica funciona muito bem no Chrome, mas não podemos dizer que é o mais inteligentes. O Chrome sempre irá utilizar a versão de alta resolução se ele já tiver ela já tiver sido armazenada no cache localmente, quer dizer que não podemos ver a versão de baixa resolução simplesmente diminuindo o tamanho da janela do navegador. Precisamos evitar o cache local do navegador abrindo uma noga janela incognito, e depois evitando carregar a versão photo-bit.jpg deixando a janela do navegador bem estreita antes de carregar a página.

Direção de arte usando <picture>

A seção anterior é perfeitamente aceitável em termos de otimização para uso de dados. Poderíamos parar por aqui e estaria ótimo, mas vamos deixar as coisas um pouco mais chiques com uma pitada de “direção de arte”. Pense em direção de arte como otimização de imagens responsivas para designers.

Isso permite que você otimize os layouts enviando imagens completamente diferentes para o usuário dependendo do dispositivo que estive usando. Compare isso à seção anterior, que otimizamos a mesma imagem para diferentes dispositivos. Por exemplo, nossa imagem de cabeçalho é bem larga. Não seria ótimos se pudéssemos cortar uma versão menor e apresentar aos dispositivos móveis ao invés de uma versão mais larga usando em desktops?

Diagram: serving a tall-cropped image to mobile devices and a wide-cropped image to standard- and high-resolution desktop devices

Para isso, precisamos com utilizar os elementos <picture> e <source>. O primeiro é somente um empacotador e o segundo carrega a imagem baseado nos media queries. Tente alterar o elemento .header para o seguinte:

<div class='section header'>
  <div class='photo'>
    <picture>
      <source media='(min-width: 401px)'
              srcset='images/photo-big.jpg'/>
      <source media='(max-width: 400px)'
              srcset='images/photo-tall.jpg'/>
      <img src='images/photo-small.jpg'/>
    </picture>
  </div>
</div>

Conceitualmente, isso é muito similar a utilizar as media queries na CSS. Em cada elemento <source>, o atributo media define quando a imagem deve ser carregada, e o elemento srcset define qual arquivo de imagem deve ser carregado. O elemento <img/> só é usado como uma garantia para navegadores antigos. Você deve conseguir ver a versão mais alta da imagem quando você diminuir a janela do navegador:

Mobile web page with tall-cropped image and desktop web page with wide-cropped image

Esse nível de controle fará seu designer bem feliz, mas em compensação não vai deixar o navegador pegar a versão automática a imagem de mais alta qualidade. Isso significa que nós perdemos nossa otimização para telas retina da seção anterior: uma vez que a largura da tela tenha 401 pixels ou mais, o navegador irá sempre utilizar a versão em alta resolução e cortada.

Enquanto é possível combinar o melhor dos dois mundos, isso também pode ficar complicado bem rápido. Nossa recomendação é utilizar a versão de 1x e 2x do srcset para imagens menores que 600 pixels de largura, use o método do srcset com o sizes da seção anterior para imagens maiores, e guarde o método com o <picture> para quando você quiser tentar fazer algo bem diferenciado juntamente com o designer.

Resumo

Imagens responsivas podem parecer muito complicado, mas só tem realmente dois problemas que estamos tentando resolver:

  • Fazer as imagens caberem em layouts para dispositivos móveis respeitando o tamanho intrínseco delas.
  • Evitar fazer downloads desnecessários de imagens muito grande.

Alcançamos o primeiro fazendo com que as imagens sempre se estiquem para preencher 100% de seu contêiner, enquanto limitamos seu tamanho com um estilo de largura máxima. Para o segundo, usamos o srcset para otimizar a resolução da tela, srcset mais sizes para otimizar a largura do dispositivo, e finalmente o elemento <picture> para um controle manual sobre qual imagem será apresentado.

Design responsivo é um tópico em evolução. Os navegadores só recentemente implementaram as técnicas de otimização de imagens tratados nesse capítulo, mesmo que design responsivo seja um padrão por mais de meia década. Enquanto as tecnologias utilizadas para criar uma página responsiva podem mudar, os problemas fundamentais de apresentação de conteúdo em diferentes dispositivos nunca irão desaparecer. Então, mesmo que você tenha que eventualmente aprender alguma nova ferramenta, os conceitos fundamentais que introduzimos devem seguir com você pra sempre.

Esses últimos cinco capítulos focaram somente nos layouts. Exploramos floats, flexbox, posicionamento avançado e como aplicar todos esses conceitos a várias telas de larguras diferentes. Isso é basicamente tudo que você precisa saber para desenvolver páginas web com HTML e CSS. O próximo capítulo volta ao mundo da HTML, introduzindo um monte de novos elementos que farão os mecanismos de busca muito mais felizes com nossas páginas web.