A maioria dos sites modernos usa técnicas de web design responsivo para garantir que eles tenham boa aparência, sejam legíveis e permaneçam utilizáveis em dispositivos com qualquer tamanho de tela, ou seja, celulares, tablets, laptops, monitores de PC desktop, televisores, projetores e muito mais.

Os sites que utilizam estas técnicas têm um único modelo, que modifica o layout em resposta às dimensões da tela:

  • Telas menores normalmente mostram uma visão linear, de uma coluna, onde controles de IU como menus são ativados clicando (hambúrguer) nos ícones.
  • Telas maiores mostram mais informações, talvez com barras laterais alinhadas horizontalmente. Controles de IU, como itens de menu, podem sempre estar visíveis para facilitar o acesso.

Uma grande parte do web design responsivo é a implementação de uma media query CSS ou JavaScript para detectar o tamanho do dispositivo e servir automaticamente o design apropriado para esse tamanho. Vamos discutir porque essas consultas são importantes e como trabalhar com elas, mas primeiro, vamos discutir o design responsivo em geral.

Por que o design responsivo é importante?

É impossível fornecer um layout de página única e esperar que funcione em qualquer lugar.

Quando os telefones celulares ganharam acesso rudimentar na web no início dos anos 2000, os proprietários de sites freqüentemente criavam dois ou três modelos de página separados, baseados amplamente para tela dos telefones celulares e desktop. Isso se tornou cada vez mais impraticável à medida que a variedade de dispositivos crescia exponencialmente.

Hoje em dia, existem inúmeros tamanhos de telas, desde pequenas telas de relógio de pulso a enormes monitores de 8K e muito mais. Mesmo se considerarmos apenas os telefones celulares, os dispositivos recentes podem ter uma resolução superior à de muitos portáteis de gama baixa.

O uso de dispositivos móveis também cresceu além do uso de computadores desktop. A menos que o seu site tenha um conjunto específico de usuários, você pode esperar que a maioria das pessoas o acesse a partir de um smartphone. Os dispositivos de tela pequena não são mais um pensamento posterior e devem ser considerados desde o início, apesar da maioria dos web designers, desenvolvedores e clientes continuarem a usar um PC padrão.

O Google reconheceu a importância dos dispositivos móveis. Os sites têm melhor classificação na pesquisa do Google quando são utilizáveis e têm um bom desempenho em um smartphone. Um bom conteúdo permanece vital, mas um site de carregamento lento que não se adapta às dimensões da tela da sua base de usuários pode prejudicar o seu negócio.

Finalmente, considere a acessibilidade. Um site que funcione para todos, não importa o dispositivo que estejam usando, alcançará um público maior. A acessibilidade é um requisito legal em muitos países, mas mesmo que não seja onde você está, considere que mais espectadores levarão a mais conversões e maior rentabilidade.

Como funciona o design responsivo?

A base do design responsivo é o Media Query: uma tecnologia CSS que pode aplicar estilos de acordo com métricas como o tipo de saída (tela, impressora ou mesmo fala), dimensões da tela, relação de aspecto da tela, orientação do dispositivo, profundidade de cor e precisão do ponteiro. Media query também podem levar em conta as preferências do usuário, incluindo animações reduzidas, modo claro/escuro e maior contraste.

Os exemplos que mostramos demonstram media query usando apenas a largura da tela, mas os sites podem ser consideravelmente mais flexíveis. Consulte o conjunto completo de opções no MDN para obter detalhes.

O suporte do media query é excelente e está em navegadores há mais de uma década. Apenas o IE8 e abaixo não têm suporte. Eles ignoram estilos aplicados pelo media query, mas isso às vezes pode ser um benefício (leia mais na seção de Melhores Práticas abaixo).

Há três maneiras padrão de aplicar estilos usando o media query. A primeira carrega stylesheet específicps em código HTML. Por exemplo, a seguinte tag carrega o stylesheet wide.css quando um dispositivo tem uma tela com pelo menos 800 pixels de largura:

<link rel="stylesheet" media="screen and (min-width: 800px)" href="wide.css" />

Em segundo lugar, as stylesheets podem ser condicionalmente carregadas em arquivos CSS usando uma at-rule @import:

/* main.css */
@import url('wide.css') screen and (min-width: 800px);

Mais tipicamente, você aplicará media query no stylesheet usando um bloco @media CSS at-rule block que modifica estilos específicos. Por exemplo, o @media CSS:

/* default styles */
main {
  width: 400px;
}

/* styles applied when screen has a width of at least 800px */
@media screen and (min-width: 800px) {
  main {
    width: 760px;
  }
}

Os desenvolvedores podem aplicar quaisquer regras de media queries  que sejam necessárias para adaptar o layout de um site.

Melhores práticas de media query

Quando media query for concebida pela primeira vez, muitos sites optaram por um conjunto de layouts rigidamente fixados. Isso é conceitualmente mais fácil de projetar e codificar porque efetivamente replica um conjunto limitado de modelos de página. Por exemplo:

  1. Larguras de tela inferiores a 600px utilizam um layout para celulares de 400px de largura.
  2. Larguras de tela entre 600px e 999px usam um layout para tablet de 600px de largura.
  3. Larguras de tela superiores a 1.000px usam um layout para desktop de 1000px de largura.

A técnica é imperfeita. Os resultados em telas muito pequenas e muito grandes podem parecer maus, e a manutenção do CSS pode ser necessária à medida que os dispositivos e os tamanhos das telas mudam com o tempo.

Uma opção melhor é usar um design mobile-first fluido com pontos de ruptura que adaptem o layout em determinados tamanhos. Em essência, o layout padrão utiliza os estilos mais simples de telas pequenas que posicionam os elementos em blocos verticais lineares.

Por exemplo, <article> e <aside> dentro de um contêiner <main>:

/* default small-screen device */
main {
  width: 100%;
}

article, aside {
  width: 100%;
  padding: 2em;
}

Aqui está o resultado em todos os navegadores – mesmo os antigos que não suportam media query:

Exemplo de captura de tela sem suporte a consultas de mídia.
Exemplo de captura de tela sem suporte do media query .

Quando media queries são suportadas e a tela excede uma largura específica, digamos 500px, os elementos <article> e <aside> podem ser posicionados horizontalmente. Este exemplo usa uma grade CSS, onde o conteúdo primário usa aproximadamente dois terços da largura, e o conteúdo secundário usa o restante um terço:

/* larger device */
@media (min-width: 500px) {
  main {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 2em;
  }

  article, aside {
    width: auto;
    padding: 0;
  }
}

Aqui está o resultado em tela maiores:

Exemplo de screenshot com suporte de consulta de mídia
Exemplo de screenshot com suporte media query.

Alternativas de media query

Projetos responsivos também podem ser implementados em CSS modernos utilizando novas propriedades que adaptam intrinsecamente o layout sem examinar as dimensões da janela de exibição. As opções incluem:

  • calc, min-width, max-width, min-height, max-height, e a propriedade mais recente de clamp podem definir as dimensões dos elementos de acordo com os limites conhecidos e o espaço disponível.
  • As unidades da janela de exibição vw, vh, vmin, e vmax podem dimensionar os elementos de acordo com as frações de dimensão da tela.
  • O texto pode ser mostrado em colunas CSS que aparecem ou desaparecem conforme o espaço permitido.
  • Os elementos podem ser dimensionados de acordo com os tamanhos do seu elemento filho, utilizando as dimensões min-content, fit-content, e max-content.
  • CSS flexbox pode embrulhar – ou não embrulhar – elementos à medida que estes começam a exceder o espaço disponível.
  • Os elementos da grade CSS podem ser dimensionados com unidades fr fração proporcional. A função de repetição do VCC pode ser usada em conjunto com minmax, auto-fit, e auto-fill para alocar o espaço disponível.
  • As novas e (atualmente) experimentais consultas de contêineres CSS podem reagir ao espaço parcial disponível para um componente dentro de um layout.

Essas opções estão além do escopo deste artigo, mas muitas vezes são mais práticas do que media query mais brutas, que só podem responder às dimensões da tela. Se você conseguir um layout sem media query , ele provavelmente usará menos código, será mais eficiente e exigirá menos manutenção ao longo do tempo.

Dito isto, há situações em que a media query continua sendo a única opção de layout viável. Elas permanecem essenciais quando você precisa considerar outros fatores de tela, como relações de aspecto, orientação do dispositivo, profundidade de cor, precisão do ponteiro ou preferências do usuário, como animações reduzidas e modo claro/escuro.

Você precisa de media query no JavaScript?

Até agora, temos falado sobretudo do CSS. Isso porque a maioria dos problemas de layout podem – e devem – ser resolvidos apenas no CSS.

Entretanto, há situações em que é prático usar uma media query no JavaScript em vez de CSS, como por exemplo, quando:

  • Um componente, tal como um menu, tem diferentes funcionalidades em telas pequenas e grandes.
  • Mude de e para retrato/paisagem afeta a funcionalidade de um aplicativo web.
  • Um jogo baseado no toque tem de alterar <canvas> layout ou adaptar os botões de controle.
  • Um aplicativo web adere as preferências do usuário, como modo escuro/claro, animação reduzida, rudeza de toque, etc.

As seções seguintes demonstram três métodos que utilizam media query – ou opções similares – em JavaScript. Todos os exemplos retornam uma seqüência de estados onde:

  • vista small (pequeno) = uma tela com uma largura inferior a 400 pixels;
  • vista medium (média) = uma tela com uma largura entre 400 e 799 pixels; e
  • vista large (grande) = um tela com uma largura de 800 pixels ou mais.

Opção 1: Monitore o tamanho da janela de exibição

Esta foi a única opção nos dias sombrios que antecederam a implementação do media query. O JavaScript escutava os eventos de “redimensionamento” do navegador, analisava as dimensões da janela de exibição usando window.innerWidthwindow.innerHeight (ou document.body.clientWidthdocument.body.clientHeight em IEs antigos), e reage em conformidade.

Este código envia a string small, medium ou grande calculada para a console:

const
  screen = {
    small: 0,
    medium: 400,
    large: 800
  };

// observe window resize
window.addEventListener('resize', resizeHandler);

// initial call
resizeHandler();

// calculate size
function resizeHandler() {

  // get window width
  const iw = window.innerWidth;
 
  // determine named size
  let size = null;
  for (let s in screen) {
    if (iw >= screen[s]) size = s;
  }

  console.log(size);
}

Você pode ver uma demonstração do trabalho aqui. (Se estiver usando um navegador desktop, abra este link em uma nova janela para tornar o redimensionamento mais fácil. Os utilizadores de dispositivos móveis podem girar o dispositivo).

O exemplo acima examina o tamanho da janela de exibição a medida que o navegador é redimensionado; determina se é pequeno, médio ou grande; e define isso como uma classe no elemento do corpo, o que muda a cor de fundo.

As vantagens deste método incluem:

  • Funciona em qualquer navegador que possa executar JavaScript – mesmo em aplicativos antigos.
  • Você está capturando as dimensões exatas e pode reagir de acordo.

As desvantagens:

  • É uma técnica antiga que requer um código considerável.
  • É demasiado exato? Você realmente precisa saber quando a largura é 966px vs 967px?
  • Pode ser necessário fazer a correspondência manual das dimensões com uma media query CSS correspondente.
  • Os usuários podem redimensionar o navegador rapidamente, fazendo com que a função de manipulador seja executada novamente a cada vez. Isto pode sobrecarregar navegadores mais antigos e lentos, acelerando o evento. Ele só pode ser acionado uma vez a cada 500 milissegundos.

Em resumo, não monitorize as dimensões da janela de exibição, a menos que tenha requisitos de dimensionamento muito específicos e complexos.

Opção 2: Defina e monitore uma propriedade CSS personalizada (variável)

Esta é uma técnica ligeiramente incomum que altera o valor de uma string de propriedades personalizada no CSS quando uma media query é acionada. As propriedades personalizadas são suportadas em todos os navegadores modernos (mas não no IE).

No exemplo abaixo, a --screen custom property  é definida como “small”, “medium”, ou “large” dentro de um bloco de código @media:

body {
  --screen: "small";
  background-color: #cff;
  text-align: center;
}

@media (min-width: 400px) {
 
  body {
    --screen: "medium";
    background-color: #fcf;
  }
 
}

@media (min-width: 800px) {
 
  body {
    --screen: "large";
    background-color: #ffc;
  }
 
}

O valor pode ser emitido apenas em CSS usando um pseudo-elemento (mas note que ele deve ser contido dentro de aspas simples ou duplas):

p::before {
  content: var(--screen);
}

Você pode obter o valor da propriedade personalizada usando o JavaScript:

const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen');

Esta não é bem a história toda, no entanto, porque o valor retornado contém todos os espaços em branco e personagens de citação definidos após os dois pontos no CSS. A string será ”large”, por isso é necessário um pouco de arrumação:

// returns small, medium, or large in a string
const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen')
                 .replace(/\W/g, '');

Você pode ver uma demonstração do trabalho aqui. (Se estiver usando um navegador desktop, abra este link em uma nova janela para tornar o redimensionamento mais fácil. Os utilizadores de dispositivos móveis podem girar o dispositivo).

O exemplo examina o valor do CSS a cada dois segundos. Ele requer um pequeno código JavaScript, mas é necessário pesquisar por alterações – você não pode detectar automaticamente que o valor da propriedade personalizada foi alterado usando CSS.

Também não é possível escrever o valor em um pseudo-elemento e detectar a mudança usando um DOM Mutation Observer. Pseudo-elementos não são uma parte “real” do DOM!

As vantagens:

  • É uma técnica simples que usa principalmente CSS e combina com consultas reais de mídia.
  • Quaisquer outras propriedades do CSS podem ser modificadas ao mesmo tempo.
  • Não há necessidade de duplicar ou analisar as sequências de media query JavaScript.

A principal desvantagem é que você não pode reagir automaticamente a uma mudança na dimensão do porto de visão do navegador. Se o usuário girar seu telefone de retrato para orientação de paisagem, o JavaScript nunca saberia. Você pode frequentemente sondar por mudanças, mas isso é ineficiente e resulta no intervalo de tempo que você vê em nossa demonstração.

A monitorização das propriedades personalizadas do CSS é uma técnica nova, mas só é prática quando:

  1. O layout pode ser fixado no ponto em que uma página é inicialmente renderizada. Um quiosque ou terminal de ponto de venda é uma possibilidade, mas é provável que estes tenham resoluções fixas e um único layout, por isso o media query JavaScript tornam-se irrelevantes.
  2. O site ou aplicativo já executa funções baseadas no tempo, tais como uma animação de jogo. A propriedade personalizada pode ser verificada ao mesmo tempo para determinar se são necessárias alterações de layout.

Opção 3: Utilize a API matchMedia

A API matchMedia é um pouco incomum, mas permite que você implemente uma media query JavaScript. É suportada na maioria dos navegadores a partir do IE10. O construtor retorna um objeto MediaQueryList que tem uma propriedade de correspondência que avalia como true ou false para sua media query específica.

O seguinte código é true quando a largura da porta de visualização do navegador é de 800px ou superior:

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
console.log( mqLarge.matches );

Um evento de “mudança” pode ser aplicado ao objeto MediaQueryList. Isto é acionado toda vez que o estado da propriedade de partida muda: Torna-se verdade (mais de 800px) depois de anteriormente ser falso (menos de 800px) ou vice-versa.

A função do manipulador receptor é passada para o objeto MediaQueryList como o primeiro parâmetro:

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
mqLarge.addEventListener('change', mqHandler);

// media query handler function
function mqHandler(e) {
 
  console.log(
    e.matches ? 'large' : 'not large'
  );
 
}

O manipulador só funciona quando a propriedade do jogo muda. Ele não será executado quando a página for carregada inicialmente, então você pode chamar a função diretamente para determinar o estado inicial:

// initial state
mqHandler(mqLarge);

A API funciona bem quando você está se movendo entre dois estados distintos. Para analisar três ou mais estados, tais como small, medium e large, será necessário mais código.

Comece definindo um objeto de estado de tela com objetos MatchMedia associados:

const
  screen = {
    small : null,
    medium: window.matchMedia( '(min-width: 400px)' ),
    large : window.matchMedia( '(min-width: 800px)' )
  };

Não é necessário definir um objeto MatchMedia no estado small porque o manipulador de eventos medium acionará quando se mover entre o small e o medium.

Os ouvintes dos eventos podem então ser definidos para os eventos de medium e large porte. Estes chamam a mesma função mqHandler() handler:

// media query change events
for (let [scr, mq] of Object.entries(screen)) {
  if (mq) mq.addEventListener('change', mqHandler);
}

A função de manuseamento deve verificar todos os objetos MediaQueryList para determinar se o small, medium ou large está atualmente ativo. Os jogos devem ser executados por ordem de tamanho, uma vez que uma largura de 999px corresponderia tanto ao medium como ao large – apenas o maior deve “ganhar”:

// media query handler function
function mqHandler() {
 
  let size = null;
  for (let [scr, mq] of Object.entries(screen)) {
    if (!mq || mq.matches) size = scr;
  }
 
  console.log(size);
 
}

Você pode ver uma demonstração do trabalho aqui. (Se estiver usando um navegador desktop, abra este link em uma nova janela para tornar o redimensionamento mais fácil. Os utilizadores de dispositivos móveis podem girar o dispositivo).

Os usos do exemplo são:

  1. Media query no CSS para definir e exibir uma propriedade personalizada (como mostrado na opção 2 acima).
  2. Media query idênticas em bjetos MatchMedia para monitorar mudanças de dimensão em JavaScript. A saída em JavaScript irá mudar exatamente ao mesmo tempo.

As principais vantagens de utilizar a API matchMedia são:

  • É orientado por eventos e eficiente no processamento de mudanças de mídia.
  • Utiliza strings de media query idênticas às do CSS.

As desvantagens:

  • Lidar com duas ou mais media queries requer mais reflexão e lógica de código.
  • Você provavelmente precisará duplicar as sequências de media query tanto em código CSS quanto em JavaScript. Isto pode levar a erros se você não os mantiver em sincronia.

Para evitar desencontros do media query , você poderia considerar o uso de tokens de design em seu sistema de construção. As strings do media query são definidas em um arquivo JSON (ou similar) e os valores são inseridos no código CSS e JavaScript no momento da compilação.

Em resumo, a matchMedia API é provavelmente a forma mais eficiente e prática de implementar uma media query JavaScript. Ela tem algumas peculiaridades, mas é a melhor opção na maioria das situações.

Resumo

As opções de dimensionamento Intrínseco do CSS são cada vez mais viáveis, mas media query continuam sendo a base do web design responsivo para a maioria dos sites. Elas serão sempre necessárias para lidar com layouts mais complexos e preferências do usuário, como o modo claro/escuro.

Tente, sempre que possível, manter as perguntas da mídia apenas para o CSS. Quando você não tem escolha a não ser se aventurar no domínio do JavaScript, a API matchMedia fornece controle adicional para componentes de media query JavaScript, que requerem funcionalidade adicional baseada em dimensões.

Você tem alguma outra dica para implementar uma media query JavaScript? Compartilhe-as na seção de comentários!

Craig Buckler

Freelance UK web developer, writer, and speaker. Has been around a long time and rants about standards and performance.