Commit e35822e9 authored by Lê Ngọc Tú's avatar Lê Ngọc Tú

finish portfolio

parent f7f61b6a
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg'
import heroImg from './assets/hero.png'
import React from 'react';
import Navbar from './components/Navbar';
import Hero from './components/Hero';
import About from './components/About';
import Skills from './components/Skills';
import Experience from './components/Experience';
import Projects from './components/Projects';
import Footer from './components/Footer';
function App() {
const [count, setCount] = useState(0)
return (
<>
hello world
</>
)
<div className="min-h-screen bg-slate-50">
<Navbar />
<main>
<Hero />
<About />
<Skills />
<Experience />
<Projects />
</main>
<Footer />
</div>
);
}
export default App
export default App;
<svg width="98" height="96" viewBox="0 0 98 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_730_27126)">
<path d="M41.4395 69.3848C28.8066 67.8535 19.9062 58.7617 19.9062 46.9902C19.9062 42.2051 21.6289 37.0371 24.5 33.5918C23.2559 30.4336 23.4473 23.7344 24.8828 20.959C28.7109 20.4805 33.8789 22.4902 36.9414 25.2656C40.5781 24.1172 44.4062 23.543 49.0957 23.543C53.7852 23.543 57.6133 24.1172 61.0586 25.1699C64.0254 22.4902 69.2891 20.4805 73.1172 20.959C74.457 23.543 74.6484 30.2422 73.4043 33.4961C76.4668 37.1328 78.0937 42.0137 78.0937 46.9902C78.0937 58.7617 69.1934 67.6621 56.3691 69.2891C59.623 71.3945 61.8242 75.9883 61.8242 81.252L61.8242 91.2051C61.8242 94.0762 64.2168 95.7031 67.0879 94.5547C84.4102 87.9512 98 70.6289 98 49.1914C98 22.1074 75.9883 6.69539e-07 48.9043 4.309e-07C21.8203 1.92261e-07 -1.9479e-07 22.1074 -4.3343e-07 49.1914C-6.20631e-07 70.4375 13.4941 88.0469 31.6777 94.6504C34.2617 95.6074 36.75 93.8848 36.75 91.3008L36.75 83.6445C35.4102 84.2188 33.6875 84.6016 32.1562 84.6016C25.8398 84.6016 22.1074 81.1563 19.4277 74.7441C18.375 72.1602 17.2266 70.6289 15.0254 70.3418C13.877 70.2461 13.4941 69.7676 13.4941 69.1934C13.4941 68.0449 15.4082 67.1836 17.3223 67.1836C20.0977 67.1836 22.4902 68.9063 24.9785 72.4473C26.8926 75.2227 28.9023 76.4668 31.2949 76.4668C33.6875 76.4668 35.2187 75.6055 37.4199 73.4043C39.0469 71.7773 40.291 70.3418 41.4395 69.3848Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_730_27126">
<rect width="98" height="96" fill="white"/>
</clipPath>
</defs>
</svg>
<svg width="98" height="96" viewBox="0 0 98 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_730_27136)">
<path d="M41.4395 69.3848C28.8066 67.8535 19.9062 58.7617 19.9062 46.9902C19.9062 42.2051 21.6289 37.0371 24.5 33.5918C23.2559 30.4336 23.4473 23.7344 24.8828 20.959C28.7109 20.4805 33.8789 22.4902 36.9414 25.2656C40.5781 24.1172 44.4062 23.543 49.0957 23.543C53.7852 23.543 57.6133 24.1172 61.0586 25.1699C64.0254 22.4902 69.2891 20.4805 73.1172 20.959C74.457 23.543 74.6484 30.2422 73.4043 33.4961C76.4668 37.1328 78.0937 42.0137 78.0937 46.9902C78.0937 58.7617 69.1934 67.6621 56.3691 69.2891C59.623 71.3945 61.8242 75.9883 61.8242 81.252L61.8242 91.2051C61.8242 94.0762 64.2168 95.7031 67.0879 94.5547C84.4102 87.9512 98 70.6289 98 49.1914C98 22.1074 75.9883 6.69539e-07 48.9043 4.309e-07C21.8203 1.92261e-07 -1.9479e-07 22.1074 -4.3343e-07 49.1914C-6.20631e-07 70.4375 13.4941 88.0469 31.6777 94.6504C34.2617 95.6074 36.75 93.8848 36.75 91.3008L36.75 83.6445C35.4102 84.2188 33.6875 84.6016 32.1562 84.6016C25.8398 84.6016 22.1074 81.1563 19.4277 74.7441C18.375 72.1602 17.2266 70.6289 15.0254 70.3418C13.877 70.2461 13.4941 69.7676 13.4941 69.1934C13.4941 68.0449 15.4082 67.1836 17.3223 67.1836C20.0977 67.1836 22.4902 68.9063 24.9785 72.4473C26.8926 75.2227 28.9023 76.4668 31.2949 76.4668C33.6875 76.4668 35.2187 75.6055 37.4199 73.4043C39.0469 71.7773 40.291 70.3418 41.4395 69.3848Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_730_27136">
<rect width="98" height="96" fill="white"/>
</clipPath>
</defs>
</svg>
import React from 'react';
import { GraduationCap, MapPin, Calendar, BookOpen } from 'lucide-react';
/**
* About component for the landing page
* id = about
* @returns {React.Component} About component
*/
const About = () => {
return (
<section id="about" className="py-24 bg-slate-50 relative">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl md:text-4xl font-bold text-slate-900 mb-4">About Me</h2>
<div className="w-16 h-1.5 bg-indigo-600 mx-auto rounded-full"></div>
</div>
<div className="grid md:grid-cols-2 gap-12 items-start">
{/* Bio Section */}
<div className="bg-white rounded-2xl shadow-sm border border-slate-100 p-8 md:p-10">
<h3 className="text-2xl font-bold text-slate-900 mb-6 flex items-center gap-3">
<span className="bg-indigo-100 text-indigo-600 p-2 rounded-lg">
<BookOpen size={24} />
</span>
My Background
</h3>
<p className="text-slate-600 leading-relaxed mb-6">
I am a final-year Information Technology student at the Posts and Telecommunications Institute of Technology (PTIT) - HCMC campus. I have built a solid foundation in Backend Development and system architecture.
</p>
<p className="text-slate-600 leading-relaxed">
Currently, I am actively seeking an IT internship opportunity. My primary goal is to apply my programming skills to real-world projects, learn from experienced software engineers, and contribute effectively to a dynamic development team.
</p>
</div>
{/* Education Section */}
<div className="bg-white rounded-2xl shadow-sm border border-slate-100 p-8 md:p-10">
<h3 className="text-2xl font-bold text-slate-900 mb-6 flex items-center gap-3">
<span className="bg-indigo-100 text-indigo-600 p-2 rounded-lg">
<GraduationCap size={24} />
</span>
Education
</h3>
<div className="border-l-2 border-indigo-100 pl-8 relative ml-2">
{/* Timeline dot */}
<div className="absolute w-4 h-4 bg-indigo-600 rounded-full -left-[9px] top-1.5 ring-4 ring-white"></div>
<h4 className="text-xl font-bold text-slate-900 mb-2">
Posts and Telecommunications Institute of Technology (PTIT)
</h4>
<div className="space-y-3 mt-4">
<div className="flex items-center gap-3 text-slate-600">
<MapPin size={18} className="text-indigo-500" />
<span>Ho Chi Minh City Campus</span>
</div>
<div className="flex items-center gap-3 text-slate-600">
<BookOpen size={18} className="text-indigo-500" />
<span>Department: Information Technology 2 (IT2)</span>
</div>
<div className="flex items-center gap-3 text-slate-600">
<Calendar size={18} className="text-indigo-500" />
<span>2022 - 2027</span>
</div>
<div className="mt-4 inline-block bg-slate-50 border border-slate-200 px-4 py-2 rounded-lg">
<span className="text-sm text-slate-500 uppercase tracking-wider font-semibold">Current GPA</span>
<p className="text-lg font-bold text-indigo-600 mt-1">3.20 <span className="text-slate-400 text-sm font-normal">/ 4.0</span></p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
};
export default About;
import React from 'react';
import { Briefcase, Calendar } from 'lucide-react';
/**
* Work experience component for the landing page
* id = experience
* @returns {React.Component} Work experience component
*/
const Experience = () => {
return (
<section id="experience" className="py-24 bg-slate-50">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl md:text-4xl font-bold text-slate-900 mb-4">Work Experience</h2>
<div className="w-16 h-1.5 bg-indigo-600 mx-auto rounded-full"></div>
</div>
<div className="max-w-3xl mx-auto relative">
{/* Vertical timeline line */}
<div className="absolute left-8 top-8 bottom-8 w-0.5 bg-indigo-100 hidden md:block"></div>
{/* Experience Item */}
<div className="relative pl-0 md:pl-20">
{/* Timeline dot */}
<div className="absolute left-[23px] top-6 w-5 h-5 bg-indigo-600 rounded-full ring-4 ring-slate-50 hidden md:block"></div>
<div className="bg-white rounded-2xl shadow-sm border border-slate-100 p-8 hover:shadow-md transition-shadow">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-4">
<div>
<h3 className="text-2xl font-bold text-slate-900">Backend Developer Intern</h3>
<div className="flex items-center gap-2 text-indigo-600 font-semibold mt-1">
<Briefcase size={18} />
<span>MeU Solutions</span>
</div>
</div>
<div className="flex items-center gap-2 text-slate-500 bg-slate-50 px-3 py-1.5 rounded-lg w-fit">
<Calendar size={16} />
<span className="text-sm font-medium">07/2026 - 09/2026</span>
</div>
</div>
<p className="text-slate-600 leading-relaxed">
As a Backend Developer Intern, I contributed to building and optimizing backend services, collaborating closely with the development team to ensure robust and scalable software solutions.
</p>
</div>
</div>
</div>
</div>
</section>
);
};
export default Experience;
import React from 'react';
import { Mail, ArrowUp } from 'lucide-react';
import githubWhite from '../assets/github_white.svg';
const Footer = () => {
return (
<footer className="bg-slate-900 text-slate-400 py-12 relative border-t border-slate-800">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col md:flex-row justify-between items-center gap-6">
<div className="text-center md:text-left">
<p className="text-xl font-bold text-white mb-2 tracking-tighter">Tu.Le</p>
<p className="text-sm text-slate-500">© {new Date().getFullYear()} Lê Ngọc Tú. All rights reserved.</p>
</div>
<div className="flex flex-col items-center md:items-end gap-4">
<div className="flex gap-4">
<a href="https://github.com/TuLe142857" target="_blank" rel="noopener noreferrer" className="bg-slate-800 p-2 rounded-full hover:bg-indigo-600 transition-colors flex items-center justify-center">
<img src={githubWhite} alt="GitHub" className="w-5 h-5" />
</a>
<a href="mailto:tuleaibk@gmail.com" className="bg-slate-800 p-2 rounded-full hover:bg-indigo-600 hover:text-white transition-colors">
<Mail size={20} />
</a>
</div>
<div className="text-sm bg-slate-800 px-3 py-1 rounded-md text-slate-300 border border-slate-700">
TOEIC 815
</div>
</div>
</div>
{/* Scroll to top button */}
<a
href="#home"
className="absolute -top-6 left-1/2 -translate-x-1/2 bg-indigo-600 text-white p-3 rounded-full shadow-lg hover:bg-indigo-500 transition-colors border-4 border-slate-50"
aria-label="Scroll to top"
>
<ArrowUp size={20} />
</a>
</footer>
);
};
export default Footer;
import React from 'react';
import { Mail, Briefcase, ChevronRight } from 'lucide-react';
import avatarImg from '../assets/avatar.jpg';
import githubBlack from '../assets/github_black.svg';
/**
* Hero component for the landing page
* id = home
* @returns {React.Component} Hero component
*/
const Hero = () => {
return (
<section id="home" className="min-h-[100dvh] flex items-center justify-center pt-32 pb-20 md:pt-40 md:pb-24 bg-white relative overflow-hidden">
{/* Decorative background shape */}
<div className="absolute top-0 right-0 -mr-32 -mt-32 w-[500px] h-[500px] rounded-full bg-indigo-50/70 blur-3xl"></div>
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 w-full relative z-10">
<div className="flex flex-col-reverse md:flex-row items-center justify-between gap-16 md:gap-12">
{/* Text Content */}
<div className="flex-1 text-center md:text-left">
<div className="inline-block px-4 py-1.5 rounded-full bg-indigo-100 text-indigo-700 font-medium text-sm mb-6">
IT Student & Aspiring Developer
</div>
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold text-slate-900 leading-tight mb-4">
Hi, I'm <span className="text-indigo-600">Lê Ngọc Tú</span>
</h1>
<h2 className="text-xl md:text-2xl text-slate-600 font-medium mb-6">
Web Developer / Backend Developer
</h2>
<p className="text-slate-600 text-lg mb-8 max-w-2xl leading-relaxed">
I'm a final-year IT student passionate about building scalable backend systems and seamless web applications. Currently seeking an internship to apply my skills to real-world projects.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center md:justify-start gap-4">
<a
href="#projects"
className="flex items-center gap-2 px-6 py-3 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition-colors shadow-sm hover:shadow-md w-full sm:w-auto justify-center"
>
View Projects
<ChevronRight size={18} />
</a>
<a
href="#experience"
className="flex items-center gap-2 px-6 py-3 bg-white text-slate-700 border border-slate-200 rounded-lg font-medium hover:bg-slate-50 transition-colors shadow-sm hover:shadow-md w-full sm:w-auto justify-center"
>
<Briefcase size={18} className="text-indigo-500" />
Work Experience
</a>
</div>
<div className="mt-10 flex items-center justify-center md:justify-start gap-5">
<a
href="https://github.com/TuLe142857"
target="_blank"
rel="noopener noreferrer"
className="p-2 flex items-center justify-center transition-colors"
aria-label="GitHub"
>
<img src={githubBlack} alt="GitHub" className="w-6 h-6 opacity-60 hover:opacity-100 transition-opacity" />
</a>
<a
href="mailto:tuleaibk@gmail.com"
className="p-2 text-slate-400 hover:text-indigo-600 transition-colors"
aria-label="Email"
>
<Mail size={24} />
</a>
</div>
</div>
{/* Avatar Image */}
<div className="flex-1 flex justify-center md:justify-end">
<div className="relative">
{/* Decorative ring */}
<div className="absolute inset-0 bg-gradient-to-tr from-indigo-100 to-indigo-50 rounded-full scale-105 -z-10"></div>
<img
src={avatarImg}
alt="Lê Ngọc Tú"
className="w-64 h-64 md:w-80 md:h-80 object-cover rounded-full border-4 border-white shadow-xl"
/>
</div>
</div>
</div>
</div>
</section>
);
};
export default Hero;
import React, { useState, useEffect } from 'react';
import { Menu, X } from 'lucide-react';
const Navbar = () => {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const navLinks = [
{ name: 'Home', href: '#home' },
{ name: 'About', href: '#about' },
{ name: 'Skills', href: '#skills' },
{ name: 'Experience', href: '#experience' },
{ name: 'Projects', href: '#projects' },
];
return (
<nav
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${isScrolled
? 'bg-white/95 backdrop-blur-md shadow-sm py-4'
: 'bg-transparent py-6'
}`}
>
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center">
<a
href="#home"
className="text-2xl font-bold text-indigo-600 tracking-tighter hover:opacity-80 transition-opacity"
>
Lê Ngọc Tú
</a>
{/* Desktop Navigation */}
<div className="hidden md:flex space-x-8">
{navLinks.map((link) => (
<a
key={link.name}
href={link.href}
className="text-slate-600 hover:text-indigo-600 font-medium transition-colors"
>
{link.name}
</a>
))}
</div>
{/* Mobile Menu Button */}
<div className="md:hidden">
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="text-slate-600 hover:text-indigo-600 focus:outline-none"
>
{isMobileMenuOpen ? <X size={28} /> : <Menu size={28} />}
</button>
</div>
</div>
</div>
{/* Mobile Navigation */}
{isMobileMenuOpen && (
<div className="md:hidden bg-white border-t border-slate-100 shadow-lg absolute w-full left-0">
<div className="flex flex-col space-y-4 px-6 py-6">
{navLinks.map((link) => (
<a
key={link.name}
href={link.href}
onClick={() => setIsMobileMenuOpen(false)}
className="text-slate-700 hover:text-indigo-600 font-medium text-lg"
>
{link.name}
</a>
))}
</div>
</div>
)}
</nav>
);
};
export default Navbar;
import React from 'react';
import { ExternalLink, Code2, Users, FolderKanban } from 'lucide-react';
import githubBlack from '../assets/github_black.svg';
/**
* Projects component for the landing page
* id = projects
* @returns {React.Component} Projects component
*/
const Projects = () => {
const projectsData = [
{
title: "DocumentHub",
type: "Course / Personal Project",
duration: "04/2026 - 05/2026",
description: "A comprehensive web platform allowing users to seamlessly upload, store, manage, and convert various digital document formats.",
technologies: ["Python", "FastAPI", "MySQL", "Redis", "Celery", "MinIO (S3)", "Gotenberg", "Docker", "React"],
achievements: [
"Developed high-performance RESTful APIs using FastAPI.",
"Integrated MinIO for secure storage and retrieval of large files.",
"Utilized Gotenberg API for automated file format conversion to PDF.",
"Built asynchronous background workers with Celery and Redis to handle heavy tasks without blocking the main thread.",
"Containerized the entire system using Docker."
],
github: "https://github.com/TuLe142857/DocumentHub",
website: null,
icon: <FolderKanban size={24} />
},
{
title: "ChessLens",
type: "Personal Project",
duration: "05/2026",
description: "An intuitive web application designed for visualizing chess player data and generating comprehensive recap reports.",
technologies: ["React (Vite)", "TypeScript", "Tailwind CSS", "TanStack Query", "Recharts", "GitHub Actions", "Vercel"],
achievements: [
"Integrated Chess.com Public API via Axios to extract large volumes of profile data and match history.",
"Optimized API fetching and state management with TanStack Query.",
"Deployed robust CI/CD pipeline with GitHub Actions and Vercel."
],
github: "https://github.com/TuLe142857/ChessLens.git",
website: "https://chesslens.io.vn/",
icon: <Code2 size={24} />
},
{
title: "MindCard",
type: "Group Project",
duration: "05/2026 - present",
description: "An interactive web application supporting efficient learning through digital flashcards.",
technologies: ["Java", "Spring Boot", "PostgreSQL", "MinIO"],
achievements: [
"Built backend system architecture using Spring Boot, adhering to clean code principles.",
"Designed secure RESTful APIs for core features (authentication, deck creation, card management).",
"Integrated PostgreSQL for user data management and MinIO for handling attached media."
],
github: "https://github.com/TuLe142857/MindCard.git",
website: null,
icon: <Users size={24} />
}
];
return (
<section id="projects" className="py-24 bg-white">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl md:text-4xl font-bold text-slate-900 mb-4">Featured Projects</h2>
<div className="w-16 h-1.5 bg-indigo-600 mx-auto rounded-full"></div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{projectsData.map((project, index) => (
<div
key={index}
className="bg-white rounded-2xl border border-slate-200 overflow-hidden hover:shadow-xl hover:-translate-y-2 transition-all duration-300 flex flex-col h-full"
>
<div className="p-8 flex-grow">
<div className="flex justify-between items-start mb-6">
<div className="bg-indigo-50 text-indigo-600 p-3 rounded-xl">
{project.icon}
</div>
<div className="flex gap-3">
{project.github && (
<a href={project.github} target="_blank" rel="noopener noreferrer" aria-label="GitHub Repository">
<img src={githubBlack} alt="GitHub" className="w-5 h-5 opacity-50 hover:opacity-100 transition-opacity" />
</a>
)}
{project.website && (
<a href={project.website} target="_blank" rel="noopener noreferrer" className="text-slate-400 hover:text-indigo-600 transition-colors" aria-label="Live Website">
<ExternalLink size={22} />
</a>
)}
</div>
</div>
<h3 className="text-2xl font-bold text-slate-900 mb-2">{project.title}</h3>
<p className="text-sm text-indigo-600 font-medium mb-4">{project.type} &bull; {project.duration}</p>
<p className="text-slate-600 mb-6 leading-relaxed">
{project.description}
</p>
<div className="mb-6">
<h4 className="text-sm font-bold text-slate-900 mb-3 uppercase tracking-wider">Key Highlights</h4>
<ul className="space-y-2">
{project.achievements.map((achievement, i) => (
<li key={i} className="text-sm text-slate-600 flex items-start gap-2">
<span className="text-indigo-500 mt-1"></span>
<span>{achievement}</span>
</li>
))}
</ul>
</div>
</div>
<div className="p-6 bg-slate-50 border-t border-slate-100">
<div className="flex flex-wrap gap-2">
{project.technologies.map((tech, i) => (
<span
key={i}
className="bg-white border border-slate-200 text-slate-700 px-2.5 py-1 rounded-md text-xs font-medium"
>
{tech}
</span>
))}
</div>
</div>
</div>
))}
</div>
</div>
</section>
);
};
export default Projects;
import React from 'react';
import { Code2, Server, Database, Wrench, Languages, Layout } from 'lucide-react';
/**
* Skills component for the landing page
* id = skills
* @returns {React.Component}
*/
const Skills = () => {
const skillCategories = [
{
title: 'Programming Languages',
icon: <Code2 size={24} />,
skills: ['Java', 'Python', 'JavaScript', 'TypeScript']
},
{
title: 'Backend',
icon: <Server size={24} />,
skills: ['FastAPI', 'Spring Boot', 'Flask', 'SQLAlchemy']
},
{
title: 'Frontend',
icon: <Layout size={24} />,
skills: ['React', 'Vite', 'Tailwind CSS']
},
{
title: 'Database',
icon: <Database size={24} />,
skills: ['MySQL', 'PostgreSQL', 'Redis']
},
{
title: 'DevOps & Tools',
icon: <Wrench size={24} />,
skills: ['Docker', 'Git', 'GitHub Actions', 'Linux (Ubuntu/Fedora)', 'MinIO', 'Celery']
},
{
title: 'Languages & Others',
icon: <Languages size={24} />,
skills: ['English (TOEIC 815)', 'Obsidian (Note-taking & Docs)']
}
];
return (
<section id="skills" className="py-24 bg-white">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl md:text-4xl font-bold text-slate-900 mb-4">Technical Skills</h2>
<div className="w-16 h-1.5 bg-indigo-600 mx-auto rounded-full"></div>
<p className="text-slate-600 mt-6 max-w-2xl mx-auto text-lg">
A comprehensive overview of my technical stack and tools I use to build robust applications.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{skillCategories.map((category, index) => (
<div
key={index}
className="bg-slate-50 border border-slate-100 rounded-2xl p-6 hover:shadow-md hover:-translate-y-1 transition-all duration-300"
>
<div className="flex items-center gap-3 mb-6">
<div className="bg-indigo-100 text-indigo-600 p-2.5 rounded-xl">
{category.icon}
</div>
<h3 className="text-xl font-bold text-slate-900">{category.title}</h3>
</div>
<div className="flex flex-wrap gap-2">
{category.skills.map((skill, i) => (
<span
key={i}
className="bg-white border border-slate-200 text-slate-700 px-3 py-1.5 rounded-lg text-sm font-medium shadow-sm"
>
{skill}
</span>
))}
</div>
</div>
))}
</div>
</div>
</section>
);
};
export default Skills;
@import "tailwindcss";
html {
scroll-behavior: smooth;
}
body {
@apply bg-slate-50 text-slate-800 font-sans antialiased selection:bg-indigo-200 selection:text-indigo-900;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment