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.
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.
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.
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.
@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.
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)
Confira o desempenho do site depois de adicionar o CSS prefers-reduced-data
:
(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
Fonte dos desafios da GUI no GitHub (em inglês)
Remixes da comunidade
Não há nada aqui ainda.