Files
2026-08-19 18:05:20 +08:00

103 lines
2.6 KiB
TypeScript

"use client";
import { useEffect, useRef } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { SectionLabel } from "./section-label";
gsap.registerPlugin(ScrollTrigger);
const groups: { name: string; items: string[] }[] = [
{
name: "语言",
items: ["TypeScript", "JavaScript", "Python"]
},
{
name: "前端",
items: ["Vue 3", "Nuxt", "Pinia", "Tailwind CSS", "Sass / Less"]
},
{
name: "UI 与可视化",
items: ["Naive UI", "Ant Design", "shadcn/ui", "ECharts"]
},
{
name: "后端",
items: ["Node.js", "Koa", "FastAPI"]
},
{
name: "工程化",
items: ["Vite", "pnpm", "Git"]
},
{
name: "设计",
items: ["Figma", "Pixso", "蓝湖"]
},
{
name: "AI 编程",
items: ["Cursor", "Copilot", "Claude Code", "Codex", "V0"]
},
{
name: "AI 模型",
items: ["GPT", "Claude", "Grok", "DeepSeek", "MiMo"]
}
];
export function TechStack() {
const rootRef = useRef<HTMLElement>(null);
useEffect(() => {
const ctx = gsap.context(() => {
gsap.fromTo(
".tech-item",
{ opacity: 0, y: 24 },
{
opacity: 1,
y: 0,
duration: 0.7,
ease: "power2.out",
stagger: 0.06,
scrollTrigger: {
trigger: rootRef.current,
start: "top 75%"
}
}
);
}, rootRef);
return () => ctx.revert();
}, []);
return (
<section
ref={rootRef}
aria-label="技术栈"
className="mx-auto max-w-6xl px-6 py-32 md:py-40"
>
<SectionLabel>Tech Stack</SectionLabel>
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 md:gap-x-16">
{groups.map((group) => (
<div key={group.name} className="tech-item">
<p className="mb-5 text-xs uppercase tracking-[0.2em] text-muted-foreground/60">
{group.name}
</p>
<ul className="flex flex-wrap gap-3">
{group.items.map((item) => (
<li key={item} className="tech-item">
<span className="inline-flex items-center gap-2 rounded-md border border-border bg-card px-4 py-2 text-sm text-secondary-foreground transition-all duration-300 hover:scale-[1.04] hover:border-foreground/20 hover:shadow-[0_0_20px_-6px_oklch(0.62_0.05_155_/_0.25)]">
<span
aria-hidden="true"
className="h-1 w-1 rounded-full bg-sage/70"
/>
{item}
</span>
</li>
))}
</ul>
</div>
))}
</div>
</section>
);
}