update: design
Vercel Production Deployment / Deploy-Production (push) Failing after 49s

This commit is contained in:
2025-11-19 11:58:16 +08:00
parent 021dad852b
commit 194a2d1810
7 changed files with 1653 additions and 722 deletions
+1 -5
View File
@@ -26,7 +26,7 @@
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
}
.list-wrapper {
@@ -73,7 +73,3 @@ body {
.transition-shadow {
transition: box-shadow 0.3s ease-in-out;
}
.container {
max-width: 1200px;
}
+116 -77
View File
@@ -3,106 +3,145 @@ import Image from "next/image";
import { websites } from "../data/urls";
import Header from "@/components/header";
import Footer from "@/components/footer";
import { motion } from "framer-motion";
import { motion, useMotionTemplate, useMotionValue, useSpring, useTransform } from "framer-motion";
import { MouseEvent } from "react";
function SpotlightCard({ website, variants }: { website: any; variants: any }) {
const mouseX = useMotionValue(0);
const mouseY = useMotionValue(0);
const x = useMotionValue(0);
const y = useMotionValue(0);
const rotateX = useSpring(useTransform(y, [-0.5, 0.5], ["2.5deg", "-2.5deg"]), { stiffness: 200, damping: 20 });
const rotateY = useSpring(useTransform(x, [-0.5, 0.5], ["-2.5deg", "2.5deg"]), { stiffness: 200, damping: 20 });
function handleMouseMove({ currentTarget, clientX, clientY }: MouseEvent) {
const { left, top, width, height } = currentTarget.getBoundingClientRect();
mouseX.set(clientX - left);
mouseY.set(clientY - top);
x.set((clientX - left) / width - 0.5);
y.set((clientY - top) / height - 0.5);
}
function handleMouseLeave() {
x.set(0);
y.set(0);
}
return (
<motion.div variants={variants} className="h-full" style={{ perspective: 1000 }}>
<motion.a
href={website.link}
rel="noopener noreferrer"
target="_blank"
className="group relative block h-full"
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
style={{
rotateX,
rotateY,
transformStyle: "preserve-3d",
}}
whileHover={{ scale: 1.02, y: -5 }}
whileTap={{ scale: 0.98 }}
>
<div className="absolute inset-0 bg-black dark:bg-white rounded-none translate-y-2 translate-x-2 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
<div className="relative h-full bg-white dark:bg-[#0a0a0a] border border-gray-200 dark:border-gray-800 p-6 rounded-none transition-all duration-300 group-hover:border-black dark:group-hover:border-white overflow-hidden">
<motion.div
className="pointer-events-none absolute -inset-px opacity-0 transition duration-300 group-hover:opacity-100 z-10"
style={{
background: useMotionTemplate`
radial-gradient(
400px circle at ${mouseX}px ${mouseY}px,
rgba(0, 0, 0, 0.08),
transparent 80%
)
`,
}}
/>
<motion.div
className="pointer-events-none absolute -inset-px opacity-0 transition duration-300 group-hover:opacity-100 z-10 hidden dark:block"
style={{
background: useMotionTemplate`
radial-gradient(
400px circle at ${mouseX}px ${mouseY}px,
rgba(255, 255, 255, 0.1),
transparent 80%
)
`,
}}
/>
<div className="relative z-20">
<div className="flex items-start justify-between mb-6">
<div className="p-3 bg-gray-50 dark:bg-white rounded-xl group-hover:bg-gray-100 dark:group-hover:bg-gray-200 transition-all duration-300 shadow-sm border border-gray-100 dark:border-gray-200">
<Image src={website.icon} alt={website.title} width={40} height={40} className="transition-all duration-300" />
</div>
<span className="text-gray-300 dark:text-gray-600 group-hover:text-black dark:group-hover:text-white transition-transform duration-300 group-hover:rotate-45">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="7" y1="17" x2="17" y2="7"></line>
<polyline points="7 7 17 7 17 17"></polyline>
</svg>
</span>
</div>
<h3 className="text-xl font-bold mb-2 text-gray-900 dark:text-white group-hover:underline decoration-2 underline-offset-4">{website.title}</h3>
<p className="text-gray-500 dark:text-gray-400 text-sm leading-relaxed line-clamp-3">{website.description}</p>
</div>
</div>
</motion.a>
</motion.div>
);
}
export default function Home() {
// 动画定义
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
staggerChildren: 0.08,
delayChildren: 0.1,
},
},
};
const itemVariants = {
hidden: { y: 20, opacity: 0 },
hidden: { y: 20, opacity: 0, scale: 0.98, filter: "blur(4px)" },
visible: {
y: 0,
opacity: 1,
transition: { type: "spring", stiffness: 100 },
},
};
const textVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { duration: 0.3, ease: "easeInOut" },
scale: 1,
filter: "blur(0px)",
transition: { type: "spring" as const, stiffness: 100, damping: 15, mass: 1 },
},
};
return (
<div className="min-h-screen flex flex-col bg-gray-100 dark:bg-gray-900 transition-colors duration-300">
<div className="flex flex-col min-h-screen bg-white dark:bg-black text-black dark:text-white">
<Header />
<motion.main className="container mx-auto px-4 py-8 grow" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5 }}>
<motion.section className="mb-16 max-w-3xl mx-auto" initial={{ opacity: 0, y: -20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.7 }}>
<motion.h2 className="text-4xl font-extrabold text-gray-900 dark:text-gray-100 mb-6 text-center" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5, delay: 0.2 }}>
智能计算实验室简介
</motion.h2>
<motion.p className="text-lg leading-relaxed text-gray-700 dark:text-gray-300 mt-4 indent-8 tracking-wide" variants={textVariants} initial="hidden" animate="visible">
在物理的世界里,粒子有形形色色的活法,有的粒子很轻,终生不为人所注意;有的粒子会一鸣惊人,在湮灭时释放出大量的光;有的粒子狡黠善变,一辈子不停地改变身份,时时刻刻要成为别的粒子;但有的粒子一生稳定,怀揣着出发时得到的巨大能量,一生孤独而平静地飞行,只活在自己的轨道里。
</motion.p>
<motion.p className="text-right text-sm text-gray-500 dark:text-gray-400 mt-3 italic" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5, delay: 0.4 }}>
——摘自《丁肇中 伟大的是物理》,作者:李斐然
</motion.p>
<motion.main className="w-full max-w-7xl mx-auto px-4 md:px-6 py-16 grow flex flex-col items-center" initial="hidden" animate="visible" variants={containerVariants}>
{/* Hero Section */}
<motion.section className="max-w-4xl w-full mb-24 text-center relative" variants={itemVariants}>
<h2 className="text-5xl md:text-6xl font-black tracking-tighter mb-8 bg-clip-text text-transparent bg-linear-to-b from-gray-900 to-gray-600 dark:from-white dark:to-gray-400">
智能计算实验室
</h2>
<div className="relative inline-block">
<p className="text-xl text-left md:text-2xl leading-relaxed text-gray-600 dark:text-gray-400 font-light max-w-2xl mx-auto">
  在物理的世界里,粒子有形形色色的活法,有的粒子很轻,终生不为人所注意;有的粒子会一鸣惊人,在湮灭时释放出大量的光;有的粒子狡黠善变,一辈子不停地改变身份,时时刻刻要成为别的粒子;但有的粒子一生稳定,怀揣着出发时得到的巨大能量,一生孤独而平静地飞行,只活在自己的轨道里。
</p>
<p className="text-right text-sm text-gray-400 dark:text-gray-500 mt-4 font-mono">—— 李斐然《丁肇中 伟大的是物理》</p>
</div>
</motion.section>
<motion.section
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8"
variants={containerVariants}
initial="hidden"
animate="visible"
transition={{ staggerChildren: 0.1, delayChildren: 0.3 }}
>
{/* Grid Section */}
<motion.section className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 w-full" variants={containerVariants}>
{websites.map((website, index) => (
<motion.a
key={index}
href={website.link}
rel="noopener noreferrer"
target="_blank"
className="group flex justify-center"
variants={itemVariants}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
<motion.div
className="flex flex-col bg-white dark:bg-gray-800 p-5 rounded-2xl shadow-md w-full max-w-md border border-gray-100 dark:border-gray-700 overflow-hidden"
whileHover={{ boxShadow: "0 10px 25px rgba(0, 0, 0, 0.1)" }}
transition={{ duration: 0.3 }}
>
<div className="flex items-center mb-4">
<motion.div
className="relative w-16 h-16 mr-4 bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-blue-100 dark:to-indigo-200 p-1 rounded-lg flex items-center justify-center"
whileHover={{ rotate: 5 }}
transition={{ type: "spring", stiffness: 300 }}
>
<Image src={website.icon} alt={website.title} width={52} height={52} className="rounded-md object-cover" />
</motion.div>
<h3 className="text-xl font-bold truncate text-gray-900 dark:text-gray-100 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors duration-300">
{website.title}
</h3>
</div>
<div className="text-sm text-gray-600 dark:text-gray-300 line-clamp-2 leading-relaxed">{website.description}</div>
<div className="mt-3 pt-3 border-t border-gray-100 dark:border-gray-700 flex justify-between items-center">
<span className="text-xs text-gray-500 dark:text-gray-400">访问站点</span>
<motion.svg
className="w-4 h-4 text-gray-400 dark:text-gray-500 group-hover:text-blue-500 dark:group-hover:text-blue-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
initial={{ x: 0 }}
whileHover={{ x: 3 }}
transition={{ type: "spring", stiffness: 400 }}
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14 5l7 7m0 0l-7 7m7-7H3" />
</motion.svg>
</div>
</motion.div>
</motion.a>
<SpotlightCard key={index} website={website} variants={itemVariants} />
))}
</motion.section>
</motion.main>
+3 -3
View File
@@ -2,11 +2,11 @@ import Link from "next/link";
export default function Footer() {
return (
<footer className="w-full bg-white dark:bg-gray-950 shadow-sm p-4 text-left text-gray-500 dark:text-gray-400 flex flex-row gap-2">
<div className="container mx-auto">
<footer className="w-full bg-white dark:bg-black border-t border-transparent dark:border-gray-800 p-6 text-left text-gray-500 dark:text-gray-500 flex flex-row gap-2 transition-colors duration-300">
<div className="w-full max-w-7xl mx-auto">
<div>
© 2024 - {new Date().getFullYear()} 智能计算实验室 · Designed by{" "}
<Link href="https://isteed.cc" target="_blank" rel="noopener noreferrer">
<Link href="https://isteed.cc" target="_blank" rel="noopener noreferrer" className="hover:text-black dark:hover:text-gray-300 transition-colors">
LufsX
</Link>
.
+65 -37
View File
@@ -19,6 +19,46 @@ export default function Header() {
setIsMenuOpen(false);
}, []);
const sidebarVariants = {
open: {
x: 0,
transition: {
type: "spring" as const,
stiffness: 300,
damping: 30,
staggerChildren: 0.1,
delayChildren: 0.1,
},
},
closed: {
x: "100%",
transition: {
type: "spring" as const,
stiffness: 300,
damping: 30,
staggerChildren: 0.05,
staggerDirection: -1,
},
},
};
const menuItemVariants = {
open: {
y: 0,
opacity: 1,
transition: {
y: { stiffness: 1000, velocity: -100 },
},
},
closed: {
y: 20,
opacity: 0,
transition: {
y: { stiffness: 1000 },
},
},
};
// 导航链接组件
const NavLink = ({ href, children, className, onClick }: { href: string; children: React.ReactNode; className?: string; onClick?: () => void }) => (
<Link href={href} className={pathname === href ? "no-underline" : ""} onClick={onClick}>
@@ -38,25 +78,25 @@ export default function Header() {
);
return (
<>
<motion.header className="fixed top-0 left-0 right-0 bg-white/80 dark:bg-gray-950/80 py-3 px-5 z-40 backdrop-blur-sm shadow-sm">
<nav className="container max-w-7xl mx-auto flex justify-between items-center">
<div className="flex items-center">
<Link href="/">
<motion.h1 className="text-xl font-bold text-gray-900 dark:text-gray-100" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}>
<div className="pb-24">
<header className="w-full fixed top-0 left-0 right-0 bg-white/80 dark:bg-black/80 py-3 z-50 backdrop-blur-md border-b border-transparent dark:border-gray-800 transition-colors duration-300">
<nav className="mx-auto max-w-7xl flex justify-between items-center px-4 md:px-6">
<div className="flex items-center min-w-0">
<Link href="/" className="truncate">
<motion.h1 className="text-xl font-bold text-gray-900 dark:text-white truncate" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}>
智能计算实验室
</motion.h1>
</Link>
</div>
{/* 桌面端导航 */}
<div className="hidden md:flex space-x-6">
<div className="hidden md:flex space-x-6 shrink-0">
{NAV_LINKS.map((link) => (
<NavLink
key={link.path}
href={link.path}
className={`text-sm font-medium text-gray-700 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 ${
pathname === link.path ? "border-b border-gray-600 dark:border-gray-400 hover:border-blue-600 dark:hover:border-blue-400" : "border-b border-transparent"
className={`text-sm font-medium text-gray-600 dark:text-gray-400 hover:text-black dark:hover:text-white transition-colors duration-200 ${
pathname === link.path ? "text-black dark:text-white font-bold" : ""
}`}
>
{link.label}
@@ -65,61 +105,49 @@ export default function Header() {
</div>
{/* 移动端菜单按钮 */}
<NavButton className="flex items-center md:hidden focus:outline-none" onClick={toggleMenu}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="打开导航菜单" className="text-gray-900 dark:text-gray-100">
<NavButton className="flex items-center md:hidden focus:outline-none shrink-0 ml-4" onClick={toggleMenu}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="打开导航菜单" className="text-gray-900 dark:text-white">
<path d="M4 6h16M4 12h16M4 18h16" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</NavButton>
</nav>
</motion.header>
</header>
{/* 遮罩层 */}
<div
className={`md:hidden fixed inset-0 bg-black bg-opacity-50 z-40 transition-opacity duration-300 ${isMenuOpen ? "opacity-25" : "opacity-0 pointer-events-none"}`}
className={`md:hidden fixed inset-0 bg-black/50 z-40 transition-opacity duration-300 ${isMenuOpen ? "opacity-100" : "opacity-0 pointer-events-none"}`}
onClick={closeMenu}
aria-hidden="true"
/>
{/* 移动端侧边菜单 */}
<motion.div
className="md:hidden fixed top-0 right-0 h-full w-64 bg-white dark:bg-gray-900 shadow-lg z-50"
initial={{ x: "100%" }}
animate={{ x: isMenuOpen ? 0 : "100%" }}
transition={{
type: "tween",
ease: [0.05, 1, 0.5, 1],
duration: 0.3,
}}
className="md:hidden fixed top-[57px] right-0 bottom-0 w-full max-w-xs bg-white/80 dark:bg-black/80 backdrop-blur-md border-l border-gray-200 dark:border-gray-800 shadow-2xl z-40"
initial="closed"
animate={isMenuOpen ? "open" : "closed"}
variants={sidebarVariants}
role="dialog"
aria-modal="true"
aria-label="导航菜单"
>
<div className="flex justify-between items-center p-3 border-b border-gray-200 dark:border-gray-800">
<h2 className="text-lg font-bold text-gray-900 dark:text-gray-100">智能计算实验室</h2>
<NavButton onClick={closeMenu} className="focus:outline-none">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="关闭导航菜单" className="text-gray-900 dark:text-gray-100">
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</NavButton>
</div>
<div className="px-5 py-4">
<div className="px-6 py-8 flex flex-col gap-2 h-full overflow-y-auto">
{NAV_LINKS.map((link) => (
<motion.div key={link.path} variants={menuItemVariants}>
<NavLink
key={link.path}
href={link.path}
className={`block py-3 text-gray-700 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 ${
pathname === link.path ? "border-l-2 border-blue-600 dark:border-blue-400 pl-3 font-medium" : "pl-3"
className={`block py-4 px-4 rounded-0 text-lg transition-all duration-200 ${
pathname === link.path
? "bg-gray-100 dark:bg-gray-800 text-black dark:text-white font-bold"
: "text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-900 hover:text-black dark:hover:text-white"
}`}
onClick={closeMenu}
>
{link.label}
</NavLink>
</motion.div>
))}
</div>
</motion.div>
{/* 避免被导航栏遮挡 */}
<div className="h-16"></div>
</>
</div>
);
}
+13 -13
View File
@@ -9,22 +9,22 @@
"lint": "next lint"
},
"dependencies": {
"framer-motion": "12.9.2",
"next": "15.3.1",
"react": "19.1.0",
"react-dom": "19.1.0"
"framer-motion": "12.23.24",
"next": "16.0.3",
"react": "19.2.0",
"react-dom": "19.2.0"
},
"devDependencies": {
"@eslint/eslintrc": "3.3.1",
"@tailwindcss/postcss": "4.1.4",
"@types/node": "22.15.3",
"@types/react": "19.1.2",
"@types/react-dom": "19.1.2",
"eslint": "9.25.1",
"eslint-config-next": "15.3.1",
"postcss": "8.5.3",
"tailwindcss": "4.1.4",
"typescript": "5.8.3"
"@tailwindcss/postcss": "4.1.17",
"@types/node": "24.10.1",
"@types/react": "19.2.6",
"@types/react-dom": "19.2.3",
"eslint": "9.39.1",
"eslint-config-next": "16.0.3",
"postcss": "8.5.6",
"tailwindcss": "4.1.17",
"typescript": "5.9.3"
},
"pnpm": {
"ignoredBuiltDependencies": [
+1445 -578
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -15,7 +15,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
@@ -33,7 +33,8 @@
"**/*.tsx",
".next/types/**/*.ts",
"next-env.d.ts",
"out/types/**/*.ts"
"out/types/**/*.ts",
"out/dev/types/**/*.ts"
],
"exclude": [
"node_modules"