Reduce Motion — um recurso de acessibilidade de sistemas operacionais e navegadores que desativa ou reduz a animação da interface para evitar desconforto em usuários com distúrbios vestibulares. No iOS, a configuração fica em Settings > Accessibility > Motion > Reduce Motion e desativa o parallax, as animações de transição entre telas e os efeitos de ícones. No Android, a opção equivalente “Remover animações” fica em Settings > Accessibility. Segundo W3C WCAG 2.2, a animação excessiva pode causar náuseas, tonturas e enxaquecas em 15–35% das pessoas com distúrbios vestibulares.
Principais pontos
Reduce Motion — uma configuração do sistema que instrui o sistema operacional, o navegador e os aplicativos a minimizar ou desativar completamente animação, movimento, dimensionamento e efeitos de parallax. O recurso é destinado a pessoas com distúrbios vestibulares, em quem os elementos em movimento da interface causam náuseas, tonturas, desorientação e enxaquecas.
O sistema vestibular do ouvido interno é responsável pelo sentido de equilíbrio e orientação espacial. Quando os olhos veem movimento na tela, mas o aparelho vestibular não percebe o deslocamento correspondente do corpo, ocorre um conflito sensorial. Esse conflito desencadeia uma reação vegetativa: náuseas, suor frio, tonturas. Segundo a Vestibular Disorders Association, cerca de 35% das pessoas com mais de 40 anos sentem desconforto com efeitos de movimento nas interfaces.
A diferença fundamental entre Reduce Motion e simplesmente desativar a animação é o nível do sistema. Quando ativada, a configuração é transmitida a todos os aplicativos e sites por meio da API do sistema operacional ou da media feature CSS prefers-reduced-motion. O desenvolvedor não precisa adivinhar se o usuário quer animação — o sistema operacional informa. Isso faz parte da abordagem de progressive enhancement: a animação é um aprimoramento, não um elemento obrigatório.
Reduce Motion afeta efeitos de parallax (deslocamento do fundo ao inclinar o dispositivo), animações de transição entre telas (push, slide, fade), dimensionamento de ícones ao tocar, efeitos de mensagens (balões SMS), animações de carregamento (spinner, skeleton, animação de barra de progresso), parallax em jogos e efeitos web de parallax scrolling. Imagens estáticas e vídeos não são afetados.
| Tipo de animação | iOS Reduce Motion | Android Remove Animation | CSS prefers-reduced-motion |
|---|---|---|---|
| Parallax do papel de parede | Desativa | Desativa | N/A |
| Transições entre telas | Substitui por fade | Desativa | CSS transitions |
| Animação de ícones | Desativa | Sem efeito | CSS animations |
| Spinner de carregamento | Sem efeito | Desativa | CSS animations |
| Telas skeleton | Sem efeito | Substitui por estático | CSS animations |
O público principal do Reduce Motion são pessoas com distúrbios vestibulares (doença de Ménière, labirintite, VPPB), enxaqueca (especialmente com aura), cinetose (enjoo de movimento no transporte) e certas formas de epilepsia. O recurso também é útil para usuários idosos — mudanças relacionadas à idade no aparelho vestibular aumentam a sensibilidade ao movimento — e para usuários com TDAH, pois os elementos em movimento distraem a leitura.
No iOS, o Reduce Motion fica em Settings > Accessibility > Motion e inclui três opções: Reduce Motion (o interruptor principal), Auto-Play Message Effects (reprodução automática de efeitos do iMessage) e Auto-Play Video Previews (reprodução automática de vídeos na App Store). O Reduce Motion é o mais poderoso: desativa o parallax do papel de parede, a animação de abertura de aplicativos (zoom), as transições entre telas (push é substituído por cross-fade) e os efeitos de ícones na tela inicial.
Tecnicamente, o iOS implementa o Reduce Motion no nível do UIKit por meio de UIView animations e UIViewControllerAnimatedTransitioning. Com a configuração ativada, UIView.animate com duração > 0 é reduzido para 0,01 segundos, e UIViewController.transitionDuration retorna 0. Para o parallax, o iOS desativa UIInterpolatingMotionEffect no UIWindow. O desenvolvedor pode verificar o estado por meio de UIAccessibility.isReduceMotionEnabled.
Um desenvolvedor pode adaptar a animação no iOS ao Reduce Motion: substituir transições complexas por efeitos fade, desativar o parallax em componentes de UI personalizados e encurtar as durações das animações. O SwiftUI fornece um modificador especial .animation(nil) quando o reduceMotion está ativo.
import UIKit
class MotionManager {
// Verificar o estado de Reduce Motion
static var isReduceMotionEnabled: Bool {
return UIAccessibility.isReduceMotionEnabled
}
// Animação segura que respeita Reduce Motion
static func animate(
duration: TimeInterval,
delay: TimeInterval = 0,
options: UIView.AnimationOptions = [],
animations: @escaping () -> Void,
completion: ((Bool) -> Void)? = nil
) {
let adjustedDuration = isReduceMotionEnabled
? 0.01
: max(duration, 0.01)
UIView.animate(
withDuration: adjustedDuration,
delay: delay,
options: options,
animations: animations,
completion: completion
)
}
}
// SwiftUI — modificador para Reduce Motion
import SwiftUI
struct MotionAwareView: View {
@Environment(\.accessibilityReduceMotion)
private var reduceMotion
@State private var isAnimating = false
var body: some View {
Circle()
.scaleEffect(isAnimating ? 1.2 : 1.0)
.animation(
reduceMotion
? nil
: .easeInOut(duration: 1.0).repeatForever(),
value: isAnimating
)
.onAppear { isAnimating = true }
}
}
// Assinatura da mudança de Reduce Motion
NotificationCenter.default.addObserver(
self,
selector: #selector(motionStatusChanged),
name: UIAccessibility.reduceMotionStatusDidChangeNotification,
object: nil
)A classe MotionManager verifica isReduceMotionEnabled e adapta a duração da animação: quando o Reduce Motion está ativo, a duração é reduzida para 0,01 segundos (praticamente instantânea). O modificador .animation(nil) do SwiftUI desativa completamente a animação quando reduceMotion == true. Assinar reduceMotionStatusDidChangeNotification permite alternar dinamicamente a animação sem reiniciar o aplicativo.
Uma opção separada do iOS — Auto-Play Message Effects — controla a reprodução de efeitos no iMessage: animação de balões, confete, lasers e fogos de artifício. Quando o Reduce Motion está ativado, os efeitos do iMessage são desativados automaticamente. Um desenvolvedor pode verificar essa opção por meio de UIAccessibility.isReduceMotionEnabled — não há API separada para Message Effects; eles estão vinculados à configuração geral.
No Android, a configuração “Remover animações” fica em dois lugares: Settings > Accessibility > Remover animações (para configurações de acessibilidade) e Opções do desenvolvedor > Escala de animação (para desenvolvedores). A versão de acessibilidade desativa todas as animações do sistema, incluindo transições entre telas, animação de abertura de aplicativos e efeitos de carregamento. As Opções do desenvolvedor permitem reduzir a escala para 0,5x, 0,1x ou desativá-la completamente.
Tecnicamente, o Android implementa a desativação de animações por meio de propriedades do sistema: window_animation_scale, transition_animation_scale e animator_duration_scale. Com a configuração de acessibilidade, as três são definidas como 0. O Launcher do sistema e os aplicativos do sistema verificam esses valores. Para aplicativos de terceiros, o Android recomenda usar Jetpack Compose e o sistema View, que leem automaticamente a configuração do sistema por meio do AnimationScaleObserver.
Um desenvolvedor pode verificar o estado da animação no Android por meio de Settings.Global e adaptar a UI. O Jetpack Compose fornece AnimationScaleObserver para ajuste automático. O valor-chave é animator_duration_scale: se for 0, a animação está totalmente desativada.
import android.content.Context
import android.provider.Settings
import android.database.ContentObserver
import android.os.Handler
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.runtime.getValue
class AndroidMotionHelper {
// Verificar se a animação está desativada
fun isAnimationDisabled(context: Context): Boolean {
return try {
Settings.Global.getFloat(
context.contentResolver,
Settings.Global.ANIMATOR_DURATION_SCALE
) == 0f
} catch (e: Settings.SettingNotFoundException) {
false
}
}
// Observar a mudança de animação (ContentObserver)
fun observeAnimationScale(
context: Context,
onChange: (Boolean) -> Unit
): ContentObserver {
val observer = object : ContentObserver(Handler()) {
override fun onChange(selfChange: Boolean) {
onChange(isAnimationDisabled(context))
}
}
context.contentResolver.registerContentObserver(
Settings.Global.getUriFor(
Settings.Global.ANIMATOR_DURATION_SCALE
),
false,
observer
)
return observer
}
}
// Jetpack Compose — animação que respeita as configurações
@Composable
fun MotionAwareAnimation(enabled: Boolean) {
val context = androidx.compose.ui.platform.LocalContext.current
val noMotion = AndroidMotionHelper().isAnimationDisabled(context)
val scale by animateFloatAsState(
targetValue = if (enabled) 1f else 0f,
animationSpec = if (noMotion) {
androidx.compose.animation.core.tween(durationMillis = 0)
} else {
androidx.compose.animation.core.spring()
},
label = "scale"
)
// UI com animação
}
A classe AndroidMotionHelper verifica ANIMATOR_DURATION_SCALE por meio de Settings.Global, fornece um ContentObserver para rastrear mudanças e permite que as animações do Jetpack Compose se adaptem: quando a animação está desativada, durationMillis = 0 (aplicação instantânea). Para o sistema View, use ViewPropertyAnimator.withLayer() e verifique isAnimationDisabled() antes de iniciar uma animação.
A configuração de acessibilidade “Remover animações” no Android é a única maneira de um usuário comum desativar a animação. As Opções do desenvolvedor são destinadas apenas a desenvolvedores e podem estar ocultas. A configuração de acessibilidade é garantidamente transmitida a todos os aplicativos por meio da API do sistema. O desenvolvedor deve confiar no AccessibilityManager e não nas Opções do desenvolvedor.
A media feature CSS prefers-reduced-motion permite que desenvolvedores web desativem ou substituam a animação quando o usuário ativou Reduce Motion no sistema operacional. O navegador informa o valor reduce quando a configuração do sistema está ativa e no-preference quando não foi alterada. É suportada por todos os navegadores modernos desde 2020: Chrome 74+, Safari 10.3+, Firefox 64+, Edge 79+.
A recomendação de MDN e W3C — sempre definir prefers-reduced-motion: reduce junto com as animações. Um usuário que ativou Reduce Motion não deve ver pulsações, parallax, carrosséis de reprodução automática ou carregamento skeleton. Em vez de animação, pode-se mostrar conteúdo estático ou usar uma transição CSS com duração mínima. Abaixo está um exemplo de adaptação completa.
Com prefers-reduced-motion: reduce, uma página web deve: desativar animações CSS (@keyframes), substituir transições CSS por mudanças instantâneas, parar animações JavaScript (requestAnimationFrame), desativar parallax scrolling, parar carrosséis e sliders, desativar efeitos skeleton e substituir o spinner por um indicador estático.
/* Animações básicas (no-preference) */
.spinner {
width: 32px;
height: 32px;
border: 3px solid #e0e0e0;
border-top-color: #0066CC;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.skeleton {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e8e8e8 37%,
#f0f0f0 63%
);
background-size: 400% 100%;
animation: shimmer 1.4s ease infinite;
}
.parallax-bg {
transform: translateZ(-1px) scale(1.5);
transition: transform 0.3s ease-out;
}
/* Reduce Motion: desativando tudo */
@media (prefers-reduced-motion: reduce) {
.spinner {
animation: none;
border-top-color: #0066CC;
}
.skeleton {
animation: none;
background: #f0f0f0;
}
.parallax-bg {
transform: none;
transition: none;
}
.carousel {
scroll-behavior: auto;
overflow-x: auto;
}
.fade-in {
opacity: 1;
transform: none;
}
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}No exemplo de CSS, usa-se um reset global de animação para prefers-reduced-motion: reduce: todas as animações e transições recebem duração 0,01ms e iteration-count 1. Separadamente, são sobrescritos o spinner (borda estática), o skeleton (fundo cinza sem oscilação), o parallax (desativado) e o carrossel (scroll-behavior: auto em vez de smooth). O seletor universal * é uma rede de segurança para todos os elementos.
Para JavaScript, está disponível matchMedia('(prefers-reduced-motion: reduce)').matches. Se for true — não execute animações por meio de requestAnimationFrame, setInterval ou da Web Animations API. Exemplo: um slider com reduceMotion não gira automaticamente, apenas pelos botões. Bibliotecas como GSAP, Framer Motion e Anime.js suportam essa verificação por meio de suas opções de acessibilidade.
O WCAG 2.2 (2023) introduziu um novo critério 2.3.3 Animation from Interactions (nível AAA): se a animação é acionada pela interação do usuário (clique, rolagem, hover), ela deve poder ser desativada ou não exceder 5 segundos. A animação não relacionada à interação (spinner de carregamento, barra de progresso) não se enquadra nesse critério. Para o nível AA, basta que a animação não seja a única forma de transmitir informação.
O critério 2.3.3 foi adicionado após inúmeras queixas de motion sickness causado por parallax scrolling e animação “pull-to-refresh”. O W3C recomenda a todos os sites e aplicativos: não animar elementos por mais de 5 segundos, fornecer um interruptor para desativar a animação (por meio de um painel de controle ou prefers-reduced-motion) e não usar elementos pulsantes e intermitentes (perigoso para epilepsia).
Para cumprir o WCAG 2.2, um desenvolvedor precisa: verificar prefers-reduced-motion e desativar a animação quando a configuração estiver ativa, definir uma duração máxima de animação de 5 segundos (se a animação não puder ser desativada), fornecer um interruptor de UI para animação nas configurações do aplicativo, não usar efeitos de parallax como única forma de navegação e testar com Reduce Motion ativado em todos os navegadores e dispositivos.
Para testar o comportamento do aplicativo com Reduce Motion, use: iOS Simulator (ativar Reduce Motion em Settings > Accessibility), Android Emulator (ativar “Remover animações”), Chrome DevTools (Rendering > Emulate CSS media feature prefers-reduced-motion) e o inspetor web do Safari (ativar Reduce Motion em macOS System Preferences > Accessibility > Display). Verifique se o conteúdo permanece acessível e legível sem animação.
Perguntas frequentes
Reduce Motion — um recurso de acessibilidade do sistema operacional que desativa a animação da interface (parallax, transições, dimensionamento de ícones) para pessoas com distúrbios vestibulares. É ativado em Settings > Accessibility. O sistema transmite o sinal aos aplicativos por meio de UIAccessibility (iOS), Settings.Global (Android) ou prefers-reduced-motion (CSS), e o desenvolvedor pode adaptar a animação.
Em um iPhone, o Reduce Motion é ativado em Settings > Accessibility > Motion > Reduce Motion. Após a ativação, são desativados o parallax do papel de parede, os efeitos do iMessage, a animação de abertura de aplicativos e as transições entre telas (substituídas por cross-fade). As animações do sistema UIKit não são reproduzidas. A configuração se aplica a todos os aplicativos sem reiniciar o dispositivo.
No Android, a animação é desativada de duas formas: Settings > Accessibility > Remover animações (recomendado para usuários) ou Opções do desenvolvedor > Escala de animação — desativar. O método de acessibilidade garante que todos os aplicativos recebam o sinal. As Opções do desenvolvedor afetam apenas as animações do sistema e podem estar ocultas em alguns dispositivos.
A media feature CSS prefers-reduced-motion: reduce permite desativar a animação no nível dos estilos. Recomenda-se definir um reset universal: animation-duration: 0,01ms, transition-duration: 0,01ms, animation-iteration-count: 1 para todos os elementos. Sobrescreva separadamente os componentes específicos (spinner, skeleton, parallax). O JavaScript é verificado por meio de matchMedia('(prefers-reduced-motion: reduce)').
WCAG 2.2 o critério 2.3.3 (AAA) exige que a animação acionada pela interação do usuário seja desativável ou não exceda 5 segundos. Para AA, basta que a animação não seja a única forma de transmitir informação. Recomenda-se sempre verificar prefers-reduced-motion e fornecer um interruptor de UI para animação nas configurações do aplicativo.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também