cover

Esta es la <navbar> que está de moda

author photo

@Héctorbliss

@blissito

En este video, vamos a animar una barra de navegación, para que se sienta todavía mejor. 👨🏻‍💻

Primera parte: Maquetado

<AnimatedNavBar> será nuestro componente principal, pero se armará de otros cuatro, tres visibles y un <Button> por debajo.

// app/components/AnimatedNavBar.tsx export const AnimatedNavBar = () => { return ( <nav className="px-4 mx-auto max-w-5xl flex justify-around items-center h-14 bg-white"> <Logo /> <Menu /> <SignInButtons /> </nav> ); };

Vamos a crear un componente <Button> base para poder reusarlo asignándole variantes.

const Button = ({ children, className, mode = "ghost", chevron, }: { chevron?: boolean; mode?: "ghost" | "solid" | "shadow" | "primary"; className?: string; children: ReactNode; }) => { return ( <button className={cn( "py-1 px-3", "group", "hover:bg-[#1717170D]", "text-xs font-medium", "flex items-center gap-1", "rounded-lg transition-all", { "bg-transparent": mode === "ghost", "bg-[#1717170D]": mode === "solid", "border shadow-xs": mode === "shadow", "border bg-black text-white": mode === "primary", "hover:bg-black hover:ring-2 ring-black": mode === "primary", }, className )} > <span>{children}</span> {chevron && ( <span className="text-[7px] group-hover:rotate-180 transition-all"> <FaChevronDown /> </span> )} </button> ); };

Este es el componente más robusto y complejo, pero su robustez nos permite crear el resto de los componentes de manera más compacta. ▪️

const Menu = () => { return ( <section className="flex"> <Button chevron mode="solid"> Producto </Button> <Button chevron>Recursos</Button> <Button chevron>Empresa</Button> <Button>Industria</Button> <Button>Precios</Button> </section> ); }; const SignInButtons = () => { return ( <Form className="flex gap-1"> <Button mode="shadow">Log in</Button> <Button mode="primary">Sign up</Button> </Form> ); };

Con unas cuántas utilidades de TailwindCSS, estamos listos para añadir el panel y luego su animación.

Segunda parte: Los paneles de opciones

He tenido que construir los tres diferentes paneles. Con el propósito de no detenernos demasiado en el maquetado, te dejo el código en los enlaces y por ahora los usaremos importándolos.

export const AnimatedNavBar = () => { const [hover, setHover] = useState(""); return ( <nav onMouseLeave={() => setHover("")} className={cn( "relative", "px-4 mx-auto max-w-5xl flex justify-around items-center h-14 bg-white" )} > <Logo /> <Menu onHover={(name: string) => setHover(name)} /> <SignInButtons /> <Panel id={hover} direction={hover === "producto" ? -1 : 1} layout={ hover === "producto" ? ( <ProductLayout /> ) : hover === "recursos" ? ( <ResourcesLayout /> ) : hover === "empresa" ? ( <CompanyLayout /> ) : null } /> </nav> ); };

Nuestro componente <Panel> queda así y estamos listos para concentrarnos en lo mero bueno: las animaciones. 🤓🪄

Tercera parte: Animación con layout

Vamos a fingir, como le hacemos siempre en la vida pero ahora en el tutorial. Vamos a simular que el contenedor cambia de tamaño según se navega entre botones, pero será falso, solo parecerá que se reajusta pero en realidad es la misma animación una y otra vez. ➿

const Panel = ({ direction = 1, id, layout, currentHover, }: { currentHover?: string; direction?: number; id: string; layout: ReactNode; }) => { return currentHover === "" ? null : ( <motion.section transition={{ type: "spring", bounce: 0.2 }} initial={{ width: "672px", height: 300, x: direction * 10, filter: "blur(1px)", }} animate={{ width: "auto", height: "auto", x: 0, filter: "blur(0px)" }} key={id} className={cn( "max-w-2xl", "rounded-3xl", "overflow-hidden", "absolute border bg-white h-max shadow top-14" )} > {layout} </motion.section> ); };

Para lograrlo, hemos transformado el panel en un componente <motion> al que se le han dado los props: animate e initial. Así como una key que cambiará detonando las animaciones. Animaremos con height y width ”auto". Necesitamos de un prop: currentHover, para que nos ayude a saber si mostrar el layout o mejor devolvemos null. 🤔

Ahí está, pues. Fíjate con qué poco se puede hacer tanto. 🪄✨💬

Abrazo. Bliss. 🤓

Enlaces relacionados

Aquí está todo el código

Inspiración

¡Nuevo curso!

Animaciones web con React + Motion 🧙🏻