Capítulo 7: Continuação do HTML

Explorar as funcionalidades do HTML5 é essencial para garantir que as imagens em sua página web se adaptem perfeitamente a qualquer dispositivo. Com os atributos corretos e as tags apropriadas, você pode otimizar a experiência do usuário, entregando imagens responsivas em HTML que carregam rapidamente e exibem a melhor qualidade possível, independentemente do tamanho da tela.

Revisão do Conteúdo Anterior

Na última vez, instalamos o PostHTML e algumas de suas ferramentas mais utilizadas. Começamos a dividir nossa página web em módulos e aprendemos alguns pontos importantes:

  • Atualmente, o padrão MVC é o mais usado para o desenvolvimento de aplicativos na web.
  • JAMSTACK é MVC, mas com um site estático adicional.
  • Como lidar com as preocupações de segurança do Node.
  • Como configurar o PostHTML.
  • Dividir o HTML em módulos pode economizar tempo e esforço.

Agora, vamos melhorar a forma como as imagens são renderizadas.

Imagens Responsivas em HTML: Aprimorando a Experiência Visual

Ao desenvolver páginas web, é comum nos preocuparmos em adaptar o conteúdo a diferentes dispositivos, desde smartphones até telas maiores. Mas e as imagens? Elas possuem largura e altura inerentes, e, embora os navegadores modernos consigam redimensioná-las, essa solução nem sempre é a ideal. Quais outras opções temos?

O Atributo srcset

O HTML5 trouxe alguns atributos adicionais para a tag img, que mantêm a compatibilidade com versões anteriores e permitem incluir caminhos para imagens maiores ou menores na mesma tag. Veja um exemplo:

<img
  src="example-image-default.jpg"
  sizes="(max-width: 600px) 480px, 800px"
  srcset="example-image-480w.jpg 480w, example-image-default.jpg 800w"
  alt="This is an example image.">

Vamos analisar cada um desses atributos:

  • src – O caminho padrão para a imagem.
  • sizes – Usa a sintaxe de CSS media queries para definir limites para a troca de imagens, com base na largura da janela de visualização do navegador. No exemplo, temos dois limites:
    • Tamanho entre zero e 600px (max-width: 600px).
    • Tamanho acima de 600px.
  • sizes (novamente) – O que significa 480px, 800px? Indica quanto espaço a imagem ocupa, considerando o padding e outros elementos ao redor.
  • srcset – Semelhante ao anterior, mas fornece os caminhos para as imagens. Cada imagem e largura são separadas por vírgula. Temos example-image-480w.jpg com 480 pixels de largura e example-image-default.jpg com 800 pixels. Os navegadores não sabem a largura de uma imagem antes de carregá-la, então essa informação ajuda o navegador a alocar espaço corretamente durante a renderização da página.
  • alt – O texto alternativo que representa a imagem antes de ser carregada.

A sintaxe pode ser um pouco complicada, mas felizmente, temos uma alternativa!

A Tag picture

A tag picture no HTML5 divide essa estrutura em múltiplas tags source, dentro de uma tag wrapper chamada picture. Veja como fica:

<picture>
  <source media="(max-width: 799px)" srcset="example-image-small.jpg">
  <source media="(min-width: 800px)" srcset="example-image-large.jpg">
  <img src="example-image-small.jpg" alt="Just imagine a really cool image.">
</picture>

Essa estrutura é mais fácil de ler e também permite verificar se um navegador suporta um formato de imagem específico. Por exemplo:

<picture>
  <!-- JPEG XL - poor support currently,
   so sharp support is experimental -->
  <source srcset="example-image.jxl" type="image/jxl">
  <source srcset="example-image.avif" type="image/avif">
  <source srcset="example-image.webp" type="image/webp">
  <img src="example-image.jpg" alt="Just imagine a really cool image.">
</picture>

O navegador começa de cima para baixo e carrega a primeira imagem que ele suporta. Em seguida, como em uma instrução if/else, ele ignora o restante do código e continua. Navegadores mais antigos renderizarão a tag img, mas os navegadores modernos ainda usarão o texto alternativo (alt) na marcação final.

Essa abordagem nos permite usar os pipelines de imagem Sharp adicionais que criamos anteriormente.

Revisando Nossa Marcação

Atualmente, dentro de index.html, referenciamos uma única imagem:

<p><img src="/img/example-01.webp" alt="An animal, yesterday"></p>

Vamos usar uma tag picture e armazená-la em um novo fragmento.

Crie um novo arquivo em src/fragments chamado picture.html. Ele deve ter a seguinte estrutura:

<picture>
  <source srcset="{{ path }}.avif" type="image/avif">
  <source srcset="{{ path }}.webp" type="image/webp">
  <img src="{{ path }}.jpg" alt="{{ alt_text }}">
</picture>

As chaves duplas são como caracteres de escape, indicando que a linguagem mudou.

Agora, volte para a página index.html e substitua o parágrafo com a tag de imagem pelo seguinte:

<module
  href="src/fragments/picture.html"
  locals='{
    "path": "/img/example-01",
    "alt_text": "An animal, yesterday"
  }'
></module>

Isso funciona como o método que usamos para importar o fragmento para a tag head, mas, desta vez, estamos chamando o módulo picture.html e enviando dados junto com a requisição.

Estamos enviando um JSON através do atributo locals. Como o JSON deve ter nomes e valores entre aspas duplas (e o HTML não se importa se usarmos aspas simples ou duplas), o atributo locals tem aspas simples envolvendo os dados JSON.

Quando essas variáveis chegam ao fragmento picture.html, nós as extraímos e adicionamos à marcação. Não podemos usar hífens para separar palavras nos nomes das variáveis (kebab case), então usamos sublinhados (snake case).

Limitações Desta Abordagem

Embora isso nos permita colocar nossa marcação em componentes e passar dados entre eles, não podemos usar lógica para alterar a marcação dependendo dos dados de entrada. Para uma abordagem mais robusta, precisaríamos instalar uma linguagem de templating como Twig, EJS, Handlebars, Pug ou Mustache. A documentação para posthtml-modules não menciona package.json ou qualquer uma das abordagens que usamos neste guia. Em vez disso, os exemplos estão em JavaScript e somos aconselhados a adicionar isso à nossa aplicação Node.

A abordagem que usamos até agora é evitar task runners tanto quanto possível e juntar comandos até termos um site que atenda às nossas necessidades. Outra abordagem é escrever código em JavaScript para ser executado pelo Node, que produz nosso site. Isso funciona de maneira semelhante a como criamos image-compress.js e o executamos em package.json usando o comando node tools/image-compress.js, em vez de npm run …, como fizemos para os outros comandos.

Poderíamos também usar um gerador de site estático dedicado, dos quais não faltam opções. Este curso tem como objetivo apresentar algumas das ferramentas usadas para criar esses pacotes e como você pode combiná-las.

Adicionando uma Segunda Página

Infelizmente, o PostHTML e seus amigos não funcionam da mesma forma que o pacote sass: chamamos uma instância do PostHTML e ele processa um arquivo de cada vez. Isso significa que precisamos adicionar alguns novos requisitos:

  1. A tarefa de observação (watch task) precisa saber quando qualquer página de origem foi alterada e atualizar a página de distribuição correspondente.
  2. Precisamos de uma tarefa que reconstrua todas as páginas HTML, para nosso hipotético segundo desenvolvedor, na primeira execução.

Isso parece familiar! Não foi isso que tivemos que fazer com o sharp também? Talvez possamos reutilizar o código!

Chamando o PostHTML

Existem duas maneiras diferentes de chamar o PostHTML (estritamente falando, estamos chamando posthtml-cli, mas tanto faz):

  • Chamando-o e apontando-o para um arquivo de configuração (posthtml.json).
  • Chamando-o e apontando-o para um arquivo de configuração, mas especificando o arquivo de entrada e saída ao mesmo tempo.

Atualmente, os arquivos de entrada e saída estão codificados em posthtml.json:

{
  "input": "src/views/**/*.html",
  "output": "dist",
  "plugins": {
      "posthtml-modules": {
          "root": "./src/views",
          "initial": true
      },
      "htmlnano": {}
  }
}

Vamos remover os nós input e output deste arquivo, para que ele apenas estabeleça os padrões para os plugins. Deve ficar assim:

{
  "plugins": {
      "posthtml-modules": {
          "root": "./src/views",
          "initial": true
      },
      "htmlnano": {}
  }
}

Agora, vamos escrever um script que chama o PostHTML com os caminhos corretos.

A Página Foi Atualizada

O primeiro requisito que descobrimos foi atualizar um arquivo no diretório dist assim que o arquivo src/views correspondente for alterado.

Dentro do seu diretório tools, crie um novo arquivo chamado html-update.js. Ele deve ter a seguinte estrutura:

import { argv } from "node:process";

// Destructuring the Array from Node which includes data we need
const [node, thisFile, srcPath, fileEvent] = argv;

// White-list of events which should cause PostHTML to rebuild pages
const triggerEvents = ['add', 'change'];

// If the wrong kind of event triggers this script, do nothing
if (triggerEvents.includes(fileEvent)) {

  console.log("HTML change detected", srcPath, fileEvent);

}

Basicamente, este é o arquivo image-compress.js, mas com algumas partes removidas.

Edite sua tarefa watch-html no package.json para que agora leia:

"watch-html": "onchange \"src/views\" \"src/fragments\" -- node tools/html-update.js {{file}} {{event}}"

Isso deve soar familiar, pois é muito semelhante ao comando watch-images, até mesmo nos argumentos {{file}} e {{event}}. Agora, execute esta tarefa no terminal:

npm run watch-html

Ele não abrirá uma janela do navegador (porque o serve não está envolvido), mas podemos mexer nos arquivos e ver o que acontece.

Você pode notar que estamos passando um argumento chamado {{file}} via package.json e ele está sendo chamado de srcPath dentro de html-update.js. Isso é normal, pois estamos desestruturando argv em diferentes variáveis, e elas recebem nomes válidos dentro do escopo deste arquivo. O JavaScript não se importa com o que elas eram chamadas antes de chegarem, ele usará o nome que você quiser.

Como estamos lidando com nomes de arquivos de entrada (fonte) e saída (distribuição) aqui, ajustei os nomes das variáveis para refletir isso.

Adicionando uma Nova Pasta

Adicione uma nova pasta dentro de src/views chamada about. Nada aparecerá no terminal porque ele está procurando por novos arquivos html. Pegue uma cópia do seu index.html e coloque-a dentro do diretório about.

O terminal deve reportar isso:

HTML change detected src\views\about\index.html add

Talvez você esteja se perguntando por que criamos uma nova pasta, em vez de apenas um arquivo chamado about.html. Servidores web podem conter vários arquivos diferentes dentro de diretórios, mas o servidor pode ser configurado para procurar um arquivo default. index.html é frequentemente um desses nomes de arquivo padrão. Isso significa que você pode especificar o diretório, mas não precisa especificar o nome do arquivo dentro dele.

Em termos de URLs, esta é a diferença entre:

http://www.mycoolsite.com/about.html

… e:

http://www.mycoolsite.com/about/

O segundo parece melhor, é mais fácil de dizer se alguém está falando sobre seu site em uma festa e (em certa medida) disfarça a tecnologia que você usou para criar o site.

Isso também significa que, se você mudar a tecnologia no futuro, os URLs podem permanecer os mesmos, o que economizará muitas dores de cabeça porque você não precisará configurar redirecionamentos.

A parte srcPath do console.log() parece errada, como de costume. Podemos reutilizar get-dist-path.js e usá-lo para resolver isso! Importe-o no topo de html-update.js:

import getDistPath from "./get-dist-path.js";

Agora, vamos usá-lo como fizemos antes. Substitua seu console.log() por isso:

const { distPath, fileName } = getDistPath(srcPath);
console.log("HTML change detected", srcPath, distPath, fileName);

Lembra de como desestruturamos o objeto antes? Agora, veremos o que recebemos de volta de getDistPath(). Tente renomear src/views/about/index.html para src/views/about/index2.html. Você deve ver isso:

HTML change detected src\views\about\index2.html ./dist/views/about index2

Temos a maior parte da informação aqui, mas não a extensão do arquivo. Isso não era importante quando estávamos lidando com imagens, mas vamos mudar /tools/get-dist-path.js para que ele envie isso também. Felizmente, já existe como uma variável em getDistPath(), então só precisamos atualizar a declaração return de:

return {
  distPath,
  fileName
}

…para:

return {
  distPath,
  fileName,
  extName
}

Mesmo que tenhamos mudado o que getDistPath() retorna, não precisamos alterar image-compress.js. Ele está pegando dados do objeto que getDistPath() retorna e não se importa que tenhamos colocado ainda mais dados dentro.

De volta a html-update.js, atualize o código dentro da sua instrução if para:

const { distPath, fileName, extName } = getDistPath(srcPath);
console.log("HTML change detected", srcPath, distPath, fileName, extName);

Agora, renomeie src/about/index2.html de volta para src/about/index.html e olhe no seu terminal. Você deve ver:

HTML change detected src\views\about\index.html ./dist/views/about index .html

Algumas correções para esses caminhos:

1) O caminho de origem precisa que \ seja substituído por /.

2) O caminho de distribuição precisa que views/ seja removido (este diretório era útil dentro de src para que pudéssemos manter todos os arquivos HTML em um só lugar, mas precisamos misturar as coisas no site ao vivo).

Vamos criar novas variáveis com essas alterações. Depois da sua desestruturação de getDistPath(), adicione algumas novas variáveis:

const { distPath, fileName, extName } = getDistPath(srcPath);
const editedSrcPath = srcPath.replaceAll('\\', '/');
const editedDistPath = distPath.replace('/views', '');

Você pode razoavelmente olhar para o código acima e se perguntar por que não apenas mudamos as variáveis originais, em vez de criar uma nova variável. Como isso:

let { distPath, fileName, extName } = getDistPath(srcPath);
srcPath = srcPath.replaceAll('\\', '/');
distPath = distPath.replace('/views', '');

(precisamos usar let em vez de const porque o valor está mudando)

Este código é desaprovado em alguns cantos da internet porque nunca temos certeza de qual pode ser o valor de srcPath em qualquer momento específico. srcPath é inicializado logo no topo de html-update.js e aqui, mais da metade do caminho para baixo, se torna algo diferente.

Chamar o PostHTML

Precisamos de um novo script que faça para o HTML o que write-images.js faz para as imagens (lembrete: write-image.js chama o Sharp várias vezes e produz imagens diferentes). Crie um novo arquivo chamado call-posthtml.js dentro do diretório tools. Ele deve ter a seguinte estrutura:

import { exec } from 'child_process';

export default function callPostHTML(inputFilePath, outputFilePath) {
  exec(`npx posthtml ${inputFilePath} -o ${outputFilePath} -c posthtml.json`, (err) => {
    if (err) {
      console.error(`exec error: ${err}`);
      return;
    }
  });
};

A função exec nos permite executar comandos no terminal de dentro do JavaScript. É como alcançar fora do script e interagir diretamente com o terminal. Desnecessário dizer que o JavaScript executado fora do Node não pode fazer isso.

Esta é a linha que é executada no terminal:

npx posthtml ${inputFilePath} -o ${outputFilePath} -c posthtml.json

Um rápido lembrete do que isso significa:

  • npx – Nos dá acesso a funções dentro de node_modules.
  • posthtml – Chama o PostHTML de dentro de node_modules.
  • ${inputFilePath} – O JavaScript dentro de call-posthtml.js substituirá isso por uma string que passamos a ele, que representa o caminho para o arquivo de input.
  • -o – Esta flag significa que o caminho seguinte representa o arquivo de output.
  • ${outputFilePath} – O JavaScript dentro de call-posthtml.js substituirá isso por uma string que passamos a ele, que representa o caminho para o arquivo de output.
  • -c – Esta flag significa que o próximo valor representa o arquivo de configuração que gostaríamos de usar com o PostHTML.
  • posthtml.json – Este é o arquivo de configuração que já adicionamos, que configura os plugins usados pelo PostHTML.

A documentação para posthtml-cli nos diz que podemos passar um caminho para o posthtml e ele será assumido como a entrada. Uma saída precisa de uma flag -o antes dela. E uma flag -c aponta para um arquivo de configuração.

A função exec tem uma função de callback que é executada se houver um erro. Vamos apenas registrar o erro no terminal e seguir em frente com nossas vidas:

(err) => {
  if (err) {
    console.error(`exec error: ${err}`);
    return;
  }
}

Finalmente, exportamos callPostHTML para que possamos usá-lo em outro lugar.

Chamando callPostHTML de Dentro de html-update.js

Atualize html-update.js para que ele fique assim:

import { argv } from "node:process";
import getDistPath from "./get-dist-path.js";
import callPostHTML from "./call-posthtml.js";

// Destructuring the Array from Node which includes data we need
const [node, thisFile, srcPath, fileEvent] = argv;

// White-list of events which should cause PostHTML to rebuild pages
const triggerEvents = ['add', 'change'];

// If the wrong kind of event triggers this script, do nothing
if (triggerEvents.includes(fileEvent)) {

  const { distPath, fileName, extName } = getDistPath(srcPath);
  const editedSrcPath = srcPath.replaceAll('\\', '/');
  const editedDistPath = distPath.replace('/views', '');

  // Pass `callPostHTML()` all our paths
  callPostHTML(editedSrcPath, `${editedDistPath}/${fileName}${extName}`);
}

A novidade é que importamos callPostHTML e, em seguida, chamamos com os caminhos corretos, em vez de apenas registrá-los no console.

Cancele e, em seguida, execute novamente a tarefa npm run watch-html no terminal. Agora, exclua o conteúdo de dist e renomeie src/views/index.html para src/views/index2.html. Você deve ver index2.html aparecer dentro do seu diretório dist.

Renomeie-o de volta para index.html. Agora, você deve ver tanto index.html quanto index2.html. Isso está funcionando como esperado!

Ainda há um pouco mais a ser feito aqui:

  • Lidar com as mudanças nos fragmentos.
  • Configurar o diretório dist corretamente para nosso hipotético segundo desenvolvedor.
  • Adicionar conteúdo dentro dos módulos.

Ainda assim, abordamos bastante coisa neste capítulo:

  • Como tornar as imagens em HTML mais dinâmicas.
  • Como passar argumentos para os módulos PostHTML.
  • Replicar nossos métodos com imagens para aplicá-los aos arquivos HTML.
  • Chamar o PostHTML do terminal com entradas e saídas.

Através do atributo srcset e da tag picture, é possível otimizar a exibição de imagens, garantindo que os usuários tenham a melhor experiência visual possível. Além disso, a utilização de fragmentos e módulos no PostHTML permite uma organização mais eficiente do código, facilitando a manutenção e a reutilização de componentes.

Implementar essas técnicas de imagens responsivas em HTML não só melhora a performance do seu site, mas também contribui para uma melhor experiência do usuário, independentemente do dispositivo que ele esteja utilizando. Além disso, a modularização do código com PostHTML facilita a manutenção e a escalabilidade do projeto.

Este conteúdo foi auxiliado por Inteligência Artificiado, mas escrito e revisado por um humano.
Via dev.to

Leave a Comment