Todos los componentes headers
headers1
Un bot贸n principal destacado
"use client";
import React, { useEffect, useRef, useState } from "react";
const Navbar: React.FC = () => {
const [scrolled, setScrolled] = useState(false);
const triggerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const trigger = triggerRef.current;
if (!trigger) return;
const observer = new IntersectionObserver(
([entry]) => {
setScrolled(!entry.isIntersecting);
},
{
root: null,
threshold: 0,
}
);
observer.observe(trigger);
return () => observer.disconnect();
}, []);
return (
<div className="w-full relative">
{/* Invisible div para detectar el scroll */}
<div ref={triggerRef} className="h-1 w-full absolute top-0" />
{/* Navbar */}
<header
className={`sticky top-0 z-50 h-20 transition-all duration-300 ease-in-out ${
scrolled
? "bg-cyan-200 shadow-lg text-white"
: "bg-white text-black"
}`}
>
<div className="mx-auto h-full px-6 flex items-center justify-between max-w-7xl">
<span className="text-xl font-bold sm:text-2xl md:text-3xl">
Sticky Navbar
</span>
<nav className="space-x-2 text-sm font-medium sm:space-x-4 md:text-base">
<a href="#" className="hover:underline">Inicio</a>
<a href="#" className="hover:underline">Servicios</a>
<a href="#" className="hover:underline">Contacto</a>
</nav>
</div>
</header>
{/* Contenido de prueba con fondo contrastado para que se note el scroll */}
<div className="bg-gray-100 min-h-[200vh] p-52 text-center text-gray-600">
<p className="text-xl">Desliza hacia abajo para ver el cambio de color 馃憞</p>
</div>
</div>
);
};
export default Navbar;
headers2
Un bot贸n principal destacado
"use client";
import React, { useEffect, useRef, useState } from "react";
// Componente reutilizable que se anima al entrar en viewport
const AnimatedScrollBlock: React.FC<{
children: React.ReactNode;
root?: HTMLElement | null;
threshold?: number;
}> = ({ children, root = null, threshold = 0.3 }) => {
const blockRef = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const el = blockRef.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) setVisible(true);
},
{
root,
threshold,
}
);
observer.observe(el);
return () => observer.disconnect();
}, [root, threshold]);
return (
<div
ref={blockRef}
className={`transition-all duration-700 ease-out transform ${
visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-10"
}`}
>
{children}
</div>
);
};
// Ejemplo visual
const AnimatedScrollExample = () => {
return (
<div className="space-y-64 p-6">
<div className="text-center text-gray-400 pt-72">Desliza hacia abajo 馃憞</div>
<AnimatedScrollBlock>
<div className="bg-emerald-100 p-8 rounded-xl shadow text-center">
馃憢 隆Hola! Aparezco con animaci贸n cuando entro en el viewport.
</div>
</AnimatedScrollBlock>
<AnimatedScrollBlock>
<div className="bg-sky-100 p-8 rounded-xl shadow text-center">
馃帀 Y yo tambi茅n aparezco m谩s abajo.
</div>
</AnimatedScrollBlock>
</div>
);
};
export default AnimatedScrollExample;