As consultas de mídia são ótimas, mas…
As consultas de mídia são ótimas, uma bênção para 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. As consultas de mídia permitem que você personalize o CSS do seu site dependendo do tamanho da tela. Antes de mergulhar neste artigo, saiba mais sobre o design responsivo e confira alguns exemplos de uso de consultas de mídia aqui: mediaqueri.es (em inglês).
Como Brad Frost aponta em um artigo anterior, mudar a aparência é apenas uma das muitas coisas a considerar ao criar para a Web para dispositivos móveis. Se a única coisa que você faz ao criar seu site para dispositivos móveis é personalizar o layout com consultas de mídia, temos a seguinte situação:
- Todos os dispositivos recebem os mesmos JavaScripts, CSSs e recursos (imagens, vídeos), resultando em tempos de carregamento mais longos do que o necessário.
- Todos os dispositivos recebem o mesmo DOM inicial, o que pode forçar os desenvolvedores a escrever CSSs muito complicados.
- Pouca flexibilidade para especificar interações personalizadas adaptadas a cada dispositivo.
Os apps da Web precisam de mais do que consultas de mídia
Não me interpretem mal. Eu não odeio o design responsivo com consultas de mídia, e acho que ele tem um lugar 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 determinado momento, você pode estar fazendo muitos ajustes incrementais e pode ser melhor exibir versões diferentes.
À medida que as interfaces que você cria aumentam de complexidade e você se inclina para aplicativos da Web de página única, é necessário personalizar mais as interfaces para cada tipo de dispositivo. Este artigo ensina como fazer essas personalizações com o mínimo de esforço. A abordagem geral envolve classificar o dispositivo do visitante na classe certa e veicular a versão adequada a ele, maximizando a reutilização de código entre as versões.
Para quais classes de dispositivo você está direcionando?
Há muitos dispositivos conectados à Internet, e quase todos têm navegadores. A complicação está na diversidade deles: laptops Mac, estações de trabalho Windows, iPhones, iPads, smartphones Android com entrada táctil, rodas de rolagem, teclados, entrada por voz, dispositivos com sensibilidade à pressão, relógios inteligentes, torradeiras e geladeiras, entre outros. Alguns desses dispositivos são onipresentes, enquanto outros são muito raros.
Para criar uma boa experiência do usuário, você precisa saber quem são seus usuários e quais dispositivos eles estão usando. Se você criar uma interface do usuário para um usuário de computador com um mouse e um teclado e entregá-la a um usuário de smartphone, a interface será frustrante porque foi projetada para outro tamanho de tela e outra modalidade de entrada.
Há dois extremos no espectro de abordagens:
Crie uma versão que funcione em todos os dispositivos. Como resultado, a UX será afetada, já que diferentes dispositivos têm considerações de design diferentes.
Crie uma versão para cada dispositivo que você quer oferecer suporte. Isso vai levar para sempre, porque você vai criar muitas versões do aplicativo. Além disso, quando o próximo smartphone chegar (o que acontece aproximadamente uma vez por semana), você será forçado a criar outra versão.
Há uma troca fundamental aqui: quanto mais categorias de dispositivos você tem, melhor é a experiência do usuário que pode ser oferecida, mas mais trabalho será necessário para projetar, implementar e manter.
Criar uma versão separada para cada classe de dispositivo pode ser uma boa ideia por motivos de performance ou se as versões que você quer veicular para diferentes classes de dispositivos variam muito. Caso contrário, o design responsivo da Web é uma abordagem perfeitamente razoável.
Uma possível solução
Aqui está um compromisso: classifique os dispositivos em categorias e crie a melhor experiência possível para cada categoria. As categorias que você escolher dependem do seu produto e do usuário-alvo. Confira um exemplo de classificação que abrange os dispositivos com capacidade de navegação mais conhecidos atualmente.
- telas pequenas e touch (principalmente smartphones)
- telas grandes e touch (principalmente tablets)
- telas grandes + teclado/mouse (principalmente computadores/laptops)
Essa é apenas uma das muitas falhas possíveis, mas faz muito sentido no momento da escrita. Os dispositivos móveis sem tela touchscreen (por exemplo, feature phones e alguns leitores de e-books especiais) não estão na lista acima. No entanto, a maioria deles tem navegação por teclado ou leitor de tela instalado, o que vai funcionar bem se você criar seu site com foco na acessibilidade.
Exemplos de apps da Web específicos para formato
Há muitos exemplos de propriedades da Web que veiculam versões totalmente diferentes para diferentes formatos. A Pesquisa Google faz isso, assim como o Facebook. As considerações incluem a performance (buscar recursos, renderizar páginas) e a experiência do usuário mais geral.
No mundo dos apps nativos, muitos desenvolvedores optam por adaptar a experiência a uma classe de dispositivo. Por exemplo, o Flipboard para iPad tem uma interface muito diferente em comparação com o Flipboard no iPhone. A versão para tablet é otimizada para uso com as duas mãos e virada horizontalmente, enquanto a versão para smartphone é destinada à interação com uma mão e a uma virada vertical. Muitos outros aplicativos para iOS também oferecem versões de smartphones e tablets significativamente diferentes, como Things (lista de tarefas) e Showyou (vídeo social), mostrados abaixo:
Abordagem 1: detecção do lado do servidor
No servidor, temos uma compreensão muito mais limitada do dispositivo com que estamos lidando. Provavelmente, a dica mais útil disponível é a string do user agent, que é fornecida pelo cabeçalho User-Agent em cada solicitação. Por isso, a mesma abordagem de sniffing do UA vai funcionar aqui. Na verdade, os projetos DeviceAtlas e WURFL já fazem isso (e fornecem muitas outras informações sobre o dispositivo).
Infelizmente, cada uma delas apresenta seus próprios desafios. O WURFL é muito grande, contendo 20 MB de XML, o que pode gerar uma sobrecarga significativa no servidor para cada solicitação. Há projetos que dividem o XML por motivos de desempenho. O DeviceAtlas não é de código aberto e exige uma licença paga para uso.
Também há alternativas mais simples e sem custo financeiro, como o projeto Detect Mobile Browsers. A desvantagem, é claro, é que a detecção de dispositivos será inevitavelmente menos abrangente. Além disso, ele só distingue entre dispositivos móveis e não móveis, oferecendo suporte limitado a tablets apenas por meio de um conjunto ad hoc de ajustes.
Abordagem 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 capacidade de toque e se a tela é grande ou pequena.
Precisamos definir uma linha para distinguir dispositivos pequenos e grandes. E quanto a casos extremos, como o Galaxy Note de 5 polegadas? O gráfico a seguir mostra vários dispositivos Android e iOS conhecidos sobrepostos (com resoluções de tela correspondentes). O asterisco indica que o dispositivo tem ou pode ter densidade dupla. Embora a densidade de pixels possa ser duplicada, o CSS ainda informa os mesmos tamanhos.
Uma observação rápida sobre pixels no CSS: os pixels do CSS na Web para dispositivos móveis não são os mesmos que os pixels da tela. Os dispositivos retina do iOS 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 retina ainda informam a mesma largura do dispositivo para evitar quebras na Web. Como outros dispositivos (por exemplo, Android) têm telas de resolução mais alta, elas usam o mesmo truque de largura do dispositivo.
No entanto, o que complica essa decisão é a importância de considerar os modos retrato e paisagem. Não queremos recarregar a página ou carregar scripts adicionais sempre que reorientarmos o dispositivo, mas talvez queiramos 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 quadrado completo):
Ao definir o limite como 650px
, classificamos o iPhone e o Galaxy Nexus como
smalltouch e o iPad e o Galaxy Tab como "tablet". O Galaxy Note
androgino é classificado como "smartphone" e vai receber o layout
de smartphone.
Uma estratégia razoável pode ser assim:
if (hasTouch) {
if (isSmall) {
device = PHONE;
} else {
device = TABLET;
}
} else {
device = DESKTOP;
}
Confira um exemplo mínimo da abordagem de detecção de recursos em ação.
A abordagem alternativa é usar a detecção de UA para detectar o tipo de
dispositivo. Basicamente, você cria um conjunto de heurísticas e as compara com
o navigator.userAgent
do usuário. O pseudocódigo tem esta aparência:
var ua = navigator.userAgent;
for (var re in RULES) {
if (ua.match(re)) {
device = RULES[re];
return;
}
}
Confira um exemplo da abordagem de detecção do UA em ação.
Observação sobre o carregamento do lado do cliente
Se você estiver fazendo a detecção do UA no servidor, poderá decidir quais CSS, JavaScript e DOM serão veiculados 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:
- Redireciona para um URL específico do tipo de dispositivo que contém a versão para esse tipo de dispositivo.
- Carregar dinamicamente os recursos específicos do tipo de dispositivo.
A primeira abordagem é simples, exigindo um redirecionamento, como
window.location.href = '/tablet'
. No entanto, o local agora terá
essas informações de tipo de dispositivo anexadas a ele. Portanto, 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. Dependendo do navegador, talvez
não seja possível fazer coisas como personalizar <meta viewport>
. Além disso,
como não há redirecionamento, você fica preso ao HTML original que foi
enviado. É claro que você pode manipulá-lo com JavaScript, mas isso pode
ser lento e/ou não muito elegante, dependendo do seu aplicativo.
Como decidir entre cliente e servidor
Confira as compensações entre as abordagens:
Cliente profissional:
- Mais preparado para o futuro, já que é baseado em tamanhos/recursos de tela, e não no UA.
- Não é necessário atualizar constantemente a lista de UA.
Servidor profissional:
- Controle total sobre qual versão é veiculada para quais dispositivos.
- Melhor desempenho: não é necessário redirecionamento do cliente ou carregamento dinâmico.
Minha preferência pessoal é começar com device.js e a detecção do lado do cliente. À medida que o 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 no servidor, economizando o tempo e o esforço necessários para fazer a análise da string do user agent.
A ideia é fornecer uma marcação compatível com mecanismos de pesquisa (link
rel=alternate) na parte de cima do <head>
indicando quais
versões do site você quer oferecer.
<link rel="alternate" href="http://foo.com" id="desktop"
media="only screen and (touch-enabled: 0)">
Em seguida, você pode fazer a detecção do 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 no recurso.
Para mais informações, consulte a página do projeto device.js e um aplicativo falso que usa device.js para redirecionamento do lado do cliente.
Recomendação: MVC com visualizações específicas do formato
Até 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 ao MVC, como Backbone, Ember etc. Se estiver, você já conhece o princípio da separação de preocupações, especificamente que a interface (camada de visualização) precisa ser separada da lógica (camada de modelo). Se você é iniciante, comece com alguns desses recursos sobre MVC e MVC em JavaScript.
A história entre dispositivos se encaixa perfeitamente no seu framework MVC. Você pode mover suas visualizações facilmente para arquivos separados, criando uma visualização personalizada para cada tipo de dispositivo. Em seguida, você pode exibir o mesmo código para todos os dispositivos, exceto a camada de visualização.
Seu projeto pode ter a seguinte estrutura (é claro, você pode escolher a estrutura que fizer mais sentido dependendo da aplicação):
models/ (shared models) item.js item-collection.js
controllers/ (shared controllers) item-controller.js
versions/ (stuff specific to the device) tablet/ desktop/ phone/ (phone-specific code) style.css index.html views/ item.js item-list.js
Esse tipo de estrutura permite controlar totalmente os recursos carregados por cada versão, já que você tem HTML, CSS e JavaScript personalizados para cada dispositivo. Isso é muito poderoso e pode levar à maneira mais simples e eficiente de desenvolver para a Web em vários dispositivos, sem depender de truques como imagens adaptativas.
Depois de executar sua ferramenta de build favorita, você vai concatenar e reduzir todos os arquivos JavaScript e CSS em arquivos únicos para um carregamento mais rápido, com o HTML de produção parecido com o seguinte (para smartphone, 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 (apenas
implementada no Firefox com um prefixo de fornecedor moz
), mas é processada
corretamente (graças ao Modernizr.touch) pelo device.js.
Substituição de versão
A detecção de dispositivos às vezes pode dar errado. Em alguns casos, um usuário pode preferir o layout do tablet no smartphone (talvez ele esteja usando um Galaxy Note). Portanto, é importante permitir que os usuários escolham qual versão do site usar se quiserem substituir manualmente.
A abordagem usual é fornecer um link para a versão para computador da versão
para dispositivos móveis. Isso é fácil de implementar, mas o device.js oferece suporte
a essa funcionalidade com o parâmetro GET device
.
Conclusão
Para resumir, ao criar interfaces de página única entre dispositivos que não se encaixam bem no mundo do design responsivo, faça o seguinte:
- Escolha um conjunto de classes de dispositivos para oferecer suporte e os critérios para classificar os dispositivos em classes.
- Crie seu app MVC com uma forte separação de preocupações, separando as visualizações do restante da base de código.
- Use device.js para fazer a detecção da classe do dispositivo do lado do cliente.
- Quando estiver tudo pronto, empacote o script e as folhas de estilo em uma de cada classe de dispositivo.
- Se a performance do redirecionamento do lado do cliente for um problema, abandone o device.js e mude para a detecção de UA do lado do servidor.