Skip to content
This page has been auto-translated and may contain errors.View in English

Design responsivo

docs.scrimba.com

Você constrói uma página no seu laptop, fica ótima, e então alguém abre no celular. As colunas que você cuidadosamente alinhava agora estão comprimidas numa tira, o texto sai da borda, e uma barra de rolagem horizontal aparece onde não deveria haver nenhuma. Ninguém criou uma versão separada do site para telefone. Há uma página, e ela precisa parecer certa num celular de 380 pixels e num monitor de 1400 pixels sem você enviar duas versões de tudo. Essa capacidade de adaptação é o design responsivo, e a maioria dele é construída com as ferramentas que você já tem.

O que responsivo significa e a viewport

Uma página responsiva é uma página que se reformula para caber em qualquer tela em que apareça. O mesmo HTML e o mesmo CSS servem um telefone, um tablet e um desktop, e o layout se ajusta para que nada transborde e nada fique apertado. Você não está construindo três sites. Você está construindo um que se curva.

Antes de qualquer coisa funcionar, o navegador precisa de uma instrução no HTML. Telefones costumavam fingir que eram da largura do desktop e encolhiam a página toda para caber, o que é por que sites antigos apareciam como uma bagunça minúscula e afastada. Esta linha, que fica no <head> do seu HTML, diz ao telefone para usar sua largura real em vez disso:

html
<meta name="viewport" content="width=device-width, initial-scale=1" />

Sem isso, seu CSS está estilizando uma tela larga imaginária e seu layout cuidadoso nunca tem uma chance.

Design responsivo significa um código único que adapta seu layout ao tamanho da viewport, a área visível da página dentro da janela do navegador. Há um conjunto de HTML e uma folha de estilos; o que muda é como essa folha de estilos responde ao espaço que recebe. A alternativa, sites separados para celular e desktop, duplica sua manutenção e é por que a indústria optou por responsivo anos atrás.

O requisito inicial é a meta tag viewport no head do HTML. Ela define a largura contra a qual o navegador faz o layout para ser a largura real do dispositivo, e impede que navegadores móveis renderizem numa largura de desktop falsa e reduzam a escala:

html
<meta name="viewport" content="width=device-width, initial-scale=1" />

width=device-width alinha a viewport de layout para a tela, e initial-scale=1 define o zoom inicial para 100%. Deixe isto fora e nenhuma quantidade de CSS correto salvará o layout, porque o navegador está medindo contra a largura errada.

Design responsivo é a prática de construir um documento cujo layout se adapta a uma gama contínua de tamanhos de viewport, em vez de focar em algumas telas fixas. A viewport é a região que o navegador usa para fazer o layout e pintar a página, e sua largura é o valor contra o qual toda unidade relativa, percentual e media query acaba se resolvendo. Acerte essa largura errado e tudo que vem depois está medindo a coisa errada.

É exatamente isso que a meta tag viewport previne. Navegadores móveis historicamente distinguiam uma viewport de layout (a largura contra a qual o CSS faz layout) de uma viewport visual (o que realmente está na tela), definindo a viewport de layout para cerca de 980px para que sites antigos apenas para desktop renderizassem e então reduzissem para caber. A meta tag fecha essa lacuna:

html
<meta name="viewport" content="width=device-width, initial-scale=1" />

width=device-width vincula a viewport de layout à largura de pixel CSS do dispositivo, e initial-scale=1 fixa o zoom inicial para que as duas viewports começem alinhadas. Pertence ao head do HTML, e é uma precondição, não uma benesse: sem isso o navegador avalia suas media queries e percentuais contra aproximadamente 980px num celular de 390px, então uma query min-width: 40rem dispara quando não deveria e o layout que você testou nunca aparece. Tudo neste capítulo assume que essa tag está presente.

JunoO que responsivo significa e a viewport Uma página responsiva é uma página que se reformula para caber em qualquer tela, de telefone a desktop, a partir de um único conjunto de HTML e CSS. Antes do seu CSS ter uma chance justa, o head do HTML precisa da meta tag viewport, que diz aos telefones para usar sua largura real em vez de fingir ser um desktop. Deixe-a de fora e o layout parece quebrado não importa o quão bom seja o CSS.
JunoO que responsivo significa e a viewport Responsivo significa um código base que se adapta à viewport, a área de página visível, não construções móveis e desktop separadas. A meta tag viewport no head do HTML é o requisito inicial: width=device-width faz o navegador fazer layout contra a largura real da tela. Pule-a e suas media queries disparam contra uma largura de desktop falsa, então CSS correto ainda parece errado.
JunoO que responsivo significa e a viewport Cada percentual e media query se resolve contra a largura da viewport, então a meta tag viewport é uma precondição em vez de uma benesse. width=device-width, initial-scale=1 vincula a viewport de layout à largura real do dispositivo em vez do padrão legado de 980px. Esqueça-a num telefone e uma query min-width: 40rem dispara quando não deveria, e o layout que você testou nunca renderiza.

Layouts fluidos primeiro

O reflexo é alcançar regras de telefone especiais imediatamente, mas muita responsividade vem de graça antes de você escrever uma sequer. Se você construir com valores fluidos, aqueles que se estendem e encolhem com o espaço, o layout já se adapta.

Dois hábitos carregam a maioria. Defina larguras como percentuais para que uma caixa tome uma parte de seu contêiner em vez de um número fixo de pixels, e use max-width para que algo possa encolher numa tela pequena mas nunca crescer além de um tamanho de leitura confortável numa grande:

css
.container {
  width: 90%;              /* ocupa 90% do espaço que tem */
  max-width: 60rem;        /* mas nunca mais largo que 60rem em telas grandes */
  margin: 0 auto;          /* centrado */
}

Num telefone este contêiner é 90% de uma tela estreita. Num monitor ele para de crescer em 60rem e fica centrado. Você não escreveu uma regra para telefone; os valores fizeram a adaptação.

Antes de media queries, construa o layout fora de valores fluidos para que ele se flexione por si próprio. Unidades relativas e percentuais deixam elementos tomar uma parte de seu contêiner em vez de um tamanho fixo, e flexbox e grid já refluem seus filhos conforme o espaço muda. Um bom layout fluido precisa de muito menos pontos de quebra do que um rígido, porque não está lutando contra a tela em primeiro lugar.

Alcance media queries apenas depois que o layout fluido para de dar conta. Percentuais e max-width cuidam da largura, e um padrão de grid cuidam do caso comum de parede de cartões sem nenhuma query:

css
.card-grid {
  display: grid;
  gap: 1.5rem;
  /* quantas colunas couberem com mín de 16rem, dividindo espaço restante igualmente */
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

repeat(auto-fit, minmax(16rem, 1fr)) diz "encaixe quantas colunas você conseguir com mínimo de 16rem cada, então estique-as para preencher a linha". Três colunas numa tela larga, duas num tablet, uma num telefone, e nenhuma media query. Quais unidades relativas preferir para estes valores é coberto em unidades de dimensionamento.

Os layouts responsivos mais fortes são na maioria fluidos antes de uma única media query ser escrita, porque um ponto de quebra é um salto discreto e a maioria do trabalho é contínuo. Percentuais, max-width, e o dimensionamento intrínseco construído em flexbox e grid deixam o layout rastrear a viewport suavemente, e media queries então cuidam apenas dos pontos onde o design realmente precisa mudar de forma, não cada pixel de redimensionamento no meio.

O padrão que vale a pena é o grid auto-ajustável, porque ele compacta um conjunto inteiro de pontos de quebra numa declaração:

css
.card-grid {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

minmax(16rem, 1fr) dá a cada trilha um piso de 16rem e um teto de uma parte igual do espaço livre, e auto-fit cria quantas trilhas couberem naquele piso, então as expande para preencher a linha. A contagem de colunas sai da largura do contêiner automaticamente, então um layout que teria levado três pontos de quebra leva nenhum. Uma nota de precisão que vale a pena levar: auto-fit colapsa trilhas vazias para que os itens presentes se estiquem para preencher a linha, enquanto auto-fill mantém as trilhas vazias reservadas, então escolha auto-fit quando você quer itens crescerem no espaço restante e auto-fill quando você quer um grid estável. Veja unidades de dimensionamento para como estes valores rem e fr se resolvem.

JunoLayouts fluidos primeiro Muita responsividade é gratuita antes de você escrever qualquer regra especial: use percentuais para que caixas tomem uma parte do seu espaço, e max-width para que encolham em telefones sem se estender muito largo em monitores. Estes dois valores adaptam o layout por si próprios. Construa fluido primeiro, e você precisará de muito menos das regras específicas para telefone que vêm a seguir.
JunoLayouts fluidos primeiro Construa fluido antes de construir pontos de quebra: percentuais e max-width cuidam da largura, e flexbox e grid já refluem por si próprios. A linha de grid a lembrar é repeat(auto-fit, minmax(16rem, 1fr)), que lhe dá uma parede de cartões responsiva com zero media queries. Alcance uma query apenas uma vez que o layout fluido realmente pare de dar conta.
JunoLayouts fluidos primeiro Um ponto de quebra é um salto discreto, mas redimensionar é contínuo, então deixe percentuais, max-width, e dimensionamento flex e grid intrínsecos carregar a parte suave e economize media queries para mudanças de forma real. repeat(auto-fit, minmax(16rem, 1fr)) compacta vários pontos de quebra numa linha. Lembre-se que auto-fit colapsa trilhas vazias para estender seus itens, enquanto auto-fill as mantém reservadas.

Media queries

Às vezes valores fluidos não são o suficiente e você precisa que o layout realmente mude em certo tamanho, por exemplo um menu que fica empilhado verticalmente num telefone mas se senta numa linha num desktop. É para isso que uma media query serve: um bloco de CSS que só se aplica quando a tela atende a uma condição que você define.

Comece com os estilos para tela pequena como seu CSS normal, depois adicione uma query que sobreponha as mudanças para tela mais larga:

css
.nav {
  display: flex;
  flex-direction: column;   /* empilhado em telas pequenas */
}

@media (min-width: 40rem) {
  .nav {
    flex-direction: row;    /* lado a lado quando há espaço */
  }
}

A regra dentro de @media (min-width: 40rem) só liga uma vez que a tela tem pelo menos 40rem de largura. Abaixo disso, a regra simples .nav se aplica e o menu fica empilhado.

Uma media query envolve um conjunto de regras numa condição, mais frequentemente um teste de largura, e essas regras se aplicam apenas quando a condição vale. O padrão a adotar é mobile-first: escreva seus estilos base para a tela mais pequena sem nenhuma query, depois sobreponha melhorias para cima com min-width.

css
.layout {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;      /* base: coluna única, telas pequenas */
}

@media (min-width: 40rem) {
  .layout {
    grid-template-columns: 1fr 1fr; /* duas colunas quando há espaço */
  }
}

Mobile-first importa por mais do que organização. Seus estilos base são aqueles que sempre se aplicam, então fazer deles o layout mais simples e confiável significa que os dispositivos mais pequenos e mais restritos recebem a experiência mais segura por padrão, e cada query apenas adiciona capacidade. Construir do outro jeito, desktop-first com queries max-width que tiram coisas, tende a deixar a visualização móvel como um detalhe posterior que quebra.

Uma media query aplica um bloco de regras apenas quando uma condição de mídia avalia como verdadeira, e para layout responsivo essa condição é quase sempre um teste de largura de viewport. A disciplina que se mantém é mobile-first: estilos base incondicionais descrevem o layout estreito, e cada query min-width é puramente aditiva sobre eles.

css
.layout {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;       /* base, se aplica em todo lugar */
}

@media (min-width: 40rem) {         /* tablet e acima */
  .layout { grid-template-columns: 1fr 1fr; }
}

@media (min-width: 64rem) {         /* desktop e acima */
  .layout { grid-template-columns: 1fr 2fr 1fr; }
}

A razão de preferir min-width aditivo sobre max-width subtrativo é a cascata. Porque estas queries compartilham especificidade, aquelas disparadas depois sobrescrevem as anteriores, então ordená-las de pequeno a grande significa cada ponto de quebra mais largo ganha limpo onde se aplica. Duas precisões que vale a pena segurar: prefira rem sobre px na condição para que o ponto de quebra rastreie o tamanho de fonte do usuário, e saiba que queries de largura leem a viewport de layout, que é apenas correto porque a meta tag viewport a prendeu à largura do dispositivo. Uma query max-width desgarrada no meio de uma pilha min-width é a causa usual de uma regra que misteriosamente para de se aplicar, porque está lutando contra a ordenação em vez de estendê-la.

JunoMedia queries Uma media query é um bloco de CSS que só liga quando a tela atende uma condição, como ter pelo menos 40rem de largura. Escreva seus estilos para tela pequena como CSS normal primeiro, então use @media (min-width: ...) para adicionar as mudanças para telas maiores. Dessa forma a visualização de telefone é seu ponto de partida, não um remendo aparafusado depois.
JunoMedia queries Media queries aplicam regras apenas quando uma condição, geralmente uma largura, vale. Vá mobile-first: estilos base sem query para a tela mais pequena, depois suba em camadas com min-width para que cada query apenas adicione. Construa do outro jeito com max-width e a visualização móvel tende a terminar o detalhe posterior quebrado.
JunoMedia queries Mobile-first significa estilos base incondicionais para o layout estreito e queries aditivos min-width empilhadas de pequeno a grande, então a cascata resolve cada ponto de quebra limpo. Use rem na condição para que rastreie o tamanho de fonte do usuário, e lembre-se queries de largura leem a viewport de layout que a meta tag prendeu. Uma única max-width numa pilha min-width é a razão clássica de uma regra ficar silenciosamente sem se aplicar.

Escolhendo pontos de quebra

Um ponto de quebra é a largura onde uma media query liga e o layout muda. A forma tentadora de escolher um é procurar a largura de um telefone ou tablet popular e coincidir com ele. Essa é a armadilha. Há centenas de tamanhos de dispositivo, eles mudam todo ano, e persegui-los é um jogo que você não pode ganhar.

A pergunta melhor é: em que largura este layout começa a parecer ruim? Amplie seu navegador lentamente e observe. Quando uma linha de texto fica muito comprida para ler confortavelmente, ou duas colunas ficam muito estreitas, é onde um ponto de quebra pertence, qualquer número que seja.

css
/* Ponto de quebra escolhido porque o texto ficou muito largo aqui, */
/* não porque um telefone acaso tenha este tamanho */
@media (min-width: 45rem) {
  .article { max-width: 38rem; }
}

Um ponto de quebra é a largura da viewport na qual você muda o layout. Escolha-os observando seu próprio conteúdo, não combinando nomes de dispositivos. Números redondos como "768px porque é um iPad" parecem autoritários e são uma armadilha: o cenário de dispositivos muda constantemente, e um layout ajustado para os tamanhos populares de ano passado quebra nos deste ano.

Deixe o conteúdo decidir. Redimensione a janela e adicione um ponto de quebra na largura onde o design realmente se esforça, uma linha ficando muito comprida, um cartão ficando muito apertado, uma navegação saindo do espaço. Você geralmente precisará apenas de dois ou três em todo um site:

css
/* Baseado em conteúdo: esta linha chegou a ~75 caracteres e ficou difícil ler */
@media (min-width: 42rem) {
  .prose { max-width: 65ch; }   /* limite a medida uma vez que há espaço */
}

/* Baseado em conteúdo: o layout de barra lateral só ganha seu espaço aqui */
@media (min-width: 60rem) {
  .page { grid-template-columns: 16rem 1fr; }
}

Um ponto de quebra é onde o design muda de forma, e a maneira durável de escolher um é deixar o conteúdo defini-lo em vez de uma folha de especificações de dispositivo. Combinar larguras de dispositivos nomeados é frágil por uma razão estrutural: a população de tamanhos de tela é grande e flutua todo ano, então qualquer layout vinculado a dimensões de dispositivo específicas está vinculado a um alvo em movimento e apodrece sob cronograma. Um ponto de quebra justificado pelo conteúdo, "a medida passou um comprimento de linha legível aqui", permanece justificado independentemente do hardware que for enviado a seguir.

Na prática isso significa redimensionar até que o layout se esforce e colocar um ponto de quebra lá, e significa que a maioria dos sites precisa apenas de um pequeno punhado:

css
/* Limite a medida onde linhas cruzam um comprimento legível, ~65 caracteres */
@media (min-width: 42rem) {
  .prose { max-width: 65ch; }
}

/* Introduza a barra lateral apenas onde a coluna principal pode poupar a largura */
@media (min-width: 60rem) {
  .page { grid-template-columns: 16rem 1fr; }
}

O movimento avançado é depender de clamp() para tipo e espaçamento fluido para que você precise de menos pontos de quebra no geral. clamp(min, preferred, max) escolhe um valor que escala com a viewport mas é limitado nos dois extremos, então títulos e lacunas crescem suavemente em vez de pular em cada query:

css
h1 {
  /* nunca abaixo de 1.75rem, nunca acima de 3rem, fluido no meio */
  font-size: clamp(1.75rem, 4vw + 1rem, 3rem);
}

Isso cobre a maioria do escalonamento tipográfico sem nenhum ponto de quebra; veja unidades de dimensionamento para como o termo vw se resolve e tipografia para o lado da escala de tipo. Três ferramentas relacionadas completam isso. Container queries estilizam um componente pela largura de seu próprio contêiner em vez da viewport, então um cartão pode reagir à coluna em que se senta em vez de toda a página, que é a contrapartida baseada em componente para media queries baseadas em viewport. A query prefers-reduced-motion deixa você cortar animação para leitores que pedem seu sistema reduzir, e prefers-color-scheme deixa você honrar uma preferência de luz ou escuridão do sistema, ambas escritas como media queries:

css
@media (prefers-reduced-motion: reduce) {
  * { animation: none; transition: none; }  /* respeite a configuração do SO */
}

@media (prefers-color-scheme: dark) {
  body { background: #111; color: #eee; }
}

Para imagens especificamente, responsividade se move para o HTML: o atributo srcset deixa o navegador escolher um arquivo apropriadamente dimensionado por tela, que o capítulo de imagens responsivas do HTML cobre em vez de CSS.

JunoEscolhendo pontos de quebra Um ponto de quebra é a largura onde seu layout muda, e a armadilha é escolhê-lo para combinar um telefone ou tablet específico. Há muitos tamanhos de dispositivo para perseguir, e eles mudam todo ano. Amplie seu navegador lentamente e adicione um ponto de quebra onde o layout realmente começa a parecer ruim, qualquer número que seja.
JunoEscolhendo pontos de quebra Deixe o conteúdo escolher seus pontos de quebra, não nomes de dispositivos: uma linha ficando muito comprida ou um cartão ficando muito apertado é o sinal real, e larguras de dispositivo redondas como 768px são um alvo em movimento que apodrece. Redimensione até que o design se esforce, e coloque o ponto de quebra lá. A maioria dos sites precisa apenas de dois ou três em toda a coisa.
JunoEscolhendo pontos de quebra Pontos de quebra baseados em conteúdo sobrevivem porque a população de tela flutua todo ano, então qualquer coisa vinculada a dimensões de dispositivo apodrece sob cronograma. Dependa de clamp() para tipo e espaçamento fluido para precisar de menos pontos de quebra no geral, mantenha container queries em mente para estilo no nível de componente em vez de viewport, e honre prefers-reduced-motion e prefers-color-scheme. Para imagens, a responsividade fica no HTML srcset, não CSS.