Uma abordagem não responsiva para criar aplicativos da Web entre dispositivos

Consultas de mídia são ótimas, mas...

As consultas de mídia são incríveis. Uma ajuda para os desenvolvedores de sites que querem fazer pequenos ajustes nas folhas de estilo para oferecer uma experiência melhor aos usuários em dispositivos de vários tamanhos. Essencialmente, as consultas de mídia permitem personalizar o CSS do site dependendo do tamanho da tela. Antes de se aprofundar neste artigo, saiba mais sobre o design responsivo e confira alguns bons exemplos de uso de consultas de mídia aqui: mediaqueri.es.

Como Brad Frost aponta em um artigo anterior, mudar a aparência é apenas uma das muitas coisas a serem consideradas ao criar para a Web para dispositivos móveis. Se a única coisa que você fizer ao criar o site para dispositivos móveis for personalizar o layout com consultas de mídia, teremos esta situação:

  • Todos os dispositivos recebem os mesmos JavaScript, CSS e recursos (imagens, vídeos), o que resulta em um tempo de carregamento mais longo que o necessário.
  • Todos os dispositivos recebem o mesmo DOM inicial, o que pode forçar os desenvolvedores a criar um CSS excessivamente complicado.
  • Pouca flexibilidade para especificar interações personalizadas para cada dispositivo.

Apps da Web precisam de mais do que consultas de mídia

Não me entenda mal. Não odeio design responsivo com consultas de mídia e definitivamente acho que ele existe no mundo. Além disso, alguns dos problemas mencionados acima podem ser resolvidos com abordagens como imagens responsivas, carregamento de script dinâmico etc. No entanto, em um determinado momento, talvez você precise fazer muitos ajustes incrementais, e talvez seja melhor disponibilizar versões diferentes.

À medida que as IUs criadas aumentam de complexidade e você passa por apps da Web de página única, convém fazer mais para personalizar as IUs para cada tipo de dispositivo. Neste artigo, você vai aprender a fazer essas personalizações com o mínimo de esforço. A abordagem geral envolve classificar o dispositivo do visitante na classe de dispositivo correta e disponibilizar a versão adequada para esse dispositivo, maximizando a reutilização de código entre as versões.

Quais classes de dispositivo você está segmentando?

Há toneladas de dispositivos conectados à Internet por aí, e quase todos têm navegadores. A complicação está na diversidade: laptops Mac, estações de trabalho Windows, iPhones, iPads, smartphones Android com entrada por toque, rodas de rolagem, teclados, entrada por voz, dispositivos com sensibilidade à pressão, smartwatches, torradeiras e geladeiras e muito mais. Alguns desses dispositivos são onipresentes, enquanto outros são muito raros.

Vários dispositivos.
Vários dispositivos (fonte).

Para criar uma boa experiência do usuário, você precisa saber quem são seus usuários e quais dispositivos eles usam. Se você criar uma interface para um usuário de computador com mouse e teclado e fornecê-la a um usuário de smartphone, sua interface vai ser uma frustração, porque foi projetada para outro tamanho de tela e outra modalidade de entrada.

Há duas extremidades no espectro de abordagens:

  1. Crie uma versão que funcione em todos os dispositivos. A UX será prejudicada como resultado, já que dispositivos diferentes têm considerações de design distintas.

  2. Crie uma versão para cada dispositivo que queira oferecer suporte. Isso demorará sempre, porque você criará muitas versões do aplicativo. Além disso, quando o próximo novo smartphone chegar (o que acontece aproximadamente uma vez por semana), você será forçado a criar mais uma versão.

Há uma troca fundamental aqui: quanto mais categorias de dispositivos você tiver, melhor será a experiência do usuário que você pode oferecer, mas mais trabalho será necessário para projetar, implementar e manter.

Criar uma versão separada para cada classe de dispositivo que você decidir pode ser uma boa ideia por motivos de desempenho ou se as versões que você quer disponibilizar para diferentes classes de dispositivo variam muito. Fora isso, o Web design responsivo é uma abordagem perfeitamente razoável.

Uma possível solução

Aqui está um compromisso: classificar dispositivos em categorias e projetar a melhor experiência possível para cada categoria. As categorias que você escolhe dependem do produto e do usuário-alvo. Veja aqui um exemplo de classificação que abrange bem os populares dispositivos habilitados para web que existem hoje.

  1. telas pequenas + toque (principalmente telefones)
  2. telas grandes + toque (principalmente tablets)
  3. telas grandes + teclado/mouse (principalmente computadores/laptops)

Esse é apenas um dos muitos detalhamentos possíveis, mas faz muito sentido no momento em que este artigo foi escrito. Estão faltando na lista acima os dispositivos móveis sem touchscreen (por exemplo, celulares simples e alguns leitores dedicados de e-books). No entanto, a maioria deles tem navegação pelo teclado ou software leitor de tela instalado, que funcionará bem se você criar o site com a acessibilidade em mente.

Exemplos de apps da Web com formatos específicos

Há muitos exemplos de propriedades da Web que veiculam versões totalmente diferentes para formatos distintos. A Pesquisa Google e o Facebook fazem isso. Isso inclui a performance (busca de recursos, renderização de páginas) e uma experiência do usuário mais geral.

No mundo dos apps nativos, muitos desenvolvedores optam por adaptar a experiência para uma classe de dispositivo. Por exemplo, o Flipboard para iPad tem uma IU muito diferente em comparação com o Flipboard no iPhone. A versão para tablet é otimizada para o uso com duas mãos e para virar na horizontal, enquanto a versão para smartphones se destina à interação com uma mão e a uma virada vertical. Muitos outros aplicativos iOS também oferecem versões significativamente diferentes para smartphones e tablets, como Coisas (lista de tarefas) e Showyou (vídeo social), apresentadas abaixo:

Personalização significativa da interface para smartphones e tablets.
Personalização significativa da interface para smartphones e tablets.

Abordagem no 1: detecção do lado do servidor

No servidor, temos um entendimento muito mais limitado do dispositivo com que estamos lidando. Provavelmente, a pista mais útil disponível é a string do user agent, fornecida pelo cabeçalho do user agent em todas as solicitações. Por isso, a mesma abordagem de detecção do UA funcionará aqui. Na verdade, os projetos DeviceAtlas e WURFL já fazem isso e fornecem muitas outras informações sobre o dispositivo.

Infelizmente, cada um deles apresenta seus próprios desafios. O WURFL é muito grande, contendo 20 MB de XML, o que pode gerar uma sobrecarga significativa do lado do servidor para cada solicitação. Há projetos que dividem o XML por motivos de desempenho. O DeviceAtlas não é de código aberto e requer uma licença paga para uso.

Há também alternativas mais simples e sem custo financeiro, como o projeto Detect Mobile Browsers. A desvantagem, obviamente, é que a detecção do dispositivo será inevitavelmente menos abrangente. Além disso, diferencia apenas dispositivos móveis e não móveis, fornecendo suporte limitado para tablets apenas por meio de um conjunto ad-hoc de ajustes.

Abordagem no 2: detecção do lado do cliente

Podemos aprender muito sobre o navegador e o dispositivo do usuário usando a detecção de recursos. As principais coisas que precisamos determinar são se o dispositivo tem o recurso de toque e se é uma tela grande ou pequena.

Precisamos desenhar a linha em algum lugar para distinguir dispositivos de toque pequenos e grandes. E os casos extremos como o Galaxy Note de 5 polegadas? O gráfico a seguir mostra vários dispositivos Android e iOS conhecidos sobrepostos (com as resoluções de tela correspondentes). O asterisco indica que o dispositivo vem ou pode vir em densidade dupla. A densidade de pixels pode ser duplicada, mas o CSS ainda informa os mesmos tamanhos.

Um comentário rápido sobre pixels em CSS: os pixels CSS na Web para dispositivos móveis não são iguais que os pixels de tela. Os dispositivos iOS com tela Retina introduziram a prática de dobrar a densidade de pixels (por exemplo, iPhone 3GS vs. 4, iPad 2 vs. 3). Os UAs do Safari para dispositivos móveis com tela Retina ainda informam a mesma largura de dispositivo para evitar falhar na Web. Conforme outros dispositivos (por exemplo, Android) têm telas de resolução mais alta, eles estão fazendo o mesmo truque de largura de dispositivo.

Resolução do dispositivo (em pixels).
Resolução do dispositivo (em pixels).

No entanto, complicar essa decisão é a importância de considerar os modos retrato e paisagem. Não queremos atualizar a página nem carregar scripts adicionais toda vez que reorientar o dispositivo, embora seja possível renderizar a página de maneira diferente.

No diagrama a seguir, os quadrados representam as dimensões máximas de cada dispositivo, como resultado da sobreposição dos contornos de retrato e paisagem e do preenchimento do quadrado:

Resolução retrato + paisagem (em pixels)
Resolução retrato + paisagem (em pixels)

Ao definir o limite como 650px, classificamos o iPhone, o Galaxy Nexus como smalltouch e o iPad e o Galaxy Tab como "tablet". Neste caso, o andrógino Galaxy Note está classificado como "telefone" e receberá o layout de telefone.

Assim, uma estratégia razoável seria:

if (hasTouch) {
  if (isSmall) {
    device = PHONE;
  } else {
    device = TABLET;
  }
} else {
  device = DESKTOP;
}

Veja um exemplo mínimo da abordagem de detecção de recursos em ação.

A abordagem alternativa aqui é usar a detecção de UA para detectar o tipo de dispositivo. Basicamente, você cria um conjunto de heurísticas e faz a correspondência delas com a navigator.userAgent do usuário. O pseudocódigo é semelhante a este:

var ua = navigator.userAgent;
for (var re in RULES) {
  if (ua.match(re)) {
    device = RULES[re];
    return;
  }
}

Veja um exemplo da abordagem de detecção de UA em ação.

Uma observação sobre o carregamento do lado do cliente

Se você estiver fazendo a detecção do UA no seu servidor, poderá decidir qual CSS, JavaScript e DOM atender quando receber uma nova solicitação. No entanto, se você estiver fazendo a detecção do lado do cliente, a situação será mais complexa. Você tem várias opções:

  1. Redirecione para um URL específico do tipo de dispositivo que contém a versão dele.
  2. Carregue dinamicamente os recursos específicos do tipo de dispositivo.

A primeira abordagem é simples, que exige um redirecionamento como window.location.href = '/tablet'. No entanto, o local terá essas informações de tipo de dispositivo anexadas, então use a API History para limpar seu URL. Infelizmente, essa abordagem envolve um redirecionamento, que pode ser lento, especialmente em dispositivos móveis.

A segunda abordagem é um pouco mais complexa de implementar. Você precisa de um mecanismo para carregar dinamicamente CSS e JS e, dependendo do navegador, talvez não seja possível personalizar o <meta viewport>, por exemplo. Além disso, como não há redirecionamento, você ficará preso no HTML original que foi exibido. Claro, é possível manipulá-lo com JavaScript, mas isso pode ser lento e/ou deselegante, dependendo do aplicativo.

Como decidir entre cliente ou servidor

Confira abaixo as compensações entre as abordagens:

Cliente profissional:

  • Mais preparada para o futuro, com base nos tamanhos/recursos da tela, e não no UA.
  • Não é necessário atualizar constantemente a lista de UAs.

Pro Server:

  • Controle total de qual versão veicular para quais dispositivos.
  • Melhor desempenho: não há necessidade de redirecionamentos do cliente ou carregamento dinâmico.

Minha preferência pessoal é começar com a detecção do device.js e do lado do cliente. À medida que seu aplicativo evolui, se você achar que o redirecionamento do lado do cliente é uma desvantagem significativa para o desempenho, remova facilmente o script device.js e implemente a detecção do UA no servidor.

Introdução ao device.js

O Device.js é um ponto de partida para fazer a detecção de dispositivos semântica e baseada em consultas de mídia sem precisar de uma configuração especial do lado do servidor, economizando o tempo e o esforço necessários para analisar a string do user agent.

A ideia é que você forneça uma marcação otimizada para mecanismos de pesquisa (link rel=alternate) na parte de cima do <head>, indicando quais versões do site você quer fornecer.

<link rel="alternate" href="http://foo.com" id="desktop"
    media="only screen and (touch-enabled: 0)">

Em seguida, é possível fazer a detecção de UA do lado do servidor e processar o redirecionamento de versão por conta própria ou usar o script device.js para fazer o redirecionamento do lado do cliente com base em recursos.

Para mais informações, consulte a página do projeto device.js e também um aplicativo falso que usa o device.js para redirecionamento do lado do cliente.

Recomendação: MVC com visualizações específicas para formatos

Agora você provavelmente está pensando que estou dizendo para criar três apps completamente separados, um para cada tipo de dispositivo. Não! O compartilhamento de código é a chave.

Esperamos que você esteja usando um framework semelhante a MVC, como Backbone, Ember etc. Se já usou, você conhece o princípio da separação de conceitos, especificamente que a IU (camada de visualização) precisa estar dissociada da sua lógica (camada de modelo). Se você ainda não tem muita experiência com isso, comece com alguns destes recursos no MVC e em MVC em JavaScript.

A história entre dispositivos se encaixa perfeitamente na sua estrutura de MVC existente. Você pode mover facilmente as visualizações para arquivos separados, criando uma visualização personalizada para cada tipo de dispositivo. Assim, você poderá disponibilizar o mesmo código para todos os dispositivos, exceto para a camada de visualização.

MVC entre dispositivos.
MVC entre dispositivos.

O projeto pode ter a estrutura a seguir. É claro que você pode escolher a estrutura que faz mais sentido dependendo do seu aplicativo:

model/ (modelos compartilhados) item.js item-collection.js

controladores/ (controladores compartilhados) item-controller.js

versões/ (coisas específicas do dispositivo) tablet/ desktop/ smartphone/ (código específico para smartphone) style.css index.html visualizações/ item.js item-list.js

Esse tipo de estrutura permite controlar totalmente quais recursos cada versão carrega, já que há HTML, CSS e JavaScript personalizados para cada dispositivo. Isso é muito eficiente e pode levar à maneira mais enxuta e com melhor desempenho de desenvolvimento para a Web entre dispositivos, sem depender de truques como imagens adaptáveis.

Depois de executar sua ferramenta de build favorita, você vai concatenar e reduzir todo o JavaScript e CSS em arquivos únicos para um carregamento mais rápido, com o HTML de produção semelhante ao seguinte (para smartphones, usando device.js):

<!doctype html>
<head>
  <title>Mobile Web Rocks! (Phone Edition)</title>

  <!-- Every version of your webapp should include a list of all
        versions. -->
  <link rel="alternate" href="http://foo.com" id="desktop"
      media="only screen and (touch-enabled: 0)">
  <link rel="alternate" href="http://m.foo.com" id="phone"
      media="only screen and (max-device-width: 650px)">
  <link rel="alternate" href="http://tablet.foo.com" id="tablet"
      media="only screen and (min-device-width: 650px)">

  <!-- Viewport is very important, since it affects results of media
        query matching. -->
  <meta name="viewport" content="width=device-width">

  <!-- Include device.js in each version for redirection. -->
  <script src="device.js"></script>

  <link rel="style" href="phone.min.css">
</head>
<body>
  <script src="phone.min.js"></script>
</body>

A consulta de mídia (touch-enabled: 0) não é padrão (implementada apenas no Firefox com um prefixo de fornecedor moz), mas é processada corretamente (graças ao Modernizr.touch) pelo device.js.

Substituição da versão

Às vezes, a detecção de dispositivos pode dar errado e, em alguns casos, um usuário pode preferir o layout do tablet no smartphone (talvez esteja usando um Galaxy Note). Por isso, é importante oferecer aos usuários a opção de qual versão do site usar para fazer a substituição manual.

A abordagem mais comum é fornecer um link da versão para computador na versão para dispositivos móveis. Isso é fácil de implementar, mas o device.js é compatível com essa funcionalidade com o parâmetro GET device.

Conclusão

Para resumir, ao criar IUs de uma única página para vários dispositivos que não se encaixam bem no mundo do design responsivo, faça o seguinte:

  1. Escolha um conjunto de classes de dispositivos para oferecer suporte e critérios para classificar dispositivos em classes.
  2. Crie seu aplicativo MVC com forte separação de problemas, dividindo as visualizações do restante da base de código.
  3. Use device.js para fazer a detecção da classe do dispositivo no lado do cliente.
  4. Quando estiver tudo pronto, empacote o script e as folhas de estilo em um de cada classe de dispositivo.
  5. Se o desempenho de redirecionamento do lado do cliente for um problema, abandone o device.js e mude para a detecção de UA do lado do servidor.