Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nicolasgrajaleshoyos/portafolio/llms.txt

Use this file to discover all available pages before exploring further.

The Hero component is the first section a visitor sees, spanning the full viewport height (min-h-screen). It opens with an animated headline and a continuously cycling typing effect that highlights Nicolas’s roles, a circular profile photo wrapped in a gradient glow, and a colour-shifting gradient background. The entire section fades and slides into view once the browser’s IntersectionObserver confirms it is on screen.

Props

Hero accepts no external props. All data — typed words, profile photo URL, and gradient classes — is defined internally.

useTypingEffect Hook

The typing animation is powered by a custom hook defined at the top of Hero.tsx. It cycles through an array of strings, typing each character at typeSpeed ms/char, pausing for delay ms once the word is complete, then deleting at deleteSpeed ms/char before moving to the next word.
const useTypingEffect = (
  words: string[],
  typeSpeed = 150,
  deleteSpeed = 100,
  delay = 1000
): string => {
  const [text, setText] = useState('');
  const [isDeleting, setIsDeleting] = useState(false);
  const [wordIndex, setWordIndex] = useState(0);
  // ... effect logic
  return text;
};
The hook’s parameter defaults (typeSpeed = 150, deleteSpeed = 100, delay = 1000) are the fallback values embedded in the function signature. The call site in Hero overrides all three with explicit arguments. The hook is called in Hero with these arguments:
const typedText = useTypingEffect(
  [
    'Desarrollador full stack',
    'Creador de experiencias digitales',
    'Soy un entusiasta de las monedas digitales y el hacking ético',
  ],
  100,  // typeSpeed  (ms per character)
  50,   // deleteSpeed (ms per character)
  2000  // delay after full word (ms)
);
The returned typedText string is rendered next to a blinking cursor element (animate-blink).

Behavior

Fade-in via IntersectionObserver

A useRef attaches to the <section> element. An IntersectionObserver with threshold: 0.1 watches it; as soon as 10 % of the section enters the viewport, isVisible flips to true and is never reset (the observer is disconnected after the first trigger). This drives two transitions:
ElementHidden stateVisible state
Section wrapperopacity-0opacity-100
Text columntranslate-y-10 opacity-0translate-y-0 opacity-100
Photo columnscale-90 opacity-0scale-100 opacity-100 (200 ms delay)

Gradient background

An absolutely-positioned <div> fills the section with a dual-mode gradient that continuously animates position:
bg-gradient-to-br
  from-cyan-100 via-white to-blue-100          ← light mode
  dark:from-slate-900 dark:via-dark dark:to-slate-800
bg-[length:200%_200%] animate-gradient-shift

Profile photo and glow ring

The profile image is served from the GitHub avatar CDN:
https://avatars.githubusercontent.com/u/130097149?v=4?s=400
The circular photo is wrapped in a group container that drives two simultaneous animations:
  • animate-float — a gentle vertical bob on the outer div.
  • animate-tilt — a subtle 3-D tilt on the gradient glow ring behind the photo.
The glow ring uses:
bg-gradient-to-r from-cyan-400 to-blue-500
blur-lg opacity-50 group-hover:opacity-100
The image itself scales up on hover via group-hover:scale-110.

Usage Example

Hero is placed directly inside <main> in App.tsx, immediately after the fixed Header:
import Hero from '@/components/Hero';

const App: React.FC = () => (
  <div className="flex flex-col min-h-screen">
    <Header theme={theme} toggleTheme={toggleTheme} />
    <main className="flex-grow">
      <Hero />
      {/* other sections */}
    </main>
  </div>
);

Customization

Edit the array passed to useTypingEffect inside Hero.tsx. Each string will be typed, paused, and deleted in order, looping indefinitely:
const typedText = useTypingEffect(
  [
    'Backend Engineer',
    'Open-Source Contributor',
    'Cloud Architecture enthusiast',
  ],
  100, 50, 2000
);
The src attribute of the <img> tag points to the GitHub avatar CDN. Swap it for any publicly accessible URL or a local asset:
<img
  src="/images/my-photo.jpg"
  alt="Foto de perfil"
  className="w-full h-full object-cover ..."
/>
The background gradient classes live on the absolutely-positioned overlay <div>. Change from-*, via-*, and to-* Tailwind tokens to match your brand palette:
from-violet-100 via-white to-purple-100
dark:from-slate-900 dark:via-dark dark:to-violet-950
Pass different millisecond values when calling the hook. Lower numbers type/delete faster; higher numbers are slower and more dramatic:
const typedText = useTypingEffect(words, 80, 40, 1500);
//                                       ↑   ↑   ↑
//                                typeSpeed deleteSpeed delay

Build docs developers (and LLMs) love