Un enfoque no responsivo para compilar apps web para dispositivos múltiples

Las consultas de medios son geniales, pero...

Las consultas de medios son geniales, un gran beneficio para los desarrolladores de sitios web que quieren hacer pequeños ajustes en sus hojas de estilo con el fin de brindar una mejor experiencia a los usuarios en dispositivos de varios tamaños. En esencia, las consultas de medios te permiten personalizar el CSS de tu sitio en función del tamaño de la pantalla. Antes de profundizar en este artículo, obtén más información sobre el diseño responsivo y revisa algunos ejemplos útiles del uso de consultas de medios en mediaqueri.es.

Como señala Brad Frost en un artículo anterior, cambiar el aspecto es solo una de las muchas cosas que debes tener en cuenta a la hora de compilar contenido para la Web móvil. Si lo único que haces cuando creas un sitio web móvil es personalizar el diseño con consultas de medios, tenemos la siguiente situación:

  • Todos los dispositivos obtienen el mismo código JavaScript y CSS, y los mismos elementos (imágenes y videos), lo que genera tiempos de carga más largos de lo necesario.
  • Todos los dispositivos obtienen el mismo DOM inicial, lo que podría obligar a los desarrolladores a escribir código CSS demasiado complicado.
  • Poca flexibilidad para especificar interacciones personalizadas adaptadas a cada dispositivo.

Las apps web necesitan más que solo consultas de medios

No me malinterpretes. No odio el diseño adaptable mediante consultas de medios y definitivamente creo que tiene un lugar en el mundo. Además, algunos de los problemas mencionados anteriormente se pueden resolver con enfoques como las imágenes responsivas, la carga dinámica de secuencias de comandos, etc. Sin embargo, en un momento determinado, es posible que hagas demasiados ajustes incrementales y que sea mejor entregar diferentes versiones.

A medida que aumente la complejidad de las IUs que compiles y te gusten las apps web de una sola página, te recomendamos que realices más acciones para personalizar las IUs para cada tipo de dispositivo. En este artículo, aprenderás a realizar estas personalizaciones con un esfuerzo mínimo. El enfoque general consiste en clasificar el dispositivo del visitante en la clase de dispositivo correcta y entregar la versión adecuada en ese dispositivo, a la vez que se maximiza la reutilización del código entre versiones.

¿A qué clases de dispositivos deseas segmentar tus anuncios?

Existen muchos dispositivos conectados a Internet y casi todos tienen navegador. La complicación reside en su diversidad: laptops Mac, estaciones de trabajo de Windows, iPhones, iPads, teléfonos Android con entrada táctil, ruedas de desplazamiento, teclados, entrada de voz, dispositivos con sensibilidad a la presión, relojes inteligentes, tostadoras y refrigeradores, y muchos más. Algunos de estos dispositivos son ubicuos, mientras que otros son muy raros.

Una variedad de dispositivos
Una variedad de dispositivos (fuente).

Para crear una buena experiencia del usuario, debes saber quiénes son tus usuarios y qué dispositivos usan. Si compilas una interfaz de usuario para un usuario de computadora de escritorio con un mouse y un teclado y se la entregas a un usuario de smartphone, la interfaz será frustrante, ya que está diseñada para otro tamaño de pantalla y otra modalidad de entrada.

Hay dos extremos extremos en el espectro de enfoques:

  1. Crea una versión que funcione en todos los dispositivos. La UX se verá afectada, ya que los diferentes dispositivos tienen diferentes consideraciones de diseño.

  2. Compila una versión para cada dispositivo que desees admitir. Esto llevará por siempre, ya que compilarás demasiadas versiones de tu aplicación. Además, cuando llegue el próximo smartphone (lo cual sucede casi todas las semanas), deberás crear otra versión.

Hay una desventaja fundamental: cuantas más categorías de dispositivos tengas, mejor será la experiencia del usuario que podrás ofrecer, pero cuanto más trabajo requerirá el diseño, la implementación y el mantenimiento.

Crear una versión independiente para cada clase de dispositivo que elijas puede ser una buena idea por razones de rendimiento o si las versiones que quieres entregar a diferentes clases de dispositivos varían mucho. Por lo demás, el diseño web responsivo es un enfoque perfectamente razonable.

Una posible solución

Aquí tienes un compromiso: clasifica los dispositivos en categorías y diseña la mejor experiencia posible para cada una. Las categorías que elijas dependerán del producto y del usuario objetivo. A continuación, se incluye una clasificación de muestra que abarca muy bien los dispositivos populares con capacidad web que existen en la actualidad.

  1. pantallas pequeñas y táctiles (principalmente teléfonos)
  2. pantallas grandes + pantalla táctil (principalmente tablets)
  3. Pantallas grandes + teclado o mouse (principalmente computadoras de escritorio y laptops)

Este es solo uno de los numerosos desgloses posibles, pero tiene mucho sentido al momento de escribirlo. En la lista anterior, faltan los dispositivos móviles sin pantallas táctiles (p. ej., teléfonos de gama media y algunos lectores de libros electrónicos dedicados). Sin embargo, la mayoría tiene instalado el software de navegación con teclado o de lector de pantalla, lo que funcionará bien si compilas el sitio teniendo en cuenta la accesibilidad.

Ejemplos de aplicaciones web para factores de forma específicos

Hay muchos ejemplos de propiedades web que entregan versiones completamente diferentes para distintos factores de forma. La Búsqueda de Google y Facebook. Se debe tener en cuenta el rendimiento (recuperación de recursos y renderización de páginas) y una experiencia del usuario más general.

En el mundo de las apps nativas, muchos desarrolladores eligen adaptar su experiencia a una clase de dispositivo. Por ejemplo, Flipboard para iPad tiene una IU muy diferente en comparación con Flipboard en iPhone. La versión para tablet está optimizada para usarse con dos manos y girar horizontalmente, mientras que la versión para teléfono está diseñada para la interacción con una sola mano y el giro vertical. Muchas otras aplicaciones para iOS también proporcionan versiones significativamente diferentes para teléfonos y tablets, como Things (lista de tareas pendientes) y Showyou (video para redes sociales), que se muestran a continuación:

Personalización significativa de la IU para teléfonos y tablets.
Personalización de la IU significativa para teléfonos y tablets

Enfoque n.o 1: Detección del servidor

En el servidor, tenemos una comprensión mucho más limitada del dispositivo con el que estamos trabajando. Probablemente, la pista más útil disponible sea la string del usuario-agente, que se proporciona a través del encabezado de usuario-agente en cada solicitud. Por lo tanto, el mismo enfoque de análisis de UA funcionará aquí. De hecho, los proyectos DeviceAtlas y WURFL ya hacen esto (y proporcionan mucha información adicional sobre el dispositivo).

Lamentablemente, cada una de ellas presenta sus propios desafíos. WURFL es muy grande y contiene 20 MB de XML, lo que podría incurrir en una sobrecarga significativa del servidor para cada solicitud. Hay proyectos que dividen el XML por motivos de rendimiento. DeviceAtlas no es de código abierto y requiere una licencia pagada para su uso.

También hay alternativas más simples y gratuitas, como el proyecto Detecta navegadores para dispositivos móviles. La desventaja, por supuesto, es que la detección de dispositivos inevitablemente será menos completa. Además, solo distingue entre dispositivos móviles y no móviles, lo que proporciona compatibilidad limitada con tablets solo mediante un conjunto ad hoc de ajustes.

Enfoque n° 2: Detección del cliente

Podemos aprender mucho sobre el navegador y el dispositivo del usuario mediante la detección de funciones. Lo principal que debemos determinar es si el dispositivo tiene capacidad táctil y si se trata de una pantalla grande o pequeña.

Debemos trazar la línea en algún lugar para distinguir los dispositivos táctiles pequeños y grandes. ¿Y las fundas perimetrales como el Galaxy Note de 5"? En el siguiente gráfico, se muestran varios dispositivos iOS y Android populares superpuestos (con las resoluciones de pantalla correspondientes). El asterisco indica que el dispositivo viene o puede tener el doble de densidad. Aunque la densidad de píxeles puede duplicarse, CSS informa los mismos tamaños.

Una aclaración rápida sobre los píxeles en CSS: Los píxeles de CSS en la Web móvil no son lo mismo que los píxeles de pantalla. En los dispositivos con tecnología Retina iOS, se duplicó la densidad de píxeles (p. ej., iPhone 3GS frente a 4, iPad 2 frente a 3). Los UA de Safari para dispositivos móviles con tecnología Retina siguen informando el mismo ancho de dispositivo para evitar romper la Web. Como otros dispositivos (p. ej., Android) obtienen pantallas de mayor resolución, hacen el mismo truco del ancho del dispositivo.

Resolución del dispositivo (en píxeles).
Resolución del dispositivo (en píxeles).

Sin embargo, para complicar esta decisión, es importante tener en cuenta los modos vertical y horizontal. No queremos volver a cargar la página ni cargar secuencias de comandos adicionales cada vez que reorientamos el dispositivo, aunque es posible que debamos renderizar la página de otra manera.

En el siguiente diagrama, los cuadrados representan las dimensiones máximas de cada dispositivo como resultado de superponer los contornos vertical y horizontal (y completar el cuadrado):

Resolución vertical y horizontal (en píxeles)
Resolución vertical y horizontal (en píxeles)

Cuando estableces el umbral en 650px, clasificamos iPhone, Galaxy Nexus como Smalltouch y iPad, Galaxy Tab como "tablet". En este caso, la Galaxy Notes andrógina se clasifica como "teléfono", y obtendrá el diseño de teléfono.

Por lo tanto, una estrategia razonable podría verse de la siguiente manera:

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

Observa una muestra mínima del enfoque de detección de funciones en acción.

El enfoque alternativo es utilizar el análisis de UA para detectar el tipo de dispositivo. Básicamente, debes crear un conjunto de heurísticas y compararlas con la navigator.userAgent de tu usuario. El pseudocódigo se ve de la siguiente manera:

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

Consulta una muestra del enfoque de detección de UA en acción.

Nota sobre la carga del cliente

Si realizas la detección de UA en tu servidor, puedes decidir qué CSS, JavaScript y DOM entregar cuando recibes una solicitud nueva. Sin embargo, si realizas la detección del cliente, la situación es más compleja. Tienes varias opciones:

  1. Redireccionar a una URL específica del tipo de dispositivo que contenga la versión para este tipo de dispositivo
  2. Carga los recursos específicos para un tipo de dispositivo de forma dinámica.

El primer enfoque es sencillo y requiere un redireccionamiento como window.location.href = '/tablet'. Sin embargo, la ubicación ahora tendrá esta información de tipo de dispositivo adjunta, por lo que te recomendamos que uses la API de History para limpiar tu URL. Lamentablemente, este enfoque implica un redireccionamiento, que puede ser lento, especialmente en dispositivos móviles.

El segundo enfoque es un poco más complejo de implementar. Necesitas un mecanismo para cargar CSS y JS de forma dinámica y, según el navegador, es posible que no puedas realizar acciones como personalizar <meta viewport>. Además, como no hay redireccionamiento, te quedas con el HTML original que se entregó. Puedes manipularlo con JavaScript, por supuesto, aunque esto puede ser lento o poco elegante, según tu aplicación.

Decidir el cliente o el servidor

Estas son las compensaciones entre los enfoques:

Cliente Pro:

  • Ofrecen mayor protección para el futuro, ya que se basan en capacidades y tamaños de pantalla en vez de en UA.
  • No es necesario actualizar constantemente la lista de UA.

Servidor Pro:

  • Tiene control total sobre qué versión entregar en cada dispositivo.
  • Mejor rendimiento, sin necesidad de redireccionamientos de cliente ni carga dinámica

Prefiero comenzar con la detección de device.js y del cliente. A medida que tu aplicación evoluciona, si consideras que el redireccionamiento del cliente es un inconveniente de rendimiento significativo, puedes quitar con facilidad la secuencia de comandos device.js y, luego, implementar la detección de UA en el servidor.

Presentación de device.js

Device.js es un punto de partida para realizar detección de dispositivos semántica y basada en consultas de medios sin necesidad de una configuración especial del servidor, lo que ahorra el tiempo y el esfuerzo necesarios para analizar las cadenas de un usuario-agente.

La idea es que incluyas lenguaje de marcado optimizado para motores de búsqueda (link rel=alternate) en la parte superior de tu <head> que indique qué versiones de tu sitio quieres incluir.

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

A continuación, puedes realizar la detección de UA del servidor y controlar el redireccionamiento de la versión por tu cuenta, o bien usar la secuencia de comandos device.js para realizar el redireccionamiento basado en las funciones del cliente.

Para obtener más información, consulta la página del proyecto de device.js y también una aplicación falsa que usa device.js para el redireccionamiento del cliente.

Recomendación: MVC con vistas específicas de factores de forma

Probablemente pienses que debes crear tres apps completamente separadas, una para cada tipo de dispositivo. ¡No! Compartir código es la clave.

Se espera que hayas usado un framework similar a MVC, como Backbone, Ember, etc. Si lo usaste, ya conoces el principio de separación de problemas, en particular, que tu IU (capa de vista) debe estar separada de tu lógica (capa de modelo). Si esto es nuevo para ti, comienza con algunos de estos recursos en MVC y MVC en JavaScript.

La historia de dispositivos múltiples se adapta perfectamente al marco de trabajo de MVC existente. Puedes mover fácilmente las vistas a archivos separados y crear una vista personalizada para cada tipo de dispositivo. Luego, puedes entregar el mismo código a todos los dispositivos, excepto en la capa de vista.

MVC multidispositivo.
MVC multidispositivo.

Tu proyecto puede tener la siguiente estructura (por supuesto, puedes elegir la que tenga más sentido según tu aplicación):

models/ (modelos compartidos) item.js item-collection.js

Controllers/ (controladores compartidos) item-controller.js

versiones/ (contenido específico del dispositivo) tablet/ escritorio/ teléfono/ (código específico del teléfono) style.css index.html views/ item.js item-list.js

Este tipo de estructura te permite controlar por completo qué recursos carga cada versión, ya que cuentas con HTML, CSS y JavaScript personalizados para cada dispositivo. Esto es muy potente y puede conducir a la forma más eficiente y eficaz de desarrollo para la Web multidispositivo, sin depender de trucos como imágenes adaptables.

Una vez que ejecutes tu herramienta de compilación favorita, concatenarás y reducirás todo el código JavaScript y CSS en archivos únicos para lograr una carga más rápida, con el código HTML de producción que se verá de la siguiente manera (para teléfonos, con 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>

Ten en cuenta que la consulta de medios (touch-enabled: 0) no es estándar (solo se implementa en Firefox detrás de un prefijo del proveedor moz), pero device.js la maneja correctamente (gracias a Modernizr.touch).

Anulación de versión

A veces, la detección de dispositivos puede fallar y, en algunos casos, es posible que un usuario prefiera mirar el diseño de tablet en su teléfono (quizás esté usando un Galaxy Note). Por lo tanto, es importante ofrecerles a los usuarios la opción de elegir qué versión de tu sitio usar si desean realizar la anulación manualmente.

El enfoque habitual es proporcionar un vínculo a la versión para computadoras de escritorio desde la versión para dispositivos móviles. Esto es fácil de implementar, pero device.js admite esta funcionalidad con el parámetro GET device.

Conclusiones

En resumen, cuando compiles IUs de una sola página para varios dispositivos, que no encajan perfectamente en el mundo del diseño responsivo, haz lo siguiente:

  1. Elige el conjunto de clases de dispositivos que admitirás y los criterios según los cuales clasificar los dispositivos en clases.
  2. Compila tu app de MVC con una gran separación de problemas mediante la división de las vistas del resto de la base de código.
  3. Usa device.js para realizar la detección de clase de dispositivo del cliente.
  4. Cuando esté todo listo, empaqueta tu secuencia de comandos y las hojas de estilo en una de cada una por clase de dispositivo.
  5. Si el rendimiento del redireccionamiento del cliente es un problema, abandona device.js y cambia a la detección de UA del servidor.