feat: Implement initial portfolio website structure with core components and a new post creator tool.

This commit is contained in:
Evandro Fonseca Junior committed 2026-03-07 06:50:56 -03:00
1 parent 036363b521
commit aa07d6faeb
15 files changed
+1337 -1855

No files matched your search

+106 -150
View File
@@ -1,171 +1,127 @@
"use client";
"use client";
import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { MenuIcon, XIcon } from "lucide-react";
import Image from "next/image";
import { cn } from "@/lib/utils";
import { useState } from "react";
import { Logo } from "./Logo";
// Definição de Tipos para consistência de dados
interface NavItem {
href: string;
label: string;
}
const navItems: NavItem[] = [
const navItems = [
{ href: "#home", label: "Home" },
{ href: "#about", label: "Sobre" },
{ href: "#skills", label: "Habilidades" },
{ href: "#experience", label: "Experiência" },
{ href: "#experience", label: "Experiencia" },
{ href: "#projects", label: "Projetos" },
{ href: "#modalities", label: "Contratacao" },
{ href: "#contact", label: "Contato" },
];
export function Header() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
// Hook de detecção de scroll para mudança de estado do Header
useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > 20);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
return (
<>
<motion.header
initial={{ y: -100 }}
animate={{ y: 0 }}
className={cn(
"fixed top-0 left-0 right-0 z-[100] transition-all duration-500 ease-in-out px-6 py-4",
scrolled
? "bg-black/40 backdrop-blur-xl border-b border-white/5 py-3"
: "bg-transparent py-6"
)}
>
<nav className="max-w-7xl mx-auto flex items-center justify-between">
{/* Logo com Identidade Visual de Engenheiro */}
<motion.a
href="#home"
onClick={(e) => {
e.preventDefault();
const element = document.querySelector("#home");
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}}
whileHover={{ scale: 1.05 }}
className="flex items-center gap-2 cursor-pointer"
<header
className="fixed left-0 right-0 top-0 z-50 border-b backdrop-blur-xl"
style={{
borderColor: "rgba(255,255,255,.1)",
background: "rgba(10,10,10,.96)",
}}
>
<div className="mx-auto flex h-[52px] w-full max-w-[1240px] items-center justify-between px-6 lg:px-16">
<a href="#home" className="flex items-center gap-3 no-underline">
<Logo variant="mark" size="sm" theme="dark" />
<span
className="font-display text-sm font-extrabold tracking-tight"
style={{ color: "#FFFFFF" }}
>
<div className="w-8 h-8 flex items-center justify-center">
<Image
src="/edev_logo.svg"
alt="Evandro.dev Logo"
width={32}
height={32}
className="w-8 h-8"
priority
/>
</div>
<span className="text-xl font-black tracking-tighter text-white">
EVANDRO<span className="text-blue-500">.DEV</span>
</span>
</motion.a>
evandro<span style={{ color: "#E8FF00" }}>.dev.br</span>
</span>
</a>
{/* Desktop Navigation - Focada em Espaçamento Ótimo */}
<div className="hidden md:flex items-center gap-1 bg-white/5 p-1 rounded-full border border-white/10 backdrop-blur-md">
<nav className="hidden items-center lg:flex">
<ul className="flex list-none items-center gap-2">
{navItems.map((item) => (
<a
key={item.href}
href={item.href}
onClick={(e) => {
e.preventDefault();
const element = document.querySelector(item.href);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}}
className="px-5 py-2 text-xs font-bold uppercase tracking-widest text-slate-300 hover:text-white hover:bg-white/10 rounded-full transition-all duration-300 cursor-pointer"
>
{item.label}
</a>
<li key={item.href}>
<a
href={item.href}
className="rounded-sm px-3 py-2 text-[10px] uppercase tracking-[0.14em] no-underline transition-all duration-200 hover:bg-white/10"
style={{ color: "rgba(255,255,255,.86)" }}
>
{item.label}
</a>
</li>
))}
</div>
{/* Botão de Ação CTA (Call to Action) */}
<div className="hidden md:block">
<a
href="/Profile.pdf"
download="Evandro_Fonseca_Junior_CV.pdf"
className="px-6 py-2 bg-white text-black text-xs font-bold uppercase tracking-widest rounded-full hover:bg-blue-500 hover:text-white transition-all duration-500 inline-block"
>
Download CV
</a>
</div>
{/* Mobile Toggle */}
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="md:hidden p-2 text-white bg-white/5 rounded-lg border border-white/10"
>
{isMenuOpen ? <XIcon size={20} /> : <MenuIcon size={20} />}
</button>
</ul>
</nav>
</motion.header>
{/* Mobile Menu Overlay com Framer Motion para fluidez */}
<AnimatePresence>
{isMenuOpen && (
<motion.div
initial={{ opacity: 0, x: "100%" }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: "100%" }}
transition={{ type: "spring", damping: 25, stiffness: 200 }}
className="fixed inset-0 z-[110] bg-black/95 backdrop-blur-2xl md:hidden flex flex-col items-center justify-center gap-8"
<div className="flex items-center gap-4">
<span
className="hidden rounded-full border px-[10px] py-1 text-[9px] uppercase tracking-[0.12em] lg:inline-block"
style={{
borderColor: "rgba(255,255,255,.15)",
color: "rgba(255,255,255,.35)",
}}
>
<button
onClick={() => setIsMenuOpen(false)}
className="absolute top-8 right-8 p-4 text-white/50 hover:text-white"
>
<XIcon size={32} />
</button>
{navItems.map((item, i) => (
<motion.a
key={item.href}
href={item.href}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: i * 0.1 }}
onClick={(e) => {
e.preventDefault();
setIsMenuOpen(false);
const element = document.querySelector(item.href);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}}
className="text-3xl font-black text-white uppercase tracking-tighter hover:text-blue-500 transition-colors cursor-pointer"
>
{item.label}
</motion.a>
v1.0 / 2026
</span>
<a
href="/Profile.pdf"
download="Evandro_Fonseca_Junior_CV.pdf"
className="hidden rounded-sm px-4 py-2 text-[10px] font-semibold uppercase tracking-[0.1em] no-underline transition-all duration-200 hover:shadow-[0_0_0_1px_rgba(232,255,0,.8),0_8px_24px_rgba(232,255,0,.35)] lg:inline-flex"
style={{ background: "#E8FF00", color: "#0A0A0A" }}
>
Download CV
</a>
<button
type="button"
className="flex h-8 w-8 flex-col items-center justify-center gap-1.5 border-none bg-transparent lg:hidden"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
aria-label="Menu"
>
<span
className={`h-0.5 w-5 bg-white transition-transform duration-200 ${mobileMenuOpen ? "translate-y-2 rotate-45" : ""}`}
/>
<span
className={`h-0.5 w-5 bg-white transition-opacity duration-200 ${mobileMenuOpen ? "opacity-0" : ""}`}
/>
<span
className={`h-0.5 w-5 bg-white transition-transform duration-200 ${mobileMenuOpen ? "-translate-y-2 -rotate-45" : ""}`}
/>
</button>
</div>
</div>
<div
className={`absolute left-0 right-0 top-[52px] overflow-hidden border-b backdrop-blur-xl transition-all duration-300 lg:hidden ${mobileMenuOpen ? "max-h-[420px] opacity-100" : "max-h-0 opacity-0"}`}
style={{
borderColor: "rgba(255,255,255,.1)",
background: "rgba(10,10,10,.98)",
}}
>
<nav className="px-6 py-4">
<ul className="flex list-none flex-col gap-2">
{navItems.map((item) => (
<li key={item.href}>
<a
href={item.href}
className="block rounded-sm px-3 py-3 text-[11px] uppercase tracking-[0.14em] no-underline transition-all duration-200 hover:bg-white/10"
style={{ color: "rgba(255,255,255,.9)" }}
onClick={() => setMobileMenuOpen(false)}
>
{item.label}
</a>
</li>
))}
<motion.a
href="/Profile.pdf"
download="Evandro_Fonseca_Junior_CV.pdf"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: navItems.length * 0.1 }}
className="mt-8 px-8 py-4 bg-white text-black text-xl font-black uppercase tracking-tighter rounded-full hover:bg-blue-500 hover:text-white transition-all duration-300"
>
Download CV
</motion.a>
</motion.div>
)}
</AnimatePresence>
</>
<li>
<a
href="/Profile.pdf"
download="Evandro_Fonseca_Junior_CV.pdf"
className="mt-2 inline-flex rounded-sm px-4 py-3 text-[10px] font-semibold uppercase tracking-[0.1em] no-underline"
style={{ background: "#E8FF00", color: "#0A0A0A" }}
>
Download CV
</a>
</li>
</ul>
</nav>
</div>
</header>
);
}
}