Reduce Motion — una funzione di accessibilità di sistemi operativi e browser che disattiva o riduce l'animazione dell'interfaccia per prevenire disagi negli utenti con disturbi vestibolari. Su iOS, l'impostazione si trova in Settings > Accessibility > Motion > Reduce Motion e disattiva il parallasse, le animazioni di transizione tra gli schermi e gli effetti delle icone. Su Android, l'opzione equivalente “Rimuovi animazioni” si trova in Settings > Accessibility. Secondo W3C WCAG 2.2, un'animazione eccessiva può causare nausea, vertigini ed emicranie nel 15–35% delle persone con disturbi vestibolari.
Punti chiave
Reduce Motion — un'impostazione di sistema che ordina al sistema operativo, al browser e alle app di ridurre al minimo o disattivare completamente animazione, movimento, scalatura ed effetti di parallasse. La funzione è pensata per le persone con disturbi vestibolari, nelle quali gli elementi in movimento dell'interfaccia causano nausea, vertigini, disorientamento ed emicranie.
Il sistema vestibolare dell'orecchio interno è responsabile del senso dell'equilibrio e dell'orientamento spaziale. Quando gli occhi vedono movimento sullo schermo ma l'apparato vestibolare non percepisce il corrispondente spostamento del corpo, si verifica un conflitto sensoriale. Questo conflitto innesca una reazione vegetativa: nausea, sudore freddo, vertigini. Secondo la Vestibular Disorders Association, circa il 35% delle persone sopra i 40 anni prova disagio per gli effetti di movimento nelle interfacce.
La differenza fondamentale tra Reduce Motion e la semplice disattivazione delle animazioni è il livello di sistema. Quando è attivata, l'impostazione viene trasmessa a tutte le app e ai siti web tramite l'API del sistema operativo o la media feature CSS prefers-reduced-motion. Lo sviluppatore non deve indovinare se l'utente vuole l'animazione — il sistema operativo lo comunica. Questo fa parte dell'approccio progressive enhancement: l'animazione è un miglioramento, non un elemento obbligatorio.
Reduce Motion influisce sugli effetti di parallasse (spostamento dello sfondo quando si inclina il dispositivo), sulle animazioni di transizione tra gli schermi (push, slide, fade), sulla scalatura delle icone al tocco, sugli effetti dei messaggi (bolle SMS), sulle animazioni di caricamento (spinner, skeleton, animazione della barra di avanzamento), sul parallasse nei giochi e sugli effetti web di parallasse scrolling. Immagini statiche e video non sono interessati.
| Tipo di animazione | iOS Reduce Motion | Android Remove Animation | CSS prefers-reduced-motion |
|---|---|---|---|
| Parallasse dello sfondo | Disattiva | Disattiva | N/A |
| Transizioni tra gli schermi | Sostituisce con fade | Disattiva | CSS transitions |
| Animazione delle icone | Disattiva | Nessun effetto | CSS animations |
| Spinner di caricamento | Nessun effetto | Disattiva | CSS animations |
| Schermate skeleton | Nessun effetto | Sostituisce con statico | CSS animations |
Il pubblico principale di Reduce Motion sono le persone con disturbi vestibolari (malattia di Ménière, labirintite, VPPB), emicrania (soprattutto con aura), cinetosi (mal d'auto nei trasporti) e alcune forme di epilessia. La funzione è utile anche per gli utenti anziani — i cambiamenti legati all'età nell'apparato vestibolare aumentano la sensibilità al movimento — e per gli utenti con ADHD, poiché gli elementi in movimento distraggono dalla lettura.
Su iOS, Reduce Motion si trova in Settings > Accessibility > Motion e comprende tre opzioni: Reduce Motion (l'interruttore principale), Auto-Play Message Effects (riproduzione automatica degli effetti di iMessage) e Auto-Play Video Previews (riproduzione automatica dei video nell'App Store). Reduce Motion è il più potente: disattiva il parallasse dello sfondo, l'animazione di apertura delle app (zoom), le transizioni tra gli schermi (push viene sostituito da cross-fade) e gli effetti delle icone nella schermata Home.
Tecnicamente, iOS implementa Reduce Motion a livello di UIKit tramite UIView animations e UIViewControllerAnimatedTransitioning. Con l'impostazione attiva, UIView.animate con durata > 0 viene ridotto a 0,01 secondi e UIViewController.transitionDuration restituisce 0. Per il parallasse, iOS disattiva UIInterpolatingMotionEffect su UIWindow. Lo sviluppatore può verificare lo stato tramite UIAccessibility.isReduceMotionEnabled.
Uno sviluppatore può adattare l'animazione su iOS a Reduce Motion: sostituire le transizioni complesse con effetti fade, disattivare il parallasse nei componenti UI personalizzati e ridurre la durata delle animazioni. SwiftUI fornisce un modificatore speciale .animation(nil) quando reduceMotion è attivo.
import UIKit
class MotionManager {
// Verificare lo stato di Reduce Motion
static var isReduceMotionEnabled: Bool {
return UIAccessibility.isReduceMotionEnabled
}
// Animazione sicura che rispetta 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 — modificatore per 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 }
}
}
// Sottoscrizione al cambiamento di Reduce Motion
NotificationCenter.default.addObserver(
self,
selector: #selector(motionStatusChanged),
name: UIAccessibility.reduceMotionStatusDidChangeNotification,
object: nil
)La classe MotionManager verifica isReduceMotionEnabled e adatta la durata dell'animazione: quando Reduce Motion è attivo, la durata viene ridotta a 0,01 secondi (praticamente istantanea). Il modificatore SwiftUI .animation(nil) disattiva completamente l'animazione quando reduceMotion == true. La sottoscrizione a reduceMotionStatusDidChangeNotification consente di cambiare dinamicamente l'animazione senza riavviare l'app.
Un'opzione separata di iOS — Auto-Play Message Effects — controlla la riproduzione degli effetti in iMessage: animazione dei palloncini, coriandoli, laser e fuochi d'artificio. Quando Reduce Motion è attivo, gli effetti di iMessage vengono disattivati automaticamente. Uno sviluppatore può verificare questa opzione tramite UIAccessibility.isReduceMotionEnabled — non esiste un'API separata per Message Effects; sono legati all'impostazione generale.
Su Android, l'impostazione “Rimuovi animazioni” si trova in due punti: Settings > Accessibility > Rimuovi animazioni (per le impostazioni di accessibilità) e Opzioni sviluppatore > Scala animazione (per gli sviluppatori). La versione di accessibilità disattiva tutte le animazioni di sistema, incluse le transizioni tra gli schermi, l'animazione di apertura delle app e gli effetti di caricamento. Le Opzioni sviluppatore consentono di ridurre la scala a 0,5x, 0,1x o di disattivarla completamente.
Tecnicamente, Android implementa la disattivazione delle animazioni tramite proprietà di sistema: window_animation_scale, transition_animation_scale e animator_duration_scale. Con l'impostazione di accessibilità, tutte e tre ricevono il valore 0. Il Launcher di sistema e le app di sistema verificano questi valori. Per le app di terze parti, Android consiglia di usare Jetpack Compose e il sistema View, che leggono automaticamente l'impostazione di sistema tramite AnimationScaleObserver.
Uno sviluppatore può verificare lo stato dell'animazione su Android tramite Settings.Global e adattare l'interfaccia. Jetpack Compose fornisce AnimationScaleObserver per l'adattamento automatico. Il valore chiave è animator_duration_scale: se è 0, l'animazione è completamente disattivata.
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 {
// Verificare se l'animazione è disattivata
fun isAnimationDisabled(context: Context): Boolean {
return try {
Settings.Global.getFloat(
context.contentResolver,
Settings.Global.ANIMATOR_DURATION_SCALE
) == 0f
} catch (e: Settings.SettingNotFoundException) {
false
}
}
// Osservare il cambiamento dell'animazione (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 — animazione che rispetta le impostazioni
@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 con animazione
}
La classe AndroidMotionHelper verifica ANIMATOR_DURATION_SCALE tramite Settings.Global, fornisce un ContentObserver per monitorare i cambiamenti e consente alle animazioni Jetpack Compose di adattarsi: quando l'animazione è disattivata, durationMillis = 0 (applicazione istantanea). Per il sistema View, utilizzare ViewPropertyAnimator.withLayer() e verificare isAnimationDisabled() prima di avviare un'animazione.
L'impostazione di accessibilità “Rimuovi animazioni” su Android è l'unico modo per un utente comune di disattivare l'animazione. Le Opzioni sviluppatore sono destinate solo agli sviluppatori e possono essere nascoste. L'impostazione di accessibilità viene trasmessa in modo garantito a tutte le app tramite l'API di sistema. Lo sviluppatore deve affidarsi ad AccessibilityManager e non alle Opzioni sviluppatore.
La media feature CSS prefers-reduced-motion consente agli sviluppatori web di disattivare o sostituire l'animazione quando l'utente ha attivato Reduce Motion nel sistema operativo. Il browser trasmette il valore reduce quando l'impostazione di sistema è attiva e no-preference se non è stata modificata. È supportata da tutti i browser moderni dal 2020: Chrome 74+, Safari 10.3+, Firefox 64+, Edge 79+.
La raccomandazione di MDN e W3C — impostare sempre prefers-reduced-motion: reduce insieme alle animazioni. Un utente che ha attivato Reduce Motion non deve vedere pulsazioni, parallasse, caroselli in riproduzione automatica o caricamenti skeleton. Al posto dell'animazione si può mostrare contenuto statico o usare una transition CSS con durata minima. Di seguito è riportato un esempio di adattamento completo.
Con prefers-reduced-motion: reduce, una pagina web deve: disattivare le animazioni CSS (@keyframes), sostituire le transition CSS con cambiamenti istantanei, fermare le animazioni JavaScript (requestAnimationFrame), disattivare il parallasse scrolling, fermare caroselli e slider, disattivare gli effetti skeleton e sostituire lo spinner con un indicatore statico.
/* Animazioni di base (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: disattiviamo tutto */
@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; }
}Nell'esempio di CSS si usa un reset globale dell'animazione per prefers-reduced-motion: reduce: tutte le animazioni e le transizioni ricevono durata 0,01ms e iteration-count 1. Separatamente vengono sovrascritti spinner (bordo statico), skeleton (sfondo grigio senza sfarfallio), parallasse (disattivato) e carosello (scroll-behavior: auto invece di smooth). Il selettore universale * è una rete di sicurezza per tutti gli elementi.
Per JavaScript è disponibile matchMedia('(prefers-reduced-motion: reduce)').matches. Se è true — non eseguire animazioni tramite requestAnimationFrame, setInterval o la Web Animations API. Esempio: uno slider con reduceMotion non ruota automaticamente, solo tramite i pulsanti. Librerie come GSAP, Framer Motion e Anime.js supportano questa verifica tramite le proprie opzioni di accessibilità.
WCAG 2.2 (2023) ha introdotto un nuovo criterio 2.3.3 Animation from Interactions (livello AAA): se un'animazione viene avviata da un'interazione dell'utente (click, scorrimento, hover), deve poter essere disattivata o non superare i 5 secondi. L'animazione non legata all'interazione (spinner di caricamento, barra di avanzamento) non rientra in questo criterio. Per il livello AA basta che l'animazione non sia l'unico modo per trasmettere informazioni.
Il criterio 2.3.3 è stato aggiunto dopo numerose segnalazioni di cinetosi causata dal parallasse scrolling e dall'animazione “pull-to-refresh”. W3C raccomanda a tutti i siti e le app: non animare gli elementi per più di 5 secondi, fornire un interruttore per disattivare l'animazione (tramite un pannello di controllo o prefers-reduced-motion) e non usare elementi pulsanti e lampeggianti (pericolosi per l'epilessia).
Per rispettare WCAG 2.2, uno sviluppatore deve: verificare prefers-reduced-motion e disattivare l'animazione quando l'impostazione è attiva, impostare una durata massima dell'animazione di 5 secondi (se l'animazione non può essere disattivata), fornire un interruttore UI per l'animazione nelle impostazioni dell'app, non usare effetti di parallasse come unico metodo di navigazione e testare con Reduce Motion attivo in tutti i browser e su tutti i dispositivi.
Per testare il comportamento dell'app con Reduce Motion, usa: iOS Simulator (attivare Reduce Motion in Settings > Accessibility), Android Emulator (attivare “Rimuovi animazioni”), Chrome DevTools (Rendering > Emulate CSS media feature prefers-reduced-motion) e l'ispettore web di Safari (attivare Reduce Motion in Preferenze di Sistema macOS > Accessibilità > Display). Verifica che il contenuto rimanga accessibile e leggibile senza animazione.
Domande frequenti
Reduce Motion — una funzione di accessibilità del sistema operativo che disattiva l'animazione dell'interfaccia (parallasse, transizioni, scalatura delle icone) per le persone con disturbi vestibolari. Si attiva in Settings > Accessibility. Il sistema trasmette il segnale alle app tramite UIAccessibility (iOS), Settings.Global (Android) o prefers-reduced-motion (CSS), e lo sviluppatore può adattare l'animazione.
Su un iPhone, Reduce Motion si attiva in Settings > Accessibility > Motion > Reduce Motion. Dopo l'attivazione vengono disattivati il parallasse dello sfondo, gli effetti di iMessage, l'animazione di apertura delle app e le transizioni tra gli schermi (sostituite da cross-fade). Le animazioni di sistema UIKit non vengono riprodotte. L'impostazione si applica a tutte le app senza riavviare il dispositivo.
Su Android, l'animazione si disattiva in due modi: Settings > Accessibility > Rimuovi animazioni (consigliato per gli utenti) o Opzioni sviluppatore > Scala animazione — spegnerla. Il metodo di accessibilità garantisce che tutte le app ricevano il segnale. Le Opzioni sviluppatore influiscono solo sulle animazioni di sistema e possono essere nascoste su alcuni dispositivi.
La media feature CSS prefers-reduced-motion: reduce consente di disattivare l'animazione a livello di stili. Si consiglia di impostare un reset universale: animation-duration: 0,01ms, transition-duration: 0,01ms, animation-iteration-count: 1 per tutti gli elementi. Sovrascrivi separatamente i componenti specifici (spinner, skeleton, parallasse). JavaScript viene verificato tramite matchMedia('(prefers-reduced-motion: reduce)').
WCAG 2.2 il criterio 2.3.3 (AAA) richiede che l'animazione avviata dall'interazione dell'utente sia disattivabile o non superi i 5 secondi. Per AA basta che l'animazione non sia l'unico modo per trasmettere informazioni. Si consiglia di verificare sempre prefers-reduced-motion e fornire un interruttore UI per l'animazione nelle impostazioni dell'app.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche