Pressione enter para ver os resultados ou esc para cancelar.

Criando content loaders confiáveis

Content loaders (também conhecidos como placeholder loaders, ou skeleton loaders) além de serem legais são muito úteis para UX, como por exemplo:

  • O loading mostra uma prévia do que será o conteúdo.
  • O usuário tem a percepção que o conteúdo carrega mais rápido.
  • Cria uma expectativa no usuário e evita surpresas.

Problemas

Fato é que se você já tentou implementar alguma vez, provavelmente já se pegou com algum desses problemas:

  1. Fidelidade: É usado uma abstração genérica de content loaders já prontos, mas que não representa fielmente o que é o componente fazendo com que ocorram glitchs no carregamento. Também pode ser considerado um problema de UX a partir do momento que o usuário espera uma coisa e aparece outra. Diferença de tamanhos pode fazer com que os elementos “dancem” na tela. As vezes é importante que o loader seja fiel ao componente.
  2. Manutenibilidade: A princípio é criado um content loader fiél ao componente, mas diante da necessidade de alterações no componente por novas demandas, torna-se necessário também alterar o content loader para que sempre fiquem ‘sincronizados’. Um problema de manutenibilidade. Uma simples alteração pode ter um custo caro de tempo por conta disso, e uma alteração feita às pressas pode fazer com que os dois fiquem dessincronizados, trazendo de volta o problema da falta de fidelidade.

Quando fugimos de um problema acabamos encontrando o outro. Por querer fugir destes dois problemas encontrei uma abordagem que solucionou minhas necessidades, e é isto que irei falar aqui. Antes de mais nada queria mencionar que a inspiração veio do superplayer.fm (se você entrar no site logo na home verá os content loaders). Talvez a implementação não seja a mesma, mas o princípio por trás é o mesmo que foi usado no superplayer.

Conceito

Essencialmente um content loader não é muito diferente do que uma variação do próprio componente.

Se você tem um componente de Button que, por padrão, tem uma aparência e um formato específicos, e quer criar botões que tenham aparências diferentes, você cria propriedades como style, size, theme, de maneira que seja simples aplicar vários tipos de variações no botão. O conceito geral aqui é o mesmo. Usar o próprio componente como loader, e não separar. Pois se você quer manter duas coisas iguais, não há nada mais confiável do que usar um só, com variação.

componente-loader

Pra ser mais claro no exemplo, o componente terá uma propriedade que diz que ele terá que se comportar como um loader, e depois simplesmente mockar o conteúdo dele.

Preenchimento

A maioria dos content loaders são barras cinzas que passam a percepção de que ali é o lugar de algo. Nestes casos uma variação de loading com um fundo cinza ja é suficiente. Quando um elemento já tem uma altura/largura definidos, é só aplicar o background-color em uma área sem conteúdo, sem segredo.

content loaders

Existem muitos casos, entretanto, que quem molda a altura e/ou largura é o próprio conteúdo. Como por exemplo, texto corrido. Você nunca sabe a área de um parágrafo sem antes saber o conteúdo que vai dentro. Existe uma técnica em que você mocka o conteúdo e esconde ele. Dessa maneira, mesmo sendo impossível ser sempre 100% fiél, você consegue resultados muito próximos.

backgroundcolor2

Você pode usar da mesma ideia com botões adicionando a propriedade pointer-events: none; evitando que o botão seja clicável. Já para imagens, é preciso que tenha uma dimensão especificada, caso contrário o bloco terá 0px de altura. Depois só adicionar um fundo.

content loadersbackgroundcolor4

Shimmer

Shimmer, em tradução livre, significa brilho. No contexto dos content loaders, ele é aquele gradiente que ‘passa’ pelas barras cinzas, fazendo-as brilhar. O shimmer aumenta a percepção do usuário de que aquela área está sendo carregada. O motivo não é muito diferente de um spinner, onde o importante é que o usuário veja algo se mexendo para não pensar que a interface travou ou algo deu errado.

contentloaders

A técnica mais comum consiste em criar um background gradient em que seu background-position é rotacionado por um animation keyframes. Isso cria a falsa ilusão de que o gradiente está andando, mas na verdade é só a posição do background. Aqui tem um exemplo de um Animated Gradient Background. E aqui está um exemplo que criei para demonstrar o shimmer funcionando.

content loaders

Tradeoffs e Conclusão

Como tudo no mundo, há desvantagens que devem ser considerados. Isso NÃO É silver bullet, isto é, não resolve todos os problemas de uma vez só. Cada tipo de abordagem faz com que você ganhe em determinados pontos e perca em outros, e com esta não é diferente. Esta abordagem resolve os dois problemas citados no começo deste artigo, porém traz consigo um outro: ele acopla o estado de loading dentro do componente. Em alguns casos pode não ser um problema, como o exemplo que dei do Button, que devido a baixa complexidade não se torna realmente um problema. Mas se for um componente relativamente grande, talvez você tenha problemas de grande complexidade, o que afetaria diretamente a manutenibilidade também. Por isso deve-se sempre considerar que pode se tornar um problema e se realmente compensa para cada caso.

A minha dica é: quanto mais granular for sua UI, mais fácil se torna aplicar loaders. Mesmo que no final você use-os para criar algo maior, eles ainda estão isolados em uma baixa complexidade.

Ainda não existe uma solução silver-bullet para content loaders. Mas um content loader handcrafted e bem feito pode trazer bons benefícios. Criei um exemplo completo usando React que você pode ver clicando aqui. No exemplo, usando styled-components, eu criei um factory em que aplica um css bem genérico quando contém a prop contentLoading, assim eu consigo reusá-lo em vário cenários. Ainda assim, seria necessário adaptações para vários outros edge cases.