Respeitando as preferências de movimento dos usuários

Publicados: 2022-03-10
Resumo rápido ↬ A consulta de mídia prefers-reduced-motion excelente suporte em todos os navegadores modernos há alguns anos. Neste artigo, Michelle Barker explica por que não há motivo para não usá-lo hoje para tornar seus sites mais acessíveis.

Ao trabalhar com movimento na web, é importante considerar que nem todos o experimentam da mesma maneira. O que pode parecer suave e liso para alguns pode ser irritante ou perturbador para outros – ou pior, induzir sentimentos de doença ou até causar convulsões. Sites com muito movimento também podem ter um impacto maior na duração da bateria de dispositivos móveis ou fazer com que mais dados sejam usados ​​(vídeos de reprodução automática, por exemplo, exigirão mais dados do usuário do que uma imagem estática). Essas são apenas algumas das razões pelas quais sites com muito movimento podem não ser desejáveis ​​para todos.

A maioria dos novos sistemas operacionais permite que o usuário defina suas preferências de movimento nas configurações de nível de sistema. A consulta de mídia prefers-reduced-motion (parte da especificação de consultas de mídia de nível 5) nos permite detectar as preferências de movimento no nível do sistema dos usuários e aplicar estilos CSS que respeitem isso.

As duas opções para prefers-reduced-motion são reduce ou no-preference . Podemos usá-lo da seguinte maneira em nosso CSS para desativar a animação de um elemento se o usuário tiver definido explicitamente uma preferência por movimento reduzido :

 .some-element { animation: bounce 1200ms; } @media (prefers-reduced-motion: reduce) { .some-element { animation: none; } }

Por outro lado, podemos definir a animação apenas se o usuário não tiver preferência de movimento. Isso tem a vantagem de reduzir a quantidade de código que precisamos escrever e significa que é menos provável que esqueçamos de atender às preferências de movimento dos usuários:

 @media (prefers-reduced-motion: no-preference) { .some-element { animation: bounce 1200ms; } }

Uma vantagem adicional é que os navegadores mais antigos que não suportam prefers-reduced-motion ignorarão a regra e exibirão apenas nosso elemento original sem movimento.

Qual regra?

Ao contrário das consultas de mídia min-width e max-width , onde o consenso mais ou menos estabelecido é mobile-first (portanto, favorecendo min-width ), não há uma única maneira “certa” de escrever seus estilos de movimento reduzido. Eu costumo favorecer o segundo exemplo (aplicar animações apenas se prefers-reduced-motion: no-preference avalia verdadeiro), pelos motivos listados acima. Tatiana Mac escreveu este excelente artigo que aborda algumas das abordagens que os desenvolvedores podem considerar, além de muitos outros pontos importantes, incluindo perguntas importantes a serem feitas ao projetar com movimento na web.

Como sempre, a comunicação da equipe e uma estratégia consistente são fundamentais para garantir que todas as bases sejam cobertas quando se trata de acessibilidade na web.

Mais depois do salto! Continue lendo abaixo ↓

Uso prático: Aplicando o comportamento de rolagem prefers-reduced-motion

prefers-reduced-motion tem muitos aplicativos além de aplicar (ou não aplicar) animações ou transições de quadro-chave. Um exemplo é a rolagem suave. Se definirmos scroll-behaviour: smooth em nosso elemento html , quando um usuário clicar em um link de âncora na página, ele será rolado suavemente para a posição apropriada na página ( atualmente não suportado no Safari ):

 html { scroll-behavior: smooth; }

Infelizmente, em CSS não temos muito controle sobre esse comportamento no momento. Se tivermos uma página longa de conteúdo, a página rola muito rápido, o que pode ser uma experiência bastante desagradável para alguém com sensibilidade ao movimento. Ao envolvê-lo em uma consulta de mídia, podemos impedir que esse comportamento seja aplicado nos casos em que o usuário tem uma preferência reduced-motion :

 @media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

Catering para preferências de movimento em Javascript

Às vezes, precisamos aplicar movimento em JavaScript em vez de CSS. Da mesma forma, podemos detectar as preferências de movimento de um usuário com JS, usando matchMedia . Vamos ver como podemos implementar condicionalmente o comportamento de rolagem suave em nosso código JS:

 /* Set the media query */ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)') button.addEventListener('click', () => { /* If the media query matches, set scroll behavior variable to 'auto', otherwise set it to 'smooth' */ const behavior = prefersReducedMotion.matches ? 'auto' : 'smooth' /* When the button is clicked, the user will be scrolled to the top */ window.scrollTo({ x: 0, y: 0, behavior }) })

O mesmo princípio pode ser usado para detectar se deve-se implementar UIs ricas em movimento com bibliotecas JS — ou até mesmo se deve-se carregar as próprias bibliotecas.

No trecho de código a seguir, a função retorna mais cedo se o usuário preferir movimento reduzido, evitando a importação desnecessária de uma grande dependência — uma vitória de desempenho para o usuário. Se eles não tiverem nenhuma preferência de movimento definida, podemos importar dinamicamente a biblioteca de animação Greensock e inicializar nossas animações.

 const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)') const loadGSAPAndInitAnimations = () => { /* If user prefers reduced motion, do nothing */ if (prefersReducedMotion.matches) return /* Otherwise, import the GSAP module and initialize animations */ import('gsap').then((object) => { const gsap = object.default /* Initialize animations with GSAP here */ }) } loadGSAPAndInitAnimations()

reduced-motion não significa nenhum movimento

Ao estilizar para preferências de movimento reduzido, é importante que ainda forneçamos ao usuário indicadores significativos e acessíveis de quando uma ação ocorreu. Por exemplo, ao desativar um estado de foco de distração ou movimento intenso para usuários que preferem movimento reduzido, devemos ter o cuidado de fornecer um estilo alternativo claro para quando o usuário estiver pairando sobre o elemento.

A demonstração a seguir mostra uma transição elaborada quando o usuário passa o mouse ou foca em um item da galeria se não tiver uma preferência de movimento definida. Se eles preferem movimento reduzido, a transição é mais sutil, mas ainda indica claramente o estado de foco:

Veja a Caneta [Galeria com movimentos reduzidos preferenciais](https://codepen.io/smashingmag/pen/KKvMqaL) de Michelle Barker.

Veja a Galeria de canetas com movimentos reduzidos preferenciais de Michelle Barker.

Movimento reduzido não significa necessariamente remover todas as transformações de nossa página da web. Por exemplo, é improvável que um botão que tenha um pequeno ícone de seta que move alguns pixels ao passar o mouse cause problemas para alguém que prefere uma experiência de movimento reduzido e fornece um indicador mais útil de uma mudança de estado do que apenas a cor.

Às vezes vejo desenvolvedores aplicando estilos de movimento reduzido da seguinte maneira, o que elimina todas as transições e animações em todos os elementos:

 @media screen and (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; scroll-behavior: auto !important; } }

Isso é sem dúvida melhor do que ignorar as preferências de movimento dos usuários, mas não nos permite personalizar facilmente os elementos para fornecer transições mais sutis quando necessário.

No trecho de código a seguir, temos um botão que cresce em escala ao passar o mouse . Estamos fazendo a transição das cores e da escala, mas os usuários com preferência por movimento reduzido não terão nenhuma transição:

 button { background-color: hotpink; transition: color 300ms, background-color 300ms, transform 500ms cubic-bezier(.44, .23, .47, 1.27); } button:hover, button:focus { background-color: darkviolet; color: white; transform: scale(1.2); } @media screen and (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; scroll-behavior: auto !important; } button { /* Even though we would still like to transition the colors of our button, the following rule will have no effect */ transition: color 200ms, background-color 200ms; } button:hover, button:focus { /* Preventing the button scaling on hover */ transform: scale(1); } }

Confira esta demonstração para ver o efeito. Isso talvez não seja o ideal, pois a mudança repentina de cor sem uma transição pode parecer mais chocante do que uma transição de algumas centenas de milissegundos. Esta é uma razão pela qual, em geral, geralmente prefiro estilizar o movimento reduzido caso a caso.

Se você estiver interessado, esta é a mesma demonstração refatorada para permitir a personalização da transição quando necessário. Ele usa uma propriedade personalizada para a duração da transição, o que nos permite ativar e desativar a transição de escala sem ter que reescrever toda a declaração.

Quando remover a animação é melhor

Eric Bailey levanta a questão de que “nem todos os dispositivos que podem acessar a web também podem renderizar animação, ou renderizar animação sem problemas” em seu artigo “Revisitando o movimento reduzido, a consulta de mídia de movimento reduzido”. Para dispositivos com baixa taxa de atualização, o que pode causar animações irregulares, pode ser preferível remover a animação . O recurso de mídia de update pode ser usado para determinar isso:

 @media screen and (prefers-reduced-motion: reduce), (update: slow) { * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; } }

Certifique-se de ler o artigo completo para as recomendações de Eric, pois ele é uma pessoa de primeira linha a seguir no campo da acessibilidade.

A soma de todas as partes

É importante ter em mente o design geral da página ao focar tão firmemente no CSS no nível do componente. O que pode parecer uma animação bastante inócua no nível do componente pode ter um impacto muito maior quando repetido por toda a página e é uma das muitas partes móveis.

No artigo de Tatiana, ela sugere organizar as animações (com prefers-reduced-motion ) em um único arquivo CSS, que pode ser carregado somente se (prefers-reduced-motion: no-preference) for verdadeiro. Ver a soma total de todas as nossas animações pode ter o benefício adicional de nos ajudar a visualizar a experiência de visitar o site como um todo e adaptar nossos estilos de movimento reduzido de acordo.

Alternar movimento explícito

Embora prefers-reduced-motion seja útil, ele tem a desvantagem de atender apenas aos usuários que estão cientes do recurso nas configurações do sistema. Muitos usuários não têm conhecimento dessa configuração, enquanto outros podem estar usando um computador emprestado, sem acesso às configurações no nível do sistema. Ainda assim, outros podem ficar satisfeitos com o movimento para a grande maioria dos sites, mas acham difícil suportar sites com uso intenso de movimento.

Pode ser irritante ter que ajustar as preferências do sistema apenas para visitar um site. Por esses motivos, em alguns casos, pode ser preferível fornecer um controle explícito no próprio site para ativar e desativar o movimento . Podemos implementar isso com JS.

A demonstração a seguir tem vários círculos flutuando ao redor do plano de fundo. Os estilos de animação iniciais são determinados pelas preferências do sistema do usuário (com prefers-reduced-motion ), no entanto, o usuário tem a capacidade de ativar ou desativar o movimento por meio de um botão. Isso adiciona uma classe ao body , que podemos usar para definir estilos dependendo da preferência selecionada. Como bônus, a escolha da preferência de movimento também é preservada no armazenamento local – para que seja “lembrada” na próxima visita do usuário.

Veja a caneta [alternância de movimento reduzido](https://codepen.io/smashingmag/pen/porEQLB) por Michelle Barker.

Veja a alternância de movimento reduzido da caneta de Michelle Barker.

Propriedades personalizadas

Um recurso na demonstração é que a alternância define uma propriedade personalizada, --playState , que podemos usar para reproduzir ou pausar animações. Isso pode ser especialmente útil se você precisar pausar ou reproduzir várias animações ao mesmo tempo. Em primeiro lugar, definimos o estado de reprodução como paused :

 .circle { animation-play-state: var(--playState, paused); }

Se o usuário tiver definido uma preferência por movimento reduzido nas configurações do sistema, podemos definir o estado de reprodução como running :

 @media (prefers-reduced-motion: no-preference) { body { --playState: running; } }

Nota: Definir isso no body , em oposição ao elemento individual, significa que a propriedade personalizada pode ser herdada.

Quando o usuário clica no botão de alternância, a propriedade personalizada é atualizada no body , o que alternará todas as instâncias em que for usada:

 // This will pause all animations that use the `--playState` custom property document.body.style.setProperty('--playState', 'paused')

Essa pode não ser a solução ideal em todos os casos, mas uma vantagem é que a animação simplesmente pausa quando o usuário clica no botão, em vez de voltar ao estado inicial, o que pode ser bastante chocante.

Agradecimentos especiais vão para Scott O'Hara por suas recomendações para melhorar a acessibilidade da alternância. Ele me alertou de que alguns leitores de tela não anunciam o texto do botão atualizado, que é alterado quando um usuário clica no botão, e sugeriu role="switch" no botão, com aria-checked on ou off ao clicar.

Componente de vídeo

Em alguns casos, alternar o movimento no nível do componente pode ser uma opção melhor. Pegue uma página da Web com um plano de fundo de vídeo de reprodução automática. Devemos garantir que o vídeo não seja reproduzido automaticamente para usuários com preferência por movimento reduzido, mas ainda devemos fornecer uma maneira para que eles reproduzam o vídeo somente se quiserem . (Alguns podem argumentar que devemos evitar a reprodução automática de vídeos, mas nem sempre vencemos essa batalha!) Da mesma forma, se um vídeo for configurado para reprodução automática para usuários sem uma preferência declarada, também devemos fornecer uma maneira de eles pausar o vídeo.

Esta demonstração mostra como podemos definir o atributo de reprodução automática quando o usuário não tem nenhuma preferência de movimento declarada, implementando um botão de reprodução/pausa personalizado para permitir que eles também alternem a reprodução, independentemente da preferência:

Veja a Caneta [Vídeo com preferência de movimento](https://codepen.io/smashingmag/pen/qBXNjqR) de Michelle Barker.

Veja o vídeo da caneta com preferência de movimento de Michelle Barker.

(Em seguida, encontrei este post de Scott O'Hara, detalhando esse caso de uso exato.)

Usando o elemento <picture>

Chris Coyier escreveu um artigo interessante combinando algumas técnicas para carregar diferentes fontes de mídia dependendo das preferências de movimento do usuário. Isso é muito legal, pois significa que, para usuários que preferem movimento reduzido , o arquivo GIF muito maior nem será baixado. A desvantagem, até onde posso ver, é que, uma vez baixado o arquivo, não há como o usuário voltar para a alternativa sem movimento.

Eu crio uma versão modificada da demonstração que adiciona esta opção. (Ative reduced-motion nas preferências do sistema para vê-lo em ação.) Infelizmente, ao alternar entre as opções animadas e sem movimento no Chrome, parece que o arquivo GIF é baixado novamente a cada vez, o que não é o caso em outros navegadores:

Veja a Caneta [Prefere a Técnica de Movimento de Redução PLUS! [bifurcado]](https://codepen.io/smashingmag/pen/porbPXG) por Michelle Barker.

Veja o Pen Preferes Reduction Motion Technique PLUS! [bifurcado] por Michelle Barker.

Ainda assim, essa técnica parece ser uma maneira mais respeitosa de exibir GIFs, o que pode ser uma fonte de frustração para os usuários.

Suporte ao navegador e considerações finais

prefers-reduced-motion tem excelente suporte em todos os navegadores modernos há alguns anos. Como vimos, ao adotar uma abordagem de movimento reduzido primeiro, os navegadores não compatíveis simplesmente obterão um fallback reduced-motion . Não há razão para não usá-lo hoje para tornar seus sites mais acessíveis.

As alternâncias personalizadas definitivamente têm um lugar e podem melhorar muito a experiência dos usuários que não estão cientes dessa configuração ou do que ela faz. A desvantagem para o usuário é a inconsistência - se cada desenvolvedor for forçado a criar sua própria solução, o usuário precisará procurar uma alternância de movimento em um local diferente em cada site.

Parece que a camada que falta aqui são os navegadores . Eu adoraria ver os navegadores implementarem reduced-motion , em algum lugar facilmente acessível ao usuário, para que as pessoas saibam onde encontrá-lo, independentemente do site em que estão navegando. Pode incentivar os desenvolvedores a gastar mais tempo garantindo a acessibilidade de movimento também.

Recursos Relacionados

  • Especificação de consultas de mídia de nível 5, World Wide Web Consortium (W3C)
  • prefers-reduced-motion , MDN Web Docs, Mozilla
  • Design com movimento reduzido para sensibilidades de movimento, cabeça Val, revista Smashing
  • Adotando uma abordagem sem movimento para animações, Tatiana Mac
  • Técnica de Cinema Reduzido, Take 2, Chris Coyier, CSS-Tricks
  • Revisitando prefers-reduced-motion , The Reduced Motion Media Query, Eric Bailey, CSS-Tricks
  • Reduzindo o movimento para melhorar a acessibilidade, Lindsey Kopacz