36e13ba7fa
Deploy to route.crispygoat.com / deploy (push) Successful in 4m15s
Follow-up to 9fcc514. The global motion pass hit the public site;
this targets the admin design system, which had its own animation
budget that was still producing positional movement on every
interaction.
Killed:
- TabSwitcher y:4 slide (every tab change in /admin)
- CommandPalette scale(0.98) panel pop
- ha-drawer-in: 100% translateX (full-screen sweep)
- ha-popover-in: translateY(-4px) + scale(0.98)
- ha-check-pop: cubic-bezier(0.34, 1.56, 0.64, 1) 1.15x overshoot
- slide-in-from-right: 100% translateX (toasts)
- dashboard-fade-up: 10px Y slide on every card
- tailwindcss-animate keyframes for slide-in-from-{left,right,top,bottom} and zoom-in-{90,95} (used by ToastContainer, LotDetailPanel)
Calmed:
- All durations cut to 120-200ms range with ease-out
- All keyframes that survived are opacity-only
Added a @media (prefers-reduced-motion: reduce) block scoped to
the admin selectors so users with that OS setting see instant
transitions across toasts/drawers/popovers/dashboard cards.
39 lines
1.2 KiB
TypeScript
39 lines
1.2 KiB
TypeScript
"use client";
|
|
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
|
|
type Props = {
|
|
tabKey: string;
|
|
children: React.ReactNode;
|
|
};
|
|
|
|
/**
|
|
* Fades the content of a tabbed panel when the active tab changes.
|
|
* The tab key drives the animation, so switching tabs triggers a quick
|
|
* opacity-only fade-in of the new content.
|
|
*
|
|
* Motion note: the previous version used `y: 4` / `y: -4` on enter/exit
|
|
* for a small slide. That 4px slide, multiplied across every tab change
|
|
* in the admin, was a steady source of positional movement. Now it's
|
|
* pure opacity, 120ms, ease-out. `reducedMotion="user"` on the global
|
|
* <MotionConfig> will further strip this to instant if the user has
|
|
* prefers-reduced-motion enabled.
|
|
*
|
|
* No motion when the tab key is stable (i.e. on first render of a given tab).
|
|
*/
|
|
export function TabSwitcher({ tabKey, children }: Props) {
|
|
return (
|
|
<AnimatePresence mode="wait" initial={false}>
|
|
<motion.div
|
|
key={tabKey}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.12, ease: "easeOut" }}
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
);
|
|
}
|