Como tornar seu site HTML5 compatível com dispositivos móveis

Introdução

O desenvolvimento para a Web para dispositivos móveis é um tema em alta hoje em dia. Este ano, pela primeira vez, os smartphones venderam mais que PCs. Cada vez mais usuários usam dispositivos móveis para navegar na Web, o que significa que os desenvolvedores precisam otimizar os sites para os navegadores móveis.

O campo de batalha "mobile" ainda é um terreno desconhecido para muitos desenvolvedores. Muitas pessoas têm sites legados que não consideram os usuários de dispositivos móveis. Em vez disso, o site foi projetado principalmente para navegação em computadores e tem um desempenho ruim em navegadores para dispositivos móveis. Este site (html5rocks.com) não é uma exceção. No lançamento, não fizemos muito esforço para criar uma versão para dispositivos móveis do site.

Como criar um html5rocks.com compatível com dispositivos móveis

Como exercício, pensei que seria interessante usar o html5rocks (um site HTML5 existente) e adicionar uma versão otimizada para dispositivos móveis. Minha principal preocupação era o trabalho mínimo necessário para segmentar smartphones. O objetivo do meu exercício não era criar um site para dispositivos móveis totalmente novo e manter duas bases de código. Isso teria demorado muito e seria um grande desperdício de tempo. Já havíamos definido a estrutura do site (markup). Analisamos a aparência e a sensação (CSS). A funcionalidade principal (JS) estava lá. O ponto é que muitos sites estão na mesma situação.

Este artigo examina como criamos uma versão para dispositivos móveis do html5rocks otimizada para dispositivos Android e iOS. Basta carregar html5rocks.com em um dispositivo compatível com um desses sistemas operacionais para notar a diferença. Não há redirecionamentos para m.html5rocks.com ou outras páginas desse tipo. Você recebe o html5rocks como está, com o benefício adicional de algo que fica ótimo e funciona bem em um dispositivo móvel.

html5rocks.com para computadores Mobile html5rocks.com
html5rocks.com em computadores (à esquerda) e dispositivos móveis (à direita)

Consultas de mídia CSS

O HTML4 e o CSS2 já oferecem suporte a folhas de estilo dependentes de mídia há algum tempo. Exemplo:

<link rel="stylesheet" media="print" href="printer.css">

segmentaria dispositivos de impressão e forneceria um estilo específico para o conteúdo da página quando ele for impresso. O CSS3 leva a ideia dos tipos de mídia um passo adiante e aprimora a funcionalidade deles com consultas de mídia. As media queries aumentam a utilidade dos tipos de mídia, permitindo a rotulagem mais precisa das folhas de estilo. Isso permite que a apresentação do conteúdo seja personalizada para um intervalo específico de dispositivos de saída sem precisar mudar o conteúdo. Parece perfeito para um layout que precisa ser modificado.

É possível usar consultas de mídia no atributo media das folhas de estilo externas para segmentar a largura da tela, a largura do dispositivo, a orientação etc. Para conferir a lista completa, consulte a especificação de consulta de mídia do W3C (em inglês).

Segmentar por tamanhos de tela

No exemplo abaixo, phone.css se aplica a dispositivos que o navegador considera "portáteis" ou dispositivos com telas de até 320 px de largura.

 <link rel='stylesheet'
  media='handheld, only screen and (max-device-width: 320px)' href='phone.css'>

O prefixo de uma consulta de mídia com a palavra-chave "only" faz com que os navegadores que não são compatíveis com o CSS3 ignorem a regra.

O exemplo a seguir tem como alvo tamanhos de tela entre 641 e 800 pixels:

 <link rel='stylesheet'
  media='only screen and (min-width: 641px) and (max-width: 800px)' href='ipad.css'>

As consultas de mídia também podem aparecer em tags <style> inline. O seguinte define os tipos de mídia all quando estão na orientação retrato:

 <style>
  @media only all and (orientation: portrait) { ... }
 </style>

media="handheld"

Precisamos parar um pouco e discutir media="handheld". O fato é que o Android e o iOS ignoram media="handheld". A alegação é que os usuários vão perder o conteúdo de ponta fornecido pelas folhas de estilo com segmentação para media="screen" e os desenvolvedores têm menos probabilidade de manter uma versão de media="handheld" de qualidade inferior. Assim, como parte do lema "toda a Web", a maioria dos navegadores de smartphones modernos simplesmente ignoram as folhas de estilo para dispositivos portáteis.

Seria ideal usar esse recurso para segmentar dispositivos móveis, mas vários navegadores o implementaram de maneiras diferentes:

  • Alguns leem apenas a folha de estilo do dispositivo portátil.
  • Alguns leem apenas a folha de estilo do dispositivo portátil, se houver uma, mas usam a folha de estilo da tela por padrão.
  • Alguns leem a folha de estilo do dispositivo portátil e a folha de estilo da tela.
  • Alguns leem apenas a folha de estilo da tela.

O Opera Mini não ignora media="handheld". O truque para fazer com que o Windows Mobile reconheça media="handheld" é colocar em maiúsculas o valor do atributo de mídia para a folha de estilo da tela:

 <!-- media="handheld" trick for Windows Mobile -->
 <link rel="stylesheet" href="screen.css" media="Screen">
 <link rel="stylesheet" href="mobile.css" media="handheld">

Como o html5rocks usa consultas de mídia

As consultas de mídia são usadas com frequência em todo o html5rocks para dispositivos móveis. Eles permitiram ajustar o layout sem precisar fazer mudanças significativas na marcação do modelo do Django. Um verdadeiro salva-vidas! Além disso, o suporte em vários navegadores é muito bom.

No <head> de cada página, você vai encontrar as seguintes folhas de estilo:

 <link rel='stylesheet'
  media='all' href='/static/css/base.min.css' />
 <link rel='stylesheet'
  media='only screen and (max-width: 800px)' href='/static/css/mobile.min.css' />

O base.css sempre definiu a aparência principal do html5rocks.com, mas agora estamos aplicando novos estilos (mobile.css) para larguras de tela abaixo de 800 px. A consulta de mídia abrange smartphones (~320 px) e o iPad (~768 px). O efeito: estamos substituindo de forma incremental os estilos em base.css (apenas quando necessário) para melhorar a aparência em dispositivos móveis.

Estas são algumas das mudanças de estilo que mobile.css aplica:

  • Reduz o espaço em branco/padding extra em todo o site. Telas pequenas significam que o espaço é valioso.
  • Remove os estados :hover. Elas nunca serão exibidas em dispositivos com tela touch.
  • Ajusta o layout para uma coluna. Mais detalhes sobre isso mais tarde.
  • Remove o box-shadow ao redor da div do contêiner principal do site. Sombras de caixa grandes reduzem o desempenho da página.
  • Usei o modelo de caixa flexível CSS box-ordinal-group para mudar a ordem de cada seção na página inicial. Você vai notar que a seção "LEARN BY MAJOR HTML5 FEATURE GROUPS" aparece antes da seção "TUTORIALS" na página inicial, mas depois dela na versão para dispositivos móveis. Essa ordem fez mais sentido para dispositivos móveis e não exigiu mudanças na marcação. CSS flexbox FTW!
  • Remove as alterações de opacity. Mudar os valores alfa afeta a performance em dispositivos móveis.

Metatags para dispositivos móveis

O Mobile WebKit oferece alguns recursos que proporcionam aos usuários uma melhor experiência de navegação em determinados dispositivos.

Configurações da janela de visualização

A primeira metadefinição, que você vai usar com mais frequência, é a propriedade de viewport. A configuração de uma viewport informa ao navegador como o conteúdo deve se ajustar à tela do dispositivo e informa ao navegador que o site está otimizado para dispositivos móveis. Exemplo:

 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">

informa ao navegador para definir a janela de visualização como a largura do dispositivo com uma escala inicial de 1. Esse exemplo também permite o zoom, algo que pode ser desejável para um site, mas não para um app da Web. Podemos impedir o zoom com user-scalable=no ou limitar a escala a um determinado nível:

 <meta name=viewport
  content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">

O Android estende a metatag de viewport permitindo que os desenvolvedores especifiquem para qual resolução de tela o site foi desenvolvido:

 <meta name="viewport" content="target-densitydpi=device-dpi">

Os valores possíveis para target-densitydpi são device-dpi, high-dpi, medium-dpi e low-dpi.

Se você quiser modificar sua página da Web para diferentes densidades de tela, use a consulta de mídia CSS -webkit-device-pixel-ratio e/ou a propriedade window.devicePixelRatio no JavaScript. Em seguida, defina a metapropriedade target-densitydpi como device-dpi. Isso impede que o Android faça a escala na sua página da Web e permite que você faça os ajustes necessários para cada densidade, usando CSS e JavaScript.

Consulte a documentação da WebView do Android para mais informações sobre como segmentar resoluções de dispositivos.

Navegação em tela cheia

Há dois outros metavalores que são iOS-sfic. apple-mobile-web-app-capable e apple-mobile-web-app-status-bar-style renderizam o conteúdo da página no modo de tela cheia semelhante a um app e tornam a barra de status translúcida:

 <meta name="apple-mobile-web-app-capable" content="yes">
 <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

Para mais informações sobre todas as metaopções disponíveis, consulte a documentação de referência do Safari.

Ícones da tela inicial

Os dispositivos iOS e Android também aceitam rel="apple-touch-icon" (iOS) e rel="apple-touch-icon-precomposed" (Android) para links. Eles criam um ícone semelhante a um app na tela inicial do usuário quando ele adiciona o site aos favoritos:

 <link rel="apple-touch-icon"
      href="/static/images/identity/HTML5_Badge_64.png" />
 <link rel="apple-touch-icon-precomposed"
      href="/static/images/identity/HTML5_Badge_64.png" />

Como o html5rocks usa metatags para dispositivos móveis

Juntando tudo, aqui está um snippet da seção <head> do html5rocks:

 <head>
  ...
   <meta name="viewport"
        content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />

   <link rel="apple-touch-icon"
        href="/static/images/identity/HTML5_Badge_64.png" />
   <link rel="apple-touch-icon-precomposed"
        href="/static/images/identity/HTML5_Badge_64.png" />
  ...
 </head>

Layout vertical

Em telas menores, é muito mais conveniente rolar verticalmente do que horizontalmente. Para dispositivos móveis, é melhor manter o conteúdo em um layout vertical de coluna única. Para o html5rocks, usamos consultas de mídia CSS3 para criar esse layout. Novamente, sem mudar a marcação.

Índice de tutoriais. Tutorial. Página de recursos do HTML5. Página de perfis de autores.
Layout vertical de coluna única em todo o site.

Otimizações para dispositivos móveis

A maioria das otimizações que fizemos são coisas que deveriam ter sido feitas desde o início. Por exemplo, reduzir o número de solicitações de rede, compactação de JS/CSS, compactação Gzip (disponível sem custo financeiro no App Engine) e minimizar manipulações do DOM. Essas técnicas são práticas recomendadas comuns, mas às vezes são ignoradas quando se apressa para lançar um site.

Ocultar a barra de endereço automaticamente

Os navegadores para dispositivos móveis não têm o espaço na tela da versão para computador. Para piorar, em diferentes plataformas, às vezes você acaba com uma barra de endereço grande na parte de cima da tela, mesmo depois que a página termina de carregar.

Uma maneira fácil de lidar com isso é rolar a página usando JavaScript. Mesmo fazendo isso por um pixel, você vai se livrar da barra de endereço. Para forçar o ocultamento da barra de endereço no html5rocks, anexei um manipulador de eventos onload ao objeto window e rolei a página verticalmente um pixel:

Barra de endereço.
A barra de endereço feia ocupa espaço na tela.
  // Hides mobile browser's address bar when page is done loading.
  window.addEventListener('load', function(e) {
    setTimeout(function() { window.scrollTo(0, 1); }, 1);
  }, false);

Também envolvemos esse listener na nossa variável de modelo is_mobile, já que ele não é necessário no computador.

Reduzir solicitações de rede e economizar largura de banda

É sabido que reduzir o número de solicitações HTTP pode melhorar muito o desempenho. Os dispositivos móveis limitam ainda mais o número de conexões simultâneas que o navegador pode fazer. Portanto, os sites para dispositivos móveis se beneficiam ainda mais com a redução dessas solicitações externas. Além disso, reduzir cada byte é fundamental porque a largura de banda geralmente é limitada em smartphones. Você pode estar fazendo com que os usuários gastem dinheiro.

Confira a seguir algumas das abordagens que adotamos para minimizar as solicitações de rede e reduzir a largura de banda no html5rocks:

  • Remova iframes: eles são lentos. Uma grande parte da nossa latência veio de widgets de compartilhamento de terceiros (Buzz, Google Friend Connect, Twitter, Facebook) nas páginas de tutoriais. Essas APIs foram incluídas com tags <script> e criam iframes que diminuem a velocidade da página. Os widgets foram removidos para dispositivos móveis.

  • display:none: em alguns casos, estávamos ocultando a marcação se ela não se encaixasse no perfil para dispositivos móveis. Um bom exemplo são as quatro caixas arredondadas na parte de cima da página inicial:

Botões de caixa na página inicial.
Botões do Box na página inicial.

Elas não estão no site móvel. É importante lembrar que o navegador ainda faz uma solicitação para cada ícone, mesmo que o contêiner esteja oculto com display:none. Portanto, não bastava esconder esses botões. Isso não apenas desperdiçaria largura de banda, mas o usuário nem mesmo veria os frutos dessa largura de banda desperdiçada. A solução foi criar um booleano "is_mobile" no nosso modelo Django para omitir condicionalmente seções de HTML. Quando o usuário está visualizando o site em um dispositivo inteligente, os botões não são exibidos.

  • Cache do aplicativo: além de oferecer suporte off-line, ele também acelera a inicialização.

  • Compressão de CSS/JS: estamos usando o compressor YUI em vez do Closure compiler principalmente porque ele processa CSS e JS. Um problema que encontramos foi que as consultas de mídia inline (que aparecem dentro de uma folha de estilo) foram geradas no compressor YUI 2.4.2. Confira este problema. O uso do YUI Compressor 2.4.4 ou mais recente corrigiu o problema.

  • Usar sprites de imagem CSS sempre que possível.

  • O pngcrush foi usado para compactação de imagens.

  • Usamos dataURIs para imagens pequenas. A codificação Base64 adiciona cerca de 30%do tamanho à imagem, mas salva a solicitação de rede.

  • A pesquisa personalizada do Google foi carregada automaticamente usando uma única tag de script em vez de ser carregada dinamicamente com google.load(). O segundo faz uma solicitação extra.

<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"> </script>
  • Nossa impressora de código e o Modernizr estavam sendo incluídos em todas as páginas, mesmo que nunca fossem usados. O Modernizr é ótimo, mas ele executa vários testes em cada carregamento. Alguns desses testes fazem modificações caras no DOM e diminuem a velocidade de carregamento da página. Agora, só incluímos essas bibliotecas nas páginas em que elas são realmente necessárias. -2 solicitações :)

Outros ajustes de desempenho:

  • Todos os JS foram movidos para a parte de baixo da página (quando possível).
  • As tags <style> inline foram removidas.
  • Pesquisas de DOM em cache e manipulações de DOM minimizadas: sempre que você toca no DOM, o navegador executa um reflow. O reflow é ainda mais caro em um dispositivo móvel.
  • O código do lado do cliente que desperdiça recursos foi transferido para o servidor. Especificamente, a verificação para definir o estilo de navegação da página atual: js var lis = document.querySelectorAll('header nav li'); var i = lis.length; while (i--) { var a = lis[i].querySelector('a'); var section = a.getAttribute("data-section"); if (new RegExp(section).test(document.location.href)) { a.className = 'current'; } }
  • Os elementos com larguras fixas foram substituídos por width:100% ou width:auto fluidos.

Cache do aplicativo

A versão para dispositivos móveis do html5rocks usa o Application Cache para acelerar o carregamento inicial e permite que os usuários leiam conteúdo off-line.

Ao implementar o AppCache no seu site, é muito importante que você não armazene em cache o arquivo de manifesto (explicitamente no próprio arquivo de manifesto ou implicitamente com cabeçalhos de controle de cache pesados). Se o manifesto for armazenado em cache pelo navegador, será um pesadelo depurar. O iOS e o Android fazem um bom trabalho de armazenamento em cache desse arquivo, mas não oferecem uma maneira conveniente de limpar o cache, como os navegadores para computador.

Para evitar esse armazenamento em cache no nosso site, primeiro definimos o App Engine para nunca armazenar arquivos de manifesto em cache:

- url: /(.*\.(appcache|manifest))
  static_files: \1
  mime_type: text/cache-manifest
  upload: (.*\.(appcache|manifest))
  expiration: "0s"

Em segundo lugar, usamos a API JS para informar ao usuário quando o download de um novo manifesto é concluído. O usuário é solicitado a atualizar a página:

window.applicationCache.addEventListener('updateready', function(e) {
  if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
    window.applicationCache.swapCache();
    if (confirm('A new version of this site is available. Load it?')) {
      window.location.reload();
    }
  }
}, false);

Para economizar o tráfego de rede, mantenha o manifesto simples. Ou seja, não indique todas as páginas do seu site. Basta listar as imagens, os arquivos CSS e JavaScript importantes. A última coisa que você quer fazer é forçar o navegador para dispositivos móveis a fazer o download de um grande número de recursos em todas as atualizações do appcache. Em vez disso, lembre-se de que o navegador armazenará em cache uma página HTML quando o usuário a visitar (e incluir um atributo <html manifest="...">).