diff --git a/app/components/ScrollFadeIn.tsx b/app/components/ScrollFadeIn.tsx
index ae8ae45..292f3b2 100644
--- a/app/components/ScrollFadeIn.tsx
+++ b/app/components/ScrollFadeIn.tsx
@@ -18,9 +18,11 @@ export default function ScrollFadeIn({ children, className = "", delay = 0 }: Sc
const ref = useRef(null);
const [isVisible, setIsVisible] = useState(false);
const [hasMounted, setHasMounted] = useState(false);
+ const [reduceMotion, setReduceMotion] = useState(false);
useEffect(() => {
setHasMounted(true);
+ setReduceMotion(window.matchMedia("(prefers-reduced-motion: reduce)").matches);
const el = ref.current;
if (!el) return;
@@ -37,7 +39,8 @@ export default function ScrollFadeIn({ children, className = "", delay = 0 }: Sc
observer.unobserve(el);
}
},
- { threshold: 0.1 }
+ // Start a little before the section is fully in view so it never feels late
+ { threshold: 0.1, rootMargin: "0px 0px -60px 0px" }
);
observer.observe(el);
@@ -50,8 +53,8 @@ export default function ScrollFadeIn({ children, className = "", delay = 0 }: Sc
className={className}
style={hasMounted ? {
opacity: isVisible ? 1 : 0,
- transform: isVisible ? "translateY(0)" : "translateY(30px)",
- transition: `opacity 0.6s ease ${delay}s, transform 0.6s ease ${delay}s`,
+ transform: isVisible || reduceMotion ? "translateY(0)" : "translateY(16px)",
+ transition: `opacity 0.6s var(--ease-out) ${delay}s, transform 0.6s var(--ease-out) ${delay}s`,
} : undefined}
>
{children}
diff --git a/app/components/ThemeToggle.tsx b/app/components/ThemeToggle.tsx
index 80479b2..2d37ab9 100644
--- a/app/components/ThemeToggle.tsx
+++ b/app/components/ThemeToggle.tsx
@@ -24,7 +24,7 @@ export function ThemeToggle() {
return (