Add blog posts, cleanup unused files, update components
- Add 100 blog posts covering AI, development, and tech topics - Add .env.example for environment configuration - Add accessibility and lighthouse audit scripts - Remove obsolete SEO reports and temporary files - Remove dev-dist build artifacts and backup files - Remove unused portrait images (moved/consolidated elsewhere) - Update contact form and component improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,13 +2,12 @@
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Database, Server, Code2, Bot, Workflow, FileCode2 } from 'lucide-react';
|
||||
|
||||
interface Skill {
|
||||
name: string;
|
||||
level: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const iconMap: Record<string, React.ReactNode> = {
|
||||
@@ -25,7 +24,6 @@ const iconMap: Record<string, React.ReactNode> = {
|
||||
|
||||
const Skills = () => {
|
||||
const t = useTranslations('pages.home.skills');
|
||||
const [selectedSkill, setSelectedSkill] = useState<string | null>(null);
|
||||
const [skills, setSkills] = useState<Skill[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -96,54 +94,16 @@ const Skills = () => {
|
||||
{skills.map((skill, idx) => (
|
||||
<motion.div
|
||||
key={skill.name}
|
||||
className="relative"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: idx * 0.1 }}
|
||||
className="p-6 rounded-xl bg-zinc-800/30 backdrop-blur-sm flex flex-col items-center justify-center gap-3"
|
||||
>
|
||||
<motion.div
|
||||
className={`relative p-6 rounded-xl bg-zinc-800/30 backdrop-blur-sm flex flex-col items-center justify-center gap-3 cursor-pointer transition-colors duration-300 hover:bg-zinc-700/50 ${
|
||||
selectedSkill === skill.name ? 'ring-2 ring-zinc-500 z-20' : ''
|
||||
}`}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
onHoverStart={() => setSelectedSkill(skill.name)}
|
||||
onHoverEnd={() => setSelectedSkill(null)}
|
||||
role="button"
|
||||
>
|
||||
<motion.div
|
||||
className={selectedSkill === skill.name ? 'text-zinc-500' : 'text-white'}
|
||||
animate={{
|
||||
rotate: selectedSkill === skill.name ? [0, -10, 10, 0] : 0,
|
||||
scale: selectedSkill === skill.name ? 1.1 : 1
|
||||
}}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
{iconMap[skill.name] || <Code2 className="w-8 h-8" />}
|
||||
</motion.div>
|
||||
<span className="text-white text-sm text-center">{skill.name}</span>
|
||||
|
||||
<AnimatePresence>
|
||||
{selectedSkill === skill.name && (
|
||||
<motion.div
|
||||
className="absolute left-1/2 top-0 -translate-x-1/2 bg-zinc-800/80 backdrop-blur-sm rounded-lg px-4 py-2 text-zinc-300 text-xs text-center shadow-xl pointer-events-none"
|
||||
initial={{ opacity: 0, y: 5 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 5 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
style={{
|
||||
whiteSpace: 'nowrap',
|
||||
transform: 'translate(-50%, calc(-100% - 12px))',
|
||||
zIndex: 999
|
||||
}}
|
||||
>
|
||||
{skill.description}
|
||||
<div className="absolute left-1/2 bottom-0 translate-y-1/2 -translate-x-1/2 w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-t-[6px] border-t-zinc-800" />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
<div className="text-white">
|
||||
{iconMap[skill.name] || <Code2 className="w-8 h-8" />}
|
||||
</div>
|
||||
<span className="text-white text-sm text-center">{skill.name}</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user