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:
1 parent
780fd7c161
commit
6fe280cea1
7 files changed
+729
-27
No files matched your search
+62
-22
@@ -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
@@ -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 */}
|
||||
|
||||
Reference in new issue
Block a user