Como criar um componente de rolagem de mídia

Uma visão geral básica sobre como criar uma visualização de rolagem horizontal responsiva para TVs, smartphones, computadores etc.

Nesta postagem, quero compartilhar ideias sobre maneiras de criar a rolagem horizontal. experiências para a Web que sejam mínimas, responsivas, acessíveis e funcionem em navegadores e plataformas (como TVs!). Experimente o demonstração.

Demonstração

Se você preferir o vídeo, aqui está uma versão do YouTube desta postagem:

Visão geral

Criaremos um layout de rolagem horizontal destinado a hospedar miniaturas de mídias ou produtos. O componente começa como uma lista humilde <ul>, mas é com CSS em uma experiência de rolagem suave e satisfatória, mostrando imagens e alinhá-las a uma grade. O JavaScript é adicionado para facilitar interações de índice em mechas, ajudando os usuários de teclado a pular a passagem de mais de 100 itens. Além disso, uma consulta de mídia experimental, prefers-reduced-data, é usada para transformar a botão de rolagem de mídia em uma experiência leve de rolagem de título.

Comece com uma marcação acessível

Um botão de rolagem de mídia é composto por apenas alguns componentes principais, uma lista com itens. Um lista, em sua forma mais simples, pode viajar ao mundo todo e ser claramente consumida por todos. Um usuário que acessa esta página pode procurar em uma lista e clicar em um link para visualizar um item. Esta é nossa base acessível.

Envie uma lista com um elemento <ul>:

<ul class="horizontal-media-scroller">
  <li></li>
  <li></li>
  <li></li>
  ...
<ul>

Torne os itens da lista interativos com um elemento <a>:

<li>
  <a href="#">
    ...
  </a>
</li>

Use um elemento <figure> para representar semanticamente uma imagem e sua legenda:

<figure>
  <picture>
    <img alt="..." loading="lazy" src="https://picsum.photos/500/500?1">
  </picture>
  <figcaption>Legends</figcaption>
</figure>

Observe os atributos alt e loading no <img>. Texto alternativo para uma mídia é uma oportunidade de UX para dar mais contexto à miniatura ou texto de substituição se a imagem não tiver sido carregada ou fornecer uma interface falada para os usuários que dependem de tecnologia assistiva, como um leitor de tela. Saiba mais com a página Five golden as regras de compliance ou texto.

O atributo loading aceita a palavra-chave lazy como uma forma de indicar essa imagem. origem deverá ser buscada somente quando a imagem estiver dentro da janela de visualização. Isso pode ser muito bom para listas grandes, pois os usuários só fazem download de imagens para itens que rolaram para visualizar.

Oferecer suporte à preferência do esquema de cores do usuário

Use color-scheme como uma tag <meta> para indicar ao navegador que sua página quer os estilos de user agent claros e escuros. É um modo escuro sem custo financeiro ou modo claro, dependendo de como você olhar para ele:

<meta name="color-scheme" content="dark light">

A metatag fornece o sinal mais antigo possível, de modo que o navegador é possível selecionar uma cor de tela padrão escura se o usuário tiver uma preferência por tema escuro. Isso significa que as navegações entre as páginas do site não piscarão uma tela branca segundo plano entre os carregamentos. Tema escuro perfeito entre carregamentos, muito melhor no olhos.

Saiba muito mais com Thomas Steiner em https://web.dev/color-scheme/.

Adicionar conteúdo

Considerando a estrutura de conteúdo de ul > li > a > figure > picture > img acima, a próxima tarefa é adicionar imagens e títulos para navegar. Incluí a demonstração com espaços reservados com imagens e textos estáticos, mas fique à vontade para potencializar isso no seu sua fonte de dados favorita.

Adicionar estilo com CSS

Agora é hora de o CSS pegar essa lista genérica de conteúdo e transformá-la do usuário. Netflix, app stores e muitos outros sites e apps usam a orientação horizontal áreas de rolagem para empacotar a janela de visualização com categorias e opções.

Criar o layout do botão de rolagem

É importante evitar cortar conteúdo em layouts ou se apoiar em texto truncamento com reticências. Muitas televisões têm controles de rolagem de mídia mas muitas vezes recorrem a conteúdos com reticências. Esse layout não! Isso também permite que o conteúdo de mídia substitua o tamanho da coluna, tornando 1 layout flexível o suficiente para lidar com muitas combinações interessantes.

2
linhas de rolagem mostradas. Um deles não tem reticências, o que significa que é mais alto e cada
título esteja totalmente legível. O outro é mais curto e muitos títulos são cortados
reticências.

O contêiner permite substituir o tamanho da coluna fornecendo o tamanho padrão como uma propriedade personalizada. Esse layout de grade é opinativo sobre o tamanho das colunas, é gerenciar somente o espaçamento e a direção:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2); /* parent owned value for children to be relative to*/
  margin: 0;
}

A propriedade personalizada é usada pelo elemento <picture> para criar nossa proporção básica: uma caixa:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

Com apenas mais alguns estilos secundários, complete os princípios básicos do botão de rolagem de mídia:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  & > li {
    display: inline-block; /* removes the list-item bullet */
  }

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

Definir overflow configura <ul> para permitir rolagem e navegação pelo teclado. na lista, cada elemento <li> filho direto tem o ::marker removido recebendo um novo tipo de exibição de inline-block.

As imagens ainda não respondem e saem das caixas em que eles estão. Domine alguns tamanhos, ajustes e estilos de borda, e um gradiente de segundo plano para quando houver carregamento lento:

img {
  /* smash into whatever box it's in */
  inline-size: 100%;
  block-size: 100%;

  /* don't squish but do cover the space */
  object-fit: cover;

  /* soften the edges */
  border-radius: 1ex;
  overflow: hidden;

  /* if empty, show a gradient placeholder */
  background-image:
    linear-gradient(
      to bottom,
      hsl(0 0% 40%),
      hsl(0 0% 20%)
    );
}

Padding de rolagem

O alinhamento ao conteúdo da página e uma área de superfície de rolagem de ponta a ponta são essenciais para um componente harmonioso e mínimo.

Para conseguir o layout de rolagem de ponta a ponta, alinhado com nossa tipografia e linhas de layout, use uma padding que corresponda a scroll-padding:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block: calc(var(--gap) / 2); /* make space for scrollbar and focus outline */
}

Correção de bug no padding de rolagem horizontal Os exemplos acima mostram como deve ser fácil preencher um contêiner de rolagem, mas há problemas de compatibilidade pendentes com ele (mas foi corrigido no Chromium 91 e versões mais recentes). Consulte aqui do histórico, mas a versão resumida é que o padding nem sempre foi considerado em uma visualização de rolagem.

Um
é destacada na extremidade in-line do último item da lista, mostrando os
o padding e o elemento tenham a mesma largura usada para criar o alinhamento desejado.

Para enganar os navegadores para que eles coloquem o padding no final do botão, vou direcionar para a última figura em cada lista e anexar um pseudoelemento que é o a quantidade desejada de padding.

.horizontal-media-scroller > li:last-of-type figure {
  position: relative;

  &::after {
    content: "";
    position: absolute;

    inline-size: var(--gap);
    block-size: 100%;

    inset-block-start: 0;
    inset-inline-end: calc(var(--gap) * -1);
  }
}

O uso de propriedades lógicas permite que o botão de rolagem de mídia funcione em qualquer modo de gravação e a direção do documento.

Ajuste de rolagem

Um contêiner de rolagem com transbordamento pode se tornar uma janela de visualização de ajuste com uma linha de CSS. Em seguida, os filhos precisam especificar como gostariam de se alinhar a essa janela de visualização.

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block-end: calc(var(--gap) / 2);

  scroll-snap-type: inline mandatory;

  & figure {
    scroll-snap-align: start;
  }
}

Foco

A inspiração para esse componente vem de sua enorme popularidade nas TVs, em lojas de aplicativos e muito mais. Muitas plataformas de videogame usam um botão de rolagem de mídia semelhante a este, como o layout principal da tela inicial. O foco é uma grande UX não apenas uma pequena adição. Imagine usar esse botão de rolagem de mídia seu sofá com um controle remoto, faça algumas pequenas melhorias nessa interação:

.horizontal-media-scroller a {
  outline-offset: 12px;

  &:focus {
    outline-offset: 7px;
  }

  @media (prefers-reduced-motion: no-preference) {
    & {
      transition: outline-offset .25s ease;
    }
  }
}

Isso deixa o estilo do contorno do foco 7px diferente da caixa, o que deixa espaço. Se o usuário não tiver preferências relacionadas à redução de movimento, o deslocamento é transferido, conferindo um movimento sutil ao evento de foco.

Índice móvel

Os usuários de gamepad e teclado precisam de atenção especial nessas longas listas de conteúdo e opções de rolagem. O padrão comum para resolver isso é chamado índice de mecha. É quando um o contêiner de itens está focado no teclado, mas apenas uma filha pode manter o foco por vez. Esse único item focalizável em uma experiência de tempo foi projetado para permitir ignorando a lista possivelmente longa de itens, em vez de pressionar a tecla Tab 50+ até chegar ao fim.

Há 300 itens no primeiro botão da demonstração. Podemos fazer melhor do que eles percorrem todos eles para chegar à próxima seção.

Para criar essa experiência, o JavaScript precisa observar eventos de teclado e focar eventos. Eu criei uma pequena biblioteca de código aberto npm para tornar esse usuário experiência do usuário fácil de ser alcançada. Confira como usá-lo com os três controles de rolagem:

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

Esta demonstração consulta o documento em busca dos controles de rolagem e, para cada um deles, chama a função função rovingIndex(). Transmita o elemento ao rovingIndex() para receber as mechas experiência do usuário, como um contêiner de lista e um seletor de consulta de destino, caso o os alvos focais não são descendentes diretos.

document.querySelectorAll('.horizontal-media-scroller')
  .forEach(scroller =>
    rovingIndex({
      element: scroller,
      target: 'a',
}))

Para saber mais sobre esse efeito, consulte a biblioteca de código aberto roving-ux:

Proporção

Até o momento desta postagem, o suporte para aspect-ratio está por trás de um no Firefox, mas disponível em navegadores Chromium ou conversores. Como o layout de grade do botão de rolagem de mídia especifica apenas a direção e o espaçamento, o dimensionamento pode mudança dentro de uma consulta de mídia que o recurso verifica se há compatibilidade com a proporção. Aprimoramento progressivo em alguns controles de rolagem de mídia mais dinâmicos.

Um
a caixa com proporção de 4:4 é mostrada ao lado das outras proporções de design usadas de 16:9
e 4:3

@supports (aspect-ratio: 1) {
  .horizontal-media-scroller figure > picture {
    inline-size: auto; /* for a block-size driven ratio */
    aspect-ratio: 1; /* boxes by default */

    @nest section:nth-child(2) & {
      aspect-ratio: 16/9;
    }

    @nest section:nth-child(3) & {
      /* double the size of the others */
      block-size: calc(var(--size) * 2);
      aspect-ratio: 4/3;

      /* adjust size to fit more items into the viewport */
      @media (width <= 480px) {
        block-size: calc(var(--size) * 1.5);
      }
    }
  }
}

Se o navegador for compatível com a sintaxe aspect-ratio, as imagens de rolagem de mídia serão tamanho atualizado para aspect-ratio. Usando a sintaxe de aninhamento de rascunho, cada imagem muda de proporção, dependendo da primeira, segunda ou terceira linhas. A A sintaxe Nest também permite definir algumas pequenas ajustes da janela de visualização, junto com a outra lógica de dimensionamento.

Com esse CSS, como o recurso está disponível em mais mecanismos de navegador, uma maneira fácil de mas um layout visualmente mais atraente será renderizado.

Prefere dados reduzidos

A próxima técnica só está disponível por trás de uma sinalização Canário, Eu queria compartilhar como eu poderia economizar uma quantidade considerável de tempo de carregamento de página e uso de dados com algumas linhas de CSS. A consulta de mídia prefers-reduced-data de level 5 permite perguntar se o dispositivo está em quaisquer estados de dados reduzidos, como um modo de economia de dados. Se estiver, posso modificar e, neste caso, ocultar as imagens.

ALT_TEXT_HERE

figure {
  @media (prefers-reduced-data: reduce) {
    & {
      min-inline-size: var(--size);

      & > picture {
        display: none;
      }
    }
  }
}

Ainda é possível navegar pelo conteúdo, mas sem o custo das imagens pesadas. baixado. Veja o site antes de adicionar o CSS prefers-reduced-data:

(7 solicitações, 100 KB de recursos em 131 ms)

ALT_TEXT_HERE

Confira o desempenho do site depois de adicionar o CSS prefers-reduced-data:

ALT_TEXT_HERE

(71 solicitações, 1,2 MB de recursos em 1,07 s)

64 solicitações a menos, o que equivale a aproximadamente 60 imagens na janela de visualização (testes realizados em uma tela widescreen) dessa guia do navegador, um aumento no carregamento de página de cerca de 80%, e 10% dos dados transmitidos. CSS muito eficiente.

Conclusão

Agora que você sabe como eu fiz isso, como faria?! 🙂

Vamos diversificar nossas abordagens e aprender todas as maneiras de criar na Web. Crie um Codepen ou hospede sua própria demonstração, envie um tweet para mim e eu a adicionarei ao seção "Remixes da comunidade" abaixo.

Origem

Remixes da comunidade

Não há nada aqui ainda.