Creación de pantallas de esqueleto con propiedades personalizadas de CSS | Programar Plus

El diseño de estados de carga en la web a menudo se pasa por alto o se descarta como una ocurrencia tardía. El rendimiento no es solo responsabilidad del desarrollador, crear una experiencia que funcione con conexiones lentas también puede ser un desafío de diseño.

Si bien los desarrolladores deben prestar atención a cosas como la minificación y el almacenamiento en caché, los diseñadores deben pensar en cómo se verá y se comportará la interfaz de usuario mientras está en un estado de “carga” o “fuera de línea”.

La ilusión de la velocidad

A medida que cambian nuestras expectativas para las experiencias móviles, también cambia nuestra comprensión del rendimiento. La gente espera que las aplicaciones web se sientan tan ágiles y receptivas como las aplicaciones nativas, independientemente de su cobertura de red actual.

El rendimiento percibido es una medida de la rapidez con la que algo siente al usuario. La idea es que los usuarios sean más pacientes y pensarán en un sistema como más rápido si saben lo que está sucediendo y pueden anticipar el contenido antes de que esté realmente allí. Se trata de gestionar las expectativas y mantener informado al usuario.

Para una aplicación web, este concepto puede incluir la visualización de “maquetas” de texto, imágenes u otros elementos de contenido, denominados pantallas de esqueleto 💀. Puede encontrarlos en la naturaleza, utilizados por empresas como Facebook, Google, Slack y otras:

Aplicación de escritorio Slack que usa pantallas de esqueleto mientras se cargaSanto cielo para ti también, Slack.
Esqueleto de Facebook

Un ejemplo

Supongamos que está creando una aplicación web. Es un tipo de consejo de viaje en el que las personas pueden compartir sus viajes y recomendar lugares, por lo que su contenido principal podría verse así:

interfaz de usuario de la tarjeta de una publicación de blog de viajes

Puede tomar esa tarjeta y reducirla a sus formas visuales básicas, el esqueleto del componente de la interfaz de usuario.

versión esqueleto de la misma tarjeta, delineada en rectángulos grises

Cada vez que alguien solicita contenido nuevo del servidor, puede comenzar a mostrar el esqueleto inmediatamente, mientras se cargan los datos en segundo plano. Una vez que el contenido esté listo, simplemente cambie el esqueleto por la tarjeta real. Esto se puede hacer con JavaScript simple o usando una biblioteca como React.

Ahora podría usar una imagen para mostrar el esqueleto, pero eso introduciría una solicitud adicional y una sobrecarga de datos. Ya estamos cargando cosas aquí, por lo que no es una buena idea esperar a que se cargue otra imagen primero. Además, no responde, y si alguna vez decidiéramos ajustar algunos de los estilos de la tarjeta de contenido, tendríamos que duplicar los cambios en la imagen del esqueleto para que volvieran a coincidir. 😒 Meh.

Una mejor solución es crear todo con solo CSS. Sin solicitudes adicionales, gastos generales mínimos, ni siquiera un marcado adicional. Y podemos construirlo de una manera que facilite mucho el cambio de diseño posterior.

Dibujar esqueletos en CSS

Primero, necesitamos dibujar las formas básicas que formarán el esqueleto de la tarjeta. Podemos hacer esto agregando diferentes gradientes al background-image propiedad. De forma predeterminada, los degradados lineales se ejecutan de arriba a abajo, con diferentes transiciones de parada de color. Si solo definimos una parada de color y dejamos el resto transparente, podemos dibujar formas.

Tenga en cuenta que aquí se apilan varias imágenes de fondo una encima de la otra, por lo que el orden es importante. La última definición de degradado estará en la parte posterior, la primera en el frente.

.skeleton {
  background-repeat: no-repeat;
  background-image: 
    /* layer 2: avatar */
    /* white circle with 16px radius */
    radial-gradient(circle 16px, white 99%, transparent 0),
    /* layer 1: title */
    /* white rectangle with 40px height */
    linear-gradient(white 40px, transparent 0),
    /* layer 0: card bg */
    /* gray rectangle that covers whole element */
    linear-gradient(gray 100%, transparent 0);
}

Estas formas se estiran para llenar todo el espacio, al igual que los elementos regulares a nivel de bloque. Si queremos cambiar eso, tendremos que definir dimensiones explícitas para ellos. El valor se empareja en background-size establecer el ancho y alto de cada capa, manteniendo el mismo orden que usamos en background-image:

.skeleton {
  background-size:
    32px 32px,  /* avatar */
    200px 40px, /* title */
    100% 100%;  /* card bg */
}

El último paso es colocar los elementos en la tarjeta. Esto funciona como position:absolute, con valores que representan el left y top propiedad. Por ejemplo, podemos simular un relleno de 24 px para el avatar y el título, para que coincida con el aspecto de la tarjeta de contenido real.

.skeleton {
  background-position:
    24px 24px,  /* avatar */
    24px 200px, /* title */
    0 0;        /* card bg */
}

Divídalo con propiedades personalizadas

Esto funciona bien en un ejemplo simple, pero si queremos construir algo un poco más complejo, el CSS se vuelve rápidamente complicado y muy difícil de leer. Si a otro desarrollador se le entregara ese código, no tendrían idea de dónde vienen todos esos números mágicos. Mantenerlo seguramente apestaría.

Afortunadamente, ahora podemos usar propiedades CSS personalizadas para escribir los estilos de esqueleto de una manera mucho más concisa y amigable para el desarrollador, e incluso tener en cuenta la relación entre diferentes valores:

.skeleton {
  /*
    define as separate properties
  */
  --card-height: 340px;
  --card-padding:24px;
  --card-skeleton: linear-gradient(gray var(--card-height), transparent 0);

  --title-height: 32px;
  --title-width: 200px;
  --title-position: var(--card-padding) 180px;
  --title-skeleton: linear-gradient(white var(--title-height), transparent 0);

  --avatar-size: 32px;
  --avatar-position: var(--card-padding) var(--card-padding);
  --avatar-skeleton: radial-gradient(
    circle calc(var(--avatar-size) / 2), 
    white 99%, 
    transparent 0
  );

  /* 
    now we can break the background up 
    into individual shapes 
  */
  background-image: 
    var(--avatar-skeleton),
    var(--title-skeleton),
    var(--card-skeleton);

  background-size:
    var(--avatar-size),
    var(--title-width) var(--title-height),
    100% 100%;

  background-position:
    var(--avatar-position),
    var(--title-position),
    0 0;
}

Esto no solo es mucho más legible, también es mucho más fácil cambiar algunos de los valores más adelante. Además, podemos usar algunas de las variables (piense --avatar-size, --card-padding, etc.) para definir los estilos de la tarjeta real y mantenerla siempre sincronizada con la versión esqueleto.

Agregar una consulta de medios para ajustar partes del esqueleto en diferentes puntos de interrupción ahora también es bastante simple:

@media screen and (min-width: 47em) {
  :root {
    --card-padding: 32px;
    --card-height: 360px;
  }
}

La compatibilidad del navegador con las propiedades personalizadas es buena, pero no al 100%. Básicamente, todos los navegadores modernos tienen soporte, con IE / Edge un poco tarde para la fiesta. Para este caso de uso específico, sería fácil agregar un respaldo usando variables Sass.

Agregar animación

Para hacerlo aún mejor, podemos animar nuestro esqueleto y hacer que se vea más como un indicador de carga. Todo lo que tenemos que hacer es poner un nuevo degradado en la capa superior y luego animar su posición con @keyframes.

Aquí hay un ejemplo completo de cómo podría verse la tarjeta esquelética terminada:

Tarjeta de carga esquelética de Max Böck (@mxbck) en CodePen.

Puedes usar el :empty selector y un pseudo elemento para dibujar el esqueleto, por lo que solo se aplica a elementos de tarjeta vacíos. Una vez que se inyecta el contenido, la pantalla de esqueleto desaparecerá automáticamente.

Más sobre el diseño para el rendimiento

Para ver más de cerca el diseño para el rendimiento percibido, consulte estos enlaces:

  • Diseñador VS. Desarrollador n. ° 8: Diseñar para un gran rendimiento
  • Vince Speelman: Los nueve estados del diseño
  • Harry Roberts: mejora del rendimiento percibido con varias imágenes de fondo
  • Sitepoint: una guía del diseñador para el rendimiento percibido
  • Manuel Wieser: Carga diferida de color dominante