Add user-controlled animation preferences and reduce motion support

- Persist prefersReducedMotion and animationSpeedMultiplier in user
profile - Provide UI controls to toggle reduce motion and adjust
animation speed globally - Centralize animation preferences via provider
and useAnimationPreferences hook - Apply preferences to charts’
animations (duration, enabled/disabled) - Inline script in layout to
apply preferences early and avoid FOUC - Update CSS to respect user
preference with reduced motion overrides and variable animation speeds
This commit is contained in:
2025-08-11 17:54:53 -04:00
parent 46767ca7e2
commit a270f6c1e5
10 changed files with 1150 additions and 14 deletions
@@ -10,6 +10,7 @@ import {
} from "recharts";
import { getEffectiveInvoiceStatus } from "~/lib/invoice-status";
import type { StoredInvoiceStatus } from "~/types/invoice";
import { useAnimationPreferences } from "~/components/providers/animation-preferences-provider";
interface Invoice {
id: string;
@@ -87,6 +88,13 @@ export function MonthlyMetricsChart({ invoices }: MonthlyMetricsChartProps) {
}),
}));
// Animation / motion preferences
const { prefersReducedMotion, animationSpeedMultiplier } =
useAnimationPreferences();
const barAnimationDuration = Math.round(
500 / (animationSpeedMultiplier || 1),
);
const CustomTooltip = ({
active,
payload,
@@ -167,24 +175,36 @@ export function MonthlyMetricsChart({ invoices }: MonthlyMetricsChartProps) {
stackId="a"
fill="hsl(0, 0%, 60%)"
radius={[0, 0, 0, 0]}
isAnimationActive={!prefersReducedMotion}
animationDuration={barAnimationDuration}
animationEasing="ease-out"
/>
<Bar
dataKey="paidInvoices"
stackId="a"
fill="var(--chart-2)"
radius={[0, 0, 0, 0]}
isAnimationActive={!prefersReducedMotion}
animationDuration={barAnimationDuration}
animationEasing="ease-out"
/>
<Bar
dataKey="pendingInvoices"
stackId="a"
fill="var(--chart-1)"
radius={[0, 0, 0, 0]}
isAnimationActive={!prefersReducedMotion}
animationDuration={barAnimationDuration}
animationEasing="ease-out"
/>
<Bar
dataKey="overdueInvoices"
stackId="a"
fill="var(--chart-3)"
radius={[2, 2, 0, 0]}
isAnimationActive={!prefersReducedMotion}
animationDuration={barAnimationDuration}
animationEasing="ease-out"
/>
</BarChart>
</ResponsiveContainer>