feat(ui): implement mobile navigation and rotating text

Add a responsive mobile navigation menu to the `Header` component, allowing users to easily access different sections of the portfolio on smaller screens. This includes a toggle button for opening/closing the menu and smooth animations for its appearance.

Introduce a `RotatingText` component in the `Hero` section to dynamically display different professional titles, enhancing the visual appeal and conveying diverse skills.

Update `package.json` to include `framer-motion` as a dependency, which is utilized for the new animations.
This commit is contained in:
¨evandrodevbr¨ committed 2025-09-03 01:47:27 -03:00
1 parent 780fd7c161
commit 6fe280cea1
7 files changed
+729 -27

No files matched your search

+62 -22
View File
@@ -1,9 +1,16 @@
"use client";
import { motion } from "motion/react";
import { CalendarIcon, HomeIcon, MailIcon, PencilIcon } from "lucide-react";
import {
CalendarIcon,
HomeIcon,
MailIcon,
MenuIcon,
PencilIcon,
XIcon,
} from "lucide-react";
import Link from "next/link";
import React from "react";
import React, { useState } from "react";
import { cn } from "@/lib/utils";
import { Dock, DockIcon } from "@/components/magicui/dock";
@@ -86,6 +93,21 @@ const DATA = {
};
export function Header() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const toggleMenu = () => {
setIsMenuOpen(!isMenuOpen);
};
const navItems = [
{ href: "#", label: "Home" },
{ href: "#about", label: "Sobre" },
{ href: "#skills", label: "Habilidades" },
{ href: "#experience", label: "Experiência" },
{ href: "#projects", label: "Projetos" },
{ href: "#contact", label: "Contato" },
];
return (
<motion.header
initial={{ y: -100, opacity: 0 }}
@@ -106,20 +128,14 @@ export function Header() {
</span>
</motion.div>
{/* Desktop Navigation */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8, delay: 0.3 }}
className="flex items-center space-x-8"
className="hidden md:flex items-center space-x-8"
>
{[
{ href: "#", label: "Home" },
{ href: "#about", label: "Sobre" },
{ href: "#skills", label: "Habilidades" },
{ href: "#experience", label: "Experiência" },
{ href: "#projects", label: "Projetos" },
{ href: "#contact", label: "Contato" },
].map((item, index) => (
{navItems.map((item, index) => (
<motion.a
key={item.href}
href={item.href}
@@ -132,20 +148,44 @@ export function Header() {
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-gradient-to-r from-cyan-400 to-blue-500 group-hover:w-full transition-all duration-300"></span>
</motion.a>
))}
<motion.a
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, delay: 1 }}
href="/cv.pdf"
className="hidden bg-gradient-to-r from-blue-600 to-cyan-600 hover:from-blue-700 hover:to-cyan-700 text-white px-4 py-2 rounded-lg transition-all duration-300 hover:scale-105 hover:shadow-lg hover:shadow-blue-500/25"
download
>
Download CV
</motion.a>
</motion.div>
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center">
<button
onClick={toggleMenu}
className="text-gray-300 hover:text-white focus:outline-none"
>
{isMenuOpen ? <XIcon size={24} /> : <MenuIcon size={24} />}
</button>
</div>
</div>
</nav>
{/* Mobile Menu */}
{isMenuOpen && (
<motion.div
initial={{ opacity: 0, y: -50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -50 }}
transition={{ duration: 0.3, ease: "easeOut" }}
className="md:hidden bg-black/70 backdrop-blur-md absolute top-full left-0 w-full py-4 px-4 flex flex-col items-center space-y-4"
>
{navItems.map((item, index) => (
<motion.a
key={item.href}
href={item.href}
onClick={toggleMenu} // Close menu on item click
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
className="text-gray-300 hover:text-white transition-all duration-300 hover:scale-105 relative group text-lg"
>
{item.label}
</motion.a>
))}
</motion.div>
)}
</motion.header>
);
}
+13 -5
View File
@@ -5,6 +5,7 @@ import Image from "next/image";
import { AuroraText } from "@/components/magicui/aurora-text";
import { AnimatedGradientText } from "@/components/magicui/animated-gradient-text";
import { useEffect } from "react";
import { RotatingText } from "@/components/RotatingText";
export default function Hero() {
const getGreetingMessage = () => {
@@ -70,12 +71,19 @@ export default function Hero() {
transition={{ duration: 0.8, delay: 0.2 }}
className="mb-8"
>
<h1 className="text-3xl md:text-5xl lg:text-6xl font-black mb-6 leading-tight">
<AuroraText>Evandro Fonseca Junior</AuroraText>
<h1 className="text-3xl md:text-5xl lg:text-6xl font-black mb-6 leading-tight text-gray-200">
Evandro Fonseca Junior
</h1>
<AnimatedGradientText className="text-xl md:text-2xl lg:text-3xl font-bold">
Desenvolvedor Full-Stack
</AnimatedGradientText>
<RotatingText
words={[
"Desenvolvedor Full-Stack",
"Desenvolvedor de Software",
"Familiarizado em UI/UX",
"Foco em resolução",
]}
className="text-xl md:text-2xl lg:text-3xl font-bold bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-transparent"
interval={4000}
/>
</motion.div>
{/* Description */}