-
-
- {eyebrow &&
{eyebrow}
}
-
{title}
- {description && (
-
{description}
- )}
+
+
+
+ {eyebrow &&
{eyebrow}
}
+
{title}
+ {description && (
+
{description}
+ )}
+
+ {aside &&
}
- {aside && }
-
-
{children}
+
+ {!disableChildrenAnimation ? (
+
+ {children}
+
+ ) : (
+
{children}
+ )}
);
diff --git a/app/components/Skills.tsx b/app/components/Skills.tsx
index 9136771..4f8ed39 100644
--- a/app/components/Skills.tsx
+++ b/app/components/Skills.tsx
@@ -2,6 +2,7 @@
import { usePortfolioData } from "@/lib/usePortfolioData";
import { Section } from "./Section";
+import { Reveal } from "./Reveal";
export default function Skills() {
const { data, loading, error } = usePortfolioData();
@@ -18,23 +19,26 @@ export default function Skills() {
title="Stack modular para produto, operação e escala"
description="Ferramentas escolhidas por previsibilidade operacional, velocidade de entrega e facilidade de manutenção."
dark
+ disableChildrenAnimation
>
{data.skills.technical.map((category, index) => (
-
-
-
ID_0{index + 1}X
-
{category.skills.length} items
-
- {category.category}
-
- {category.skills.map((skill) => (
-
- {skill}
-
- ))}
-
-
+
+
+
+
ID_0{index + 1}X
+
{category.skills.length} items
+
+ {category.category}
+
+ {category.skills.map((skill) => (
+
+ {skill}
+
+ ))}
+
+
+
))}
diff --git a/app/components/WorkModalities.tsx b/app/components/WorkModalities.tsx
index d058ce9..a30dc9f 100644
--- a/app/components/WorkModalities.tsx
+++ b/app/components/WorkModalities.tsx
@@ -2,6 +2,7 @@
import { useState } from "react";
import { Section } from "./Section";
+import { Reveal } from "./Reveal";
interface Tier {
id: string;
@@ -268,6 +269,7 @@ export function WorkModalities() {
eyebrow="CONTRACT_PROTOCOLS.sys"
title="Engenharia sob demanda. Previsibilidade de entrega sem overhead."
description="Três níveis de contratação desenhados como uma esteira de valor — do diagnóstico rápido à parceria estratégica de longo prazo."
+ disableChildrenAnimation
>
- {TIERS.map((tier) => (
-
+ {TIERS.map((tier, index) => (
+
+
+
))}
diff --git a/app/hooks/useScrollReveal.ts b/app/hooks/useScrollReveal.ts
new file mode 100644
index 0000000..a9af952
--- /dev/null
+++ b/app/hooks/useScrollReveal.ts
@@ -0,0 +1,48 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+import { gsap } from "gsap";
+import { ScrollTrigger } from "gsap/ScrollTrigger";
+
+export function useScrollReveal(options?: {
+ y?: number;
+ duration?: number;
+ delay?: number;
+ ease?: string;
+ start?: string;
+ end?: string;
+ scrub?: boolean | number;
+}) {
+ const ref = useRef
(null);
+
+ useEffect(() => {
+ if (!ref.current) return;
+
+ // Lazy registration to avoid Next.js hydration mismatch issues
+ gsap.registerPlugin(ScrollTrigger);
+
+ const ctx = gsap.context(() => {
+ gsap.fromTo(
+ ref.current,
+ { y: options?.y ?? 60, opacity: 0 },
+ {
+ y: 0,
+ opacity: 1,
+ duration: options?.duration ?? 0.9,
+ delay: options?.delay ?? 0,
+ ease: options?.ease ?? "power3.out",
+ scrollTrigger: {
+ trigger: ref.current,
+ start: options?.start ?? "top 85%",
+ end: options?.end ?? "top 50%",
+ scrub: options?.scrub ?? false,
+ },
+ }
+ );
+ }, ref);
+
+ return () => ctx.revert();
+ }, [options?.y, options?.duration, options?.delay, options?.ease, options?.start, options?.end, options?.scrub]);
+
+ return ref;
+}
diff --git a/app/layout.tsx b/app/layout.tsx
index 5f6c1df..5bff897 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,9 +1,11 @@
import type { Metadata, Viewport } from "next";
import { ReactNode } from "react";
import GoogleAnalytics from "./components/GoogleAnalytics";
+import { SmoothScrollProvider } from "./providers/SmoothScrollProvider";
import "./globals.css";
export const metadata: Metadata = {
+ // ... existing metadata ...
title:
"Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
description:
@@ -137,12 +139,14 @@ export default function RootLayout({ children }: Readonly) {
-
- {children}
-
+
+
+ {children}
+
+