diff --git a/frontend/src/components/Animation/AnimatedCard.tsx b/frontend/src/components/Animation/AnimatedCard.tsx
new file mode 100644
index 0000000..dad0cc1
--- /dev/null
+++ b/frontend/src/components/Animation/AnimatedCard.tsx
@@ -0,0 +1,12 @@
+import type { ReactNode } from "react";
+import AnimationWrapper from "./AnimationWrapper";
+
+interface AnimatedCardProps {
+ children: ReactNode;
+ delay?: number;
+ className?: string;
+}
+
+export default function AnimatedCard(props: AnimatedCardProps) {
+ return ;
+}
\ No newline at end of file
diff --git a/frontend/src/components/Animation/AnimationWrapper.tsx b/frontend/src/components/Animation/AnimationWrapper.tsx
new file mode 100644
index 0000000..e0a9529
--- /dev/null
+++ b/frontend/src/components/Animation/AnimationWrapper.tsx
@@ -0,0 +1,57 @@
+import type { ReactNode } from "react";
+import { motion } from "framer-motion";
+
+import { fadeIn, slideUp, slideLeft, scaleIn } from "@/utils/animationConfig";
+import { useInViewAnimation } from "@/hooks/useInViewAnimation";
+
+type AnimationType = "fade" | "slideUp" | "slideLeft" | "scale";
+
+interface AnimationWrapperProps {
+ children: ReactNode;
+ type?: AnimationType;
+ delay?: number;
+ duration?: number;
+ className?: string;
+ once?: boolean;
+}
+
+const variantsMap = {
+ fade: fadeIn,
+ slideUp: slideUp,
+ slideLeft: slideLeft,
+ scale: scaleIn,
+};
+
+export default function AnimationWrapper({
+ children,
+ type = "fade",
+ delay = 0,
+ duration,
+ className,
+ once = true,
+}: AnimationWrapperProps) {
+ const { ref, isInView, prefersReducedMotion } =
+ useInViewAnimation({ once });
+
+ if (prefersReducedMotion) {
+ return
{children}
;
+ }
+
+ const variants = variantsMap[type];
+
+ return (
+ }
+ className={className}
+ variants={variants}
+ initial="hidden"
+ animate={isInView ? "visible" : "hidden"}
+ transition={{
+ delay,
+ ...(duration ? { duration } : {}),
+ }}
+ >
+ {children}
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/components/Animation/FadeIn.tsx b/frontend/src/components/Animation/FadeIn.tsx
new file mode 100644
index 0000000..7f5dbec
--- /dev/null
+++ b/frontend/src/components/Animation/FadeIn.tsx
@@ -0,0 +1,13 @@
+import type { ReactNode } from "react";
+import AnimationWrapper from "./AnimationWrapper";
+
+interface FadeInProps {
+ children: ReactNode;
+ delay?: number;
+ duration?: number;
+ className?: string;
+}
+
+export default function FadeIn(props: FadeInProps) {
+ return ;
+}
\ No newline at end of file
diff --git a/frontend/src/components/Animation/SlideIn.tsx b/frontend/src/components/Animation/SlideIn.tsx
new file mode 100644
index 0000000..b706ebf
--- /dev/null
+++ b/frontend/src/components/Animation/SlideIn.tsx
@@ -0,0 +1,13 @@
+import type { ReactNode } from "react";
+import AnimationWrapper from "./AnimationWrapper";
+
+interface SlideInProps {
+ children: ReactNode;
+ delay?: number;
+ duration?: number;
+ className?: string;
+}
+
+export default function SlideIn(props: SlideInProps) {
+ return ;
+}
\ No newline at end of file
diff --git a/frontend/src/hooks/useInViewAnimation.ts b/frontend/src/hooks/useInViewAnimation.ts
new file mode 100644
index 0000000..32e17c3
--- /dev/null
+++ b/frontend/src/hooks/useInViewAnimation.ts
@@ -0,0 +1,28 @@
+import { useRef } from "react";
+import { useInView, useReducedMotion } from "framer-motion";
+
+interface UseInViewAnimationOptions {
+ once?: boolean;
+ amount?: "some" | "all" | number;
+}
+
+export const useInViewAnimation = (
+ options: UseInViewAnimationOptions = {}
+) => {
+ const { once = true, amount = 0.2 } = options;
+
+ const ref = useRef(null);
+
+ const isInView = useInView(ref, {
+ once,
+ amount,
+ });
+
+ const prefersReducedMotion = useReducedMotion();
+
+ return {
+ ref,
+ isInView,
+ prefersReducedMotion,
+ };
+};
\ No newline at end of file
diff --git a/frontend/src/utils/animationConfig.ts b/frontend/src/utils/animationConfig.ts
new file mode 100644
index 0000000..ecb8245
--- /dev/null
+++ b/frontend/src/utils/animationConfig.ts
@@ -0,0 +1,58 @@
+import type { Variants } from "framer-motion";
+export const fadeIn: Variants = {
+ hidden: {
+ opacity: 0,
+ },
+ visible: {
+ opacity: 1,
+ transition: {
+ duration: 0.5,
+ ease: "easeOut",
+ },
+ },
+};
+
+export const slideUp: Variants = {
+ hidden: {
+ opacity: 0,
+ y: 20,
+ },
+ visible: {
+ opacity: 1,
+ y: 0,
+ transition: {
+ duration: 0.5,
+ ease: "easeOut",
+ },
+ },
+};
+
+export const slideLeft: Variants = {
+ hidden: {
+ opacity: 0,
+ x: 20,
+ },
+ visible: {
+ opacity: 1,
+ x: 0,
+ transition: {
+ duration: 0.5,
+ ease: "easeOut",
+ },
+ },
+};
+
+export const scaleIn: Variants = {
+ hidden: {
+ opacity: 0,
+ scale: 0.95,
+ },
+ visible: {
+ opacity: 1,
+ scale: 1,
+ transition: {
+ duration: 0.45,
+ ease: "easeOut",
+ },
+ },
+};
\ No newline at end of file