fix(a11y): honor prefers-reduced-motion for avatar decorations (P2)

Avatar decorations are animated APNGs and were the only motion feature not
gated on prefers-reduced-motion (chat backgrounds / seasonal overlays all
suppress motion under it). Since there's no static-frame asset to freeze to,
render just the avatar (no decoration overlay) when the user prefers reduced
motion — the only motion-respecting option. Users without the preference are
unaffected; live OS-toggle is reactive via useReducedMotion. Also relieves the
mobile perf drain of dozens of live APNGs in scrolling lists.

Reviewed: correct a11y behavior, hooks-safe, no layout dependency on the overlay.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 00:00:07 -04:00
co-authored by Claude Opus 4.8
parent 8a1168bc5f
commit c3e1fbfff5
@@ -1,5 +1,6 @@
import React from 'react';
import { useAvatarDecoration } from '../../hooks/useAvatarDecoration';
import { useReducedMotion } from '../../hooks/useReducedMotion';
import { decorationUrl } from '../../features/lotus/avatarDecorations';
const DEFAULT_INSET = 8;
@@ -16,8 +17,14 @@ export function AvatarDecoration({
inset = DEFAULT_INSET,
}: AvatarDecorationProps) {
const slug = useAvatarDecoration(userId);
const reducedMotion = useReducedMotion();
if (!slug) {
// Decorations are animated APNGs with no static asset to freeze to, so honor
// prefers-reduced-motion by not rendering the animation at all (consistent
// with the rest of the theming stack — chat backgrounds / seasonal overlays —
// which all suppress motion under this preference; also avoids dozens of live
// APNGs animating in scrolling mobile lists).
if (!slug || reducedMotion) {
return <>{children}</>;
}