Sintaxes prescritivas

O elemento <picture> não renderiza nada sozinho, mas atua como um mecanismo de decisão para um elemento <img> interno, informando o que será renderizado. <picture> segue um precedente já definido pelos elementos <audio> e <video>: um elemento wrapper que contém elementos <source> individuais.

<picture>
   <source …>
   <source …>
    <img …>
</picture …>

Esse <img> interno também fornece um padrão substituto confiável para navegadores mais antigos sem suporte a imagens responsivas: se o elemento <picture> não for reconhecido pelo navegador do usuário, ele será ignorado. Os elementos <source> também são descartados, já que o navegador não os reconhecerá ou não terá contexto significativo sem um pai <video> ou <audio>. No entanto, o elemento <img> interno será reconhecido por qualquer navegador, e a fonte especificada no src será renderizada conforme o esperado.

Imagens com "direção de arte" com <picture>

Fazer alterações no conteúdo ou na proporção de uma imagem com base no tamanho dela na página normalmente é chamado de imagens responsivas "direcionadas à arte". srcset e sizes foram projetados para funcionar de modo invisível, trocando as origens de acordo com as necessidades do navegador do usuário. No entanto, há momentos em que você quer alterar as origens nos pontos de interrupção para destacar melhor o conteúdo, da mesma forma que adapta os layouts das páginas. Por exemplo: uma imagem de cabeçalho de largura total com um pequeno foco central pode funcionar bem em uma janela de visualização grande:

Imagem com a largura do cabeçalho de uma flor azul-laranja cercada por folhas e caules sendo visitada por uma abelha.

Mas, quando reduzida para se adequar a janelas de visualização pequenas, o foco central da imagem pode ser perdido:

Imagem com a largura do cabeçalho de uma flor azul-lavanda, reduzida. A abelha quase não está visível.

O assunto dessas origens de imagem é o mesmo. No entanto, para focar melhor esse assunto visualmente, as proporções da origem da imagem precisam mudar nos pontos de interrupção. Por exemplo, um zoom mais restrito no centro da imagem e alguns detalhes nas bordas são cortados:

Um recorte ampliado da flor azul-lavanda.

Esse tipo de "corte" pode ser feito com CSS, mas faria com que o usuário solicitasse todos os dados que compõem a imagem, mesmo que nunca a vejam.

Cada elemento source tem atributos que definem as condições para a seleção desse source: media, que aceita uma consulta de mídia, e type, que aceita um tipo de mídia (anteriormente conhecido como "tipo MIME"). O primeiro <source> na ordem de origem para corresponder ao contexto de navegação atual do usuário é selecionado, e o conteúdo do atributo srcset nessa source será usado para determinar os candidatos certos para esse contexto. Neste exemplo, a primeira source com um atributo media que corresponde ao tamanho da janela de visualização do usuário será selecionada:

<picture>
  <source media="(min-width: 1200px)" srcset="wide-crop.jpg">
  <img src="close-crop.jpg" alt="…">
</picture>

É necessário sempre especificar o img interno por último na ordem. Se nenhum dos elementos source corresponder aos critérios media ou type, a imagem atuará como uma fonte "padrão". Se você estiver usando consultas de mídia min-width, é recomendável ter as maiores origens primeiro, conforme mostrado no código anterior. Ao usar consultas de mídia max-width, coloque a menor origem primeiro.

<picture>
   <source media="(max-width: 400px)" srcset="mid-bp.jpg">
   <source media="(max-width: 800px)" srcset="high-bp.jpg">
   <img src="highest-bp.jpg" alt="…">
</picture>

Quando uma fonte é escolhida com base nos critérios especificados, o atributo srcset em source é transmitido para o <img> como se tivesse sido definido na própria <img>. Isso significa que você pode usar sizes para otimizar também origens de imagens direcionadas à arte.

<picture>
   <source media="(min-width: 800px)" srcset="high-bp-1600.jpg 1600w, high-bp-1000.jpg 1000w">
   <source srcset="lower-bp-1200.jpg 1200w, lower-bp-800.jpg 800w">
   <img src="fallback.jpg" alt="…" sizes="calc(100vw - 2em)">
</picture>

Obviamente, uma imagem com proporções que podem variar dependendo do elemento <source> selecionado gera um problema de desempenho: <img> oferece suporte apenas a um único atributo width e height, mas omitir esses atributos pode levar a uma experiência do usuário consideravelmente pior. Para isso, uma adição relativamente recente, com suporte, à especificação HTML permite o uso dos atributos height e width nos elementos <source>. Elas trabalham para reduzir as mudanças de layout da mesma forma que fazem em <img>, com o espaço adequado reservado no layout para qualquer elemento <source> que estiver selecionado.

<picture>
   <source
      media="(min-width: 800px)"
      srcset="high-bp-1600.jpg 1600w, high-bp-1000.jpg 1000w"
      width="1600"
      height="800">
   <img src="fallback.jpg"
      srcset="lower-bp-1200.jpg 1200w, lower-bp-800.jpg 800w"
      sizes="calc(100vw - 2em)"
      width="1200"
      height="750"
      alt="…">
</picture>

É importante observar que a direção de arte pode ser usada para mais do que decisões baseadas no tamanho da janela de visualização. Isso seria necessário, já que a maioria desses casos pode ser processada de maneira mais eficiente com srcset/sizes. Por exemplo, selecionar uma fonte de imagem mais adequada ao esquema de cores ditado pela preferência do usuário:

<picture>
   <source media="(prefers-color-scheme: dark)" srcset="hero-dark.jpg">
   <img srcset="hero-light.jpg">
</picture>

O atributo type

O atributo type permite que você use o mecanismo de decisão de solicitação única do elemento <picture> para veicular formatos de imagem apenas para navegadores com suporte a eles.

Como você aprendeu em Formatos e compactação de imagens, uma codificação que o navegador não puder analisar nem será reconhecida como dados de imagem.

Antes da introdução do elemento <picture>, as soluções de front-end mais viáveis para exibir novos formatos de imagem exigiam que o navegador solicitasse e tentasse analisar um arquivo de imagem antes de determinar se ele seria descartado e carregasse um substituto. Um exemplo comum foi um script com estas linhas:

   <img src="image.webp"
    data-fallback="image.jpg"
    onerror="this.src=this.getAttribute('data-fallback'); this.onerror=null;"
    alt="...">

Com esse padrão, uma solicitação de image.webp ainda seria feita em todos os navegadores, o que significa uma transferência desperdiçada para navegadores sem suporte ao WebP. Os navegadores que não conseguiam analisar a codificação WebP gerariam um evento onerror e trocariam o valor data-fallback por src. Era uma solução dispendiosa, mas, novamente, abordagens como essa eram a única opção disponível no front-end. Lembre-se de que o navegador começa a fazer solicitações de imagens antes que qualquer script personalizado possa ser executado ou até mesmo analisado. Portanto, não foi possível forçar a interrupção desse processo.

O elemento <picture> foi projetado explicitamente para evitar essas solicitações redundantes. Embora ainda não haja como um navegador reconhecer um formato incompatível sem solicitá-lo, o atributo type avisa o navegador sobre as codificações de origem antecipadamente para que ele possa decidir se vai fazer uma solicitação ou não.

No atributo type, forneça o tipo de mídia (anteriormente, tipo MIME) da origem da imagem especificada no atributo srcset de cada <source>. Isso fornece ao navegador todas as informações necessárias para determinar imediatamente se a imagem candidata a imagem fornecida por source pode ser decodificada sem fazer solicitações externas. Se o tipo de mídia não for reconhecido, o <source> e todos os candidatos serão desconsiderados, e o navegador vai avançar.

<picture>
 <source type="image/webp" srcset="pic.webp">
 <img src="pic.jpg" alt="...">
</picture>

Aqui, qualquer navegador compatível com a codificação WebP reconhecerá o tipo de mídia image/webp especificado no atributo type do elemento <source>, selecione esse <source> e, como fornecemos apenas um candidato em srcset, instruirá o <img> interno a solicitar, transferir e renderizar pic.webp. Qualquer navegador sem suporte para WebP desconsiderará o source e ausente qualquer instrução em contrário, o <img> renderizará o conteúdo de src da mesma forma que vem sendo feito desde 1992. Não é necessário especificar um segundo elemento <source> com type="image/jpeg" aqui. Você pode presumir que o suporte universal para JPEG seja oferecido.

Independentemente do contexto de navegação do usuário, tudo isso é feito com uma única transferência de arquivo, sem desperdício de largura de banda em origens de imagem que não podem ser renderizadas. Isso também é inovador: uma vez que formatos de arquivo mais novos e eficientes virão com os próprios tipos de mídia, e poderemos aproveitá-los graças ao picture, sem JavaScript, dependências do servidor e toda a velocidade do <img>.

O futuro das imagens responsivas

Todos os padrões de marcação discutidos aqui foram um grande aumento em termos de padronização: mudar a funcionalidade de algo estabelecido e central na Web como o <img> não foi um trabalho fácil e o conjunto de problemas que essas mudanças visavam resolver eram abrangentes para dizer o mínimo. Se você já se pegou pensando que há muito espaço para melhorar com esses padrões de marcação, você está certo. Desde o início, esses padrões tinham como objetivo fornecer uma linha de base para a criação de tecnologias futuras.

Todas essas soluções dependiam necessariamente da marcação, para serem incluídas no payload inicial do servidor e chegarem a tempo para o navegador solicitar origens de imagem, uma limitação que levava ao atributo sizes claramente difícil de administrar.

No entanto, como esses recursos foram introduzidos à plataforma Web, um método nativo de adiar solicitações de imagem foi introduzido. Elementos <img> com o atributo loading="lazy" não são solicitados até que o layout da página seja conhecido para adiar solicitações de imagens fora da janela de visualização inicial do usuário até mais tarde no processo de renderização da página, possivelmente evitando solicitações desnecessárias. Como o navegador entende totalmente o layout da página no momento em que essas solicitações são feitas, um atributo sizes="auto" foi proposto como uma adição à especificação HTML para evitar a tarefa de criar atributos sizes escritos manualmente nesses casos.

Também fizemos adições ao elemento <picture> no horizonte para corresponder a algumas mudanças excepcionalmente interessantes na forma como estilizamos os layouts de página. Embora as informações da janela de visualização sejam uma base sólida para decisões de layout de alto nível, elas impedem uma abordagem de desenvolvimento totalmente de componente, ou seja, um componente que pode ser inserido em qualquer parte de um layout de página, com estilos que respondem ao espaço que o próprio componente ocupa. Essa preocupação levou à criação de consultas de contêiner, um método de definir o estilo dos elementos com base no tamanho do contêiner pai, e não apenas na janela de visualização.

Embora a sintaxe de consulta do contêiner tenha apenas se estabilizado e o suporte ao navegador seja muito limitado, até o momento deste artigo, a adição das tecnologias de navegador que a ativam fornece ao elemento <picture> uma forma de fazer a mesma coisa: um possível atributo container que permite critérios de seleção <source> com base no espaço que o <img> do elemento <picture> ocupa, em vez de se basear no tamanho da janela de visualização.

Se isso parece um pouco vago, bem, há um bom motivo: essas discussões sobre padrões da Web estão em andamento, mas estão longe de serem resolvidas. Ainda não é possível usá-las.

Embora a marcação de imagem responsiva prometa apenas ficar mais fácil de trabalhar com o tempo, como qualquer tecnologia da Web, há vários serviços, tecnologias e frameworks para ajudar a aliviar o fardo de escrever à mão essa marcação disponível. No próximo módulo, veremos como integrar tudo o que aprendemos sobre formatos de imagem, compactação e imagens responsivas em um fluxo de trabalho de desenvolvimento moderno.