feat: 优化项目网站 UI/UX 并新增项目详情组件

- 重构首页和项目列表页布局,优化视觉层次
- 新增 ProjectDetail、ProjectSidebar、RelatedProjects 组件
- 改进项目卡片样式,增加悬停效果
- 优化标签云和搜索栏交互体验
- 更新全局样式和 Tailwind 配置

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-26 16:36:33 +08:00
co-authored by Claude
parent 85fe0b920b
commit 4abe6715bb
19 changed files with 2108 additions and 227 deletions
+152 -28
View File
@@ -1,6 +1,6 @@
import { notFound } from "next/navigation"
import { getRequestConfig } from 'next-intl/server'
import { setRequestLocale } from 'next-intl/server'
import Link from "next/link"
const locales = ['zh', 'en']
@@ -24,36 +24,160 @@ export default async function LocaleLayout({
return (
<div className="min-h-screen flex flex-col">
<header className="border-b">
<div className="container mx-auto px-4 py-4">
<nav className="flex items-center justify-between">
<a href={`/${locale}`} className="text-xl font-semibold">
Agent Park
</a>
<div className="flex items-center gap-4">
<a href={`/${locale}`} className="text-sm hover:underline">
</a>
<a href={`/${locale}/projects`} className="text-sm hover:underline">
</a>
<div className="flex gap-2 text-sm">
<a href="/zh" className={locale === 'zh' ? 'font-semibold' : 'hover:underline'}>
</a>
<span>/</span>
<a href="/en" className={locale === 'en' ? 'font-semibold' : 'hover:underline'}>
EN
</a>
</div>
{/* Top announcement bar */}
<div className="bg-primary w-full py-2 px-4 border-b-2 border-black dark:border-gray-600 flex justify-center items-center text-xs font-bold font-display tracking-wide text-black">
<span>DISCOVER THE FUTURE OF AI AGENTS</span>
<span className="material-icons text-sm align-middle ml-1">arrow_forward</span>
</div>
{/* Navigation */}
<header className="w-full border-b-2 border-black dark:border-gray-600 bg-background-light dark:bg-background-dark sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
{/* Logo */}
<div className="flex-shrink-0 flex items-center gap-2">
<span className="material-icons text-3xl">smart_toy</span>
<Link href={`/${locale}`} className="font-display font-bold text-xl tracking-tight">
Agent Park
</Link>
</div>
</nav>
{/* Desktop Nav */}
<nav className="hidden md:flex space-x-8 items-center">
<Link
className="font-display text-sm font-bold hover:text-secondary dark:hover:text-primary transition-colors"
href={`/${locale}`}
>
HOME
</Link>
<Link
className="font-display text-sm font-bold hover:text-secondary dark:hover:text-primary transition-colors"
href={`/${locale}/projects`}
>
PROJECTS
</Link>
<Link
className="font-display text-sm font-bold hover:text-secondary dark:hover:text-primary transition-colors"
href="#"
>
BLOG
</Link>
<Link
className="font-display text-sm font-bold hover:text-secondary dark:hover:text-primary transition-colors"
href="#"
>
ABOUT
</Link>
</nav>
{/* Right side */}
<div className="hidden md:flex items-center space-x-4">
<button className="font-display text-sm font-bold hover:opacity-70">
/ EN
</button>
<Link
className="bg-white dark:bg-surface-dark border-2 border-black dark:border-white px-4 py-2 font-display text-sm font-bold shadow-neo-sm hover:shadow-none hover:translate-x-[2px] hover:translate-y-[2px] transition-all"
href="#"
>
SUBMIT PROJECT
</Link>
</div>
{/* Mobile menu button */}
<div className="md:hidden flex items-center">
<button className="text-text-light dark:text-text-dark hover:text-gray-600 focus:outline-none">
<span className="material-icons">menu</span>
</button>
</div>
</div>
</div>
</header>
<main className="flex-1">{children}</main>
<footer className="border-t py-6">
<div className="container mx-auto px-4 text-center text-sm text-muted-foreground">
© 2025 Agent Park. All rights reserved.
<main className="flex-1 relative">{children}</main>
{/* Newsletter Section */}
<section className="border-t-2 border-black dark:border-gray-600">
<div className="grid grid-cols-1 md:grid-cols-2">
<div className="bg-primary dark:bg-yellow-600 p-12 md:p-20 border-b-2 md:border-b-0 md:border-r-2 border-black dark:border-gray-600 flex flex-col justify-center">
<h2 className="font-display text-4xl font-bold text-black mb-4">STAY UPDATED</h2>
<p className="font-sans text-black mb-8 max-w-md">
Get the latest AI tools and trends delivered straight to your inbox. No spam, just intelligence.
</p>
<form className="space-y-4 max-w-md">
<input
className="w-full bg-background-light border-2 border-black p-3 font-display text-sm placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-black"
placeholder="E-mail"
type="email"
/>
<div className="flex items-center gap-2">
<input className="w-4 h-4 border-2 border-black text-black focus:ring-0" id="consent" type="checkbox" />
<label className="text-xs font-bold font-display text-black" htmlFor="consent">
Subscribe to Agent Park updates
</label>
</div>
<button
className="bg-white text-black font-display font-bold py-3 px-8 border-2 border-black shadow-neo hover:shadow-none hover:translate-x-[2px] hover:translate-y-[2px] transition-all"
type="submit"
>
SUBSCRIBE
</button>
</form>
</div>
<div className="bg-background-light dark:bg-background-dark p-12 md:p-20 relative overflow-hidden flex items-center justify-center">
<div className="relative w-64 h-64">
<div className="absolute inset-0 border-2 border-black dark:border-gray-500 bg-white dark:bg-surface-dark transform rotate-3"></div>
<div className="absolute inset-0 border-2 border-black dark:border-gray-500 bg-primary dark:bg-primary transform -rotate-3 translate-x-4 translate-y-4 opacity-80"></div>
<div className="absolute inset-0 flex items-center justify-center z-10">
<span className="material-icons text-8xl">rocket_launch</span>
</div>
</div>
</div>
</div>
</section>
{/* Footer */}
<footer className="bg-background-light dark:bg-background-dark border-t-2 border-black dark:border-gray-700 py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 mb-12">
<div className="col-span-1 md:col-span-1">
<Link className="flex items-center gap-2 mb-4" href={`/${locale}`}>
<span className="material-icons text-2xl">smart_toy</span>
<span className="font-display font-bold text-lg tracking-tight">Agent Park</span>
</Link>
<p className="text-sm text-gray-600 dark:text-gray-400">
Discover and explore high-quality AI projects from across the web. Curated for developers and enthusiasts.
</p>
</div>
<div>
<h4 className="font-display font-bold text-lg mb-4">Resources</h4>
<ul className="space-y-2 text-sm font-sans text-gray-600 dark:text-gray-400">
<li><Link className="hover:text-black dark:hover:text-white" href="#">Blog</Link></li>
<li><Link className="hover:text-black dark:hover:text-white" href="#">Newsletter</Link></li>
<li><Link className="hover:text-black dark:hover:text-white" href="#">Documentation</Link></li>
</ul>
</div>
<div>
<h4 className="font-display font-bold text-lg mb-4">Legal</h4>
<ul className="space-y-2 text-sm font-sans text-gray-600 dark:text-gray-400">
<li><Link className="hover:text-black dark:hover:text-white" href="#">Privacy Policy</Link></li>
<li><Link className="hover:text-black dark:hover:text-white" href="#">Terms of Service</Link></li>
</ul>
</div>
<div>
<h4 className="font-display font-bold text-lg mb-4">Follow Us</h4>
<div className="flex space-x-4">
<Link className="w-10 h-10 border-2 border-black dark:border-gray-500 flex items-center justify-center hover:bg-primary hover:text-black transition-colors font-display font-bold" href="#">X</Link>
<Link className="w-10 h-10 border-2 border-black dark:border-gray-500 flex items-center justify-center hover:bg-primary hover:text-black transition-colors font-display font-bold" href="#">Li</Link>
<Link className="w-10 h-10 border-2 border-black dark:border-gray-500 flex items-center justify-center hover:bg-primary hover:text-black transition-colors font-display font-bold" href="#">Gh</Link>
</div>
</div>
</div>
<div className="border-t border-black dark:border-gray-700 pt-8 flex flex-col md:flex-row justify-between items-center">
<p className="text-sm text-gray-500 font-display">© 2025 Agent Park. All rights reserved.</p>
<div className="mt-4 md:mt-0 font-display text-sm font-bold">
DESIGNED FOR AI BUILDERS
</div>
</div>
</div>
</footer>
</div>
+42 -15
View File
@@ -3,6 +3,7 @@ import { getProjects, getAllTags } from '@/hooks/useProjects'
import { ProjectList } from '@/components/project/ProjectList'
import { TagCloud } from '@/components/project/TagCloud'
import { SearchBar } from '@/components/search/SearchBar'
import { Suspense } from 'react'
interface HomePageProps {
params: Promise<{ locale: string }>
@@ -18,24 +19,50 @@ export default async function HomePage({ params }: HomePageProps) {
])
return (
<div className="container mx-auto px-4 py-8">
<div className="text-center mb-12">
<h1 className="text-4xl font-bold mb-4">{t('title')}</h1>
<p className="text-lg text-muted-foreground">{t('subtitle')}</p>
</div>
<>
{/* Hero Section with floating shapes */}
<main className="relative overflow-hidden">
<div className="absolute top-20 left-10 w-24 h-24 border-2 border-black dark:border-gray-600 rounded-full opacity-20 animate-float hidden lg:block"></div>
<div className="absolute top-40 right-20 w-32 h-20 border-2 border-black dark:border-gray-600 rounded-[2rem] transform -rotate-12 opacity-20 animate-float hidden lg:block" style={{ animationDelay: '1s' }}></div>
<SearchBar locale={locale} />
<section className="max-w-4xl mx-auto px-4 py-20 text-center relative z-10">
<h1 className="font-display text-5xl md:text-7xl font-bold mb-6 tracking-tight leading-tight">
AI PROJECT <br className="hidden md:block" />NAVIGATOR
</h1>
<p className="text-lg md:text-xl text-gray-600 dark:text-gray-300 mb-10 max-w-2xl mx-auto font-sans">
Discover and explore high-quality AI projects from across the web. Curated for developers and enthusiasts.
</p>
<Suspense fallback={<div className="h-16"></div>}>
<SearchBar locale={locale} />
</Suspense>
</section>
<div className="mb-12">
<h2 className="text-2xl font-semibold mb-4">{t('browseByTag')}</h2>
<TagCloud tags={tags} locale={locale} />
</div>
{/* Browse by Tags Section */}
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mb-16">
<h2 className="font-display text-xl font-bold mb-6 border-b-2 border-black dark:border-gray-700 pb-2 inline-block">
BROWSE BY TAGS
</h2>
<TagCloud tags={tags} locale={locale} />
</section>
<div>
<h2 className="text-2xl font-semibold mb-6">{t('featuredProjects')}</h2>
<ProjectList projects={projectsData.projects} locale={locale} />
</div>
</div>
{/* Featured Projects Section */}
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-24">
<div className="flex items-end justify-between mb-8">
<h2 className="font-display text-3xl font-bold">FEATURED PROJECTS</h2>
<a className="hidden md:flex items-center font-display text-sm font-bold hover:underline" href={`/${locale}/projects`}>
VIEW ALL <span className="ml-1"></span>
</a>
</div>
<ProjectList projects={projectsData.projects} locale={locale} featured />
<div className="mt-8 text-center md:hidden">
<a className="inline-flex items-center font-display text-sm font-bold border-b-2 border-black dark:border-white pb-1" href={`/${locale}/projects`}>
VIEW ALL PROJECTS <span className="ml-1"></span>
</a>
</div>
</section>
</main>
</>
)
}
+62 -38
View File
@@ -1,6 +1,8 @@
import { notFound } from 'next/navigation'
import { getProjectBySlug } from '@/hooks/useProjects'
import { ExternalLinkCard } from '@/components/project/ExternalLinkCard'
import { getProjectBySlug, getProjects } from '@/hooks/useProjects'
import { ProjectDetail } from '@/components/project/ProjectDetail'
import { ProjectSidebar } from '@/components/project/ProjectSidebar'
import { RelatedProjects } from '@/components/project/RelatedProjects'
interface ProjectDetailPageProps {
params: Promise<{ locale: string; id: string }>
@@ -9,60 +11,82 @@ interface ProjectDetailPageProps {
export default async function ProjectDetailPage({
params,
}: ProjectDetailPageProps) {
const { locale, id } = await params
const resolvedParams = await params
const { locale, id } = resolvedParams
// Get project by slug
const project = await getProjectBySlug(id)
if (!project) {
notFound()
}
// Get related projects (same tags, excluding current project)
const relatedProjectsResult = await getProjects({
limit: 3,
})
const relatedProjects = relatedProjectsResult.projects
.filter(p => p.id !== project.id)
.filter(p => p.tags.some(t => project.tags.some(pt => pt.id === t.id)))
.slice(0, 3)
return (
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto">
<main className="min-h-screen bg-background-light dark:bg-background-dark text-text-light dark:text-text-dark font-body transition-colors duration-200">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 py-12">
{/* Back Button */}
<div className="mb-8">
<a
className="inline-flex items-center text-sm font-display font-bold text-gray-500 hover:text-black dark:text-gray-400 dark:hover:text-white transition-colors group"
href={`/${locale}/projects`}
className="text-primary hover:underline text-sm"
>
<span className="material-icons text-base mr-1 group-hover:-translate-x-1 transition-transform">
arrow_back
</span>
BACK TO PROJECTS
</a>
</div>
<div className="mb-8">
<h1 className="text-3xl font-bold mb-4">{project.name}</h1>
{project.nameEn && (
<h2 className="text-xl text-muted-foreground mb-4">{project.nameEn}</h2>
)}
<p className="text-lg text-muted-foreground">{project.description}</p>
{/* Main Grid Layout */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12">
{/* Main Content (8 columns) */}
<div className="lg:col-span-8">
<ProjectDetail project={project} locale={locale} />
</div>
{/* Sidebar (4 columns) */}
<aside className="lg:col-span-4">
<ProjectSidebar project={project} locale={locale} />
</aside>
</div>
{project.content && (
<div className="mb-8">
<h3 className="text-xl font-semibold mb-4"></h3>
<div className="prose max-w-none">
<p>{project.content}</p>
</div>
</div>
)}
<div className="mb-8">
<h3 className="text-xl font-semibold mb-4"></h3>
<div className="flex flex-wrap gap-2">
{project.tags.map((tag) => (
<span
key={tag.id}
className="inline-flex items-center px-3 py-1 rounded-full text-sm bg-secondary text-secondary-foreground"
>
{tag.name}
</span>
))}
</div>
</div>
<ExternalLinkCard links={project.links} />
{/* Related Projects Section */}
<RelatedProjects projects={relatedProjects} locale={locale} />
</div>
</div>
</main>
)
}
export async function generateMetadata({ params }: ProjectDetailPageProps) {
const resolvedParams = await params
const { id, locale } = resolvedParams
const project = await getProjectBySlug(id)
if (!project) {
return {
title: 'Project Not Found',
}
}
const displayName = locale === 'en' && project.nameEn ? project.nameEn : project.name
const displayDescription =
locale === 'en' && project.descriptionEn ? project.descriptionEn : project.description
return {
title: `${displayName} - Agent Park`,
description: displayDescription,
}
}
export const revalidate = 300 // ISR: revalidate every 5 minutes
+68 -23
View File
@@ -1,3 +1,4 @@
import { Suspense } from 'react'
import { getTranslations } from 'next-intl/server'
import { getProjects, getAllTags } from '@/hooks/useProjects'
import { ProjectList } from '@/components/project/ProjectList'
@@ -14,7 +15,7 @@ export default async function ProjectsPage({
searchParams,
}: ProjectsPageProps) {
const t = await getTranslations('home')
const [resolvedParams, resolvedSearchParams] = await Promise.all([params, searchParams])
const { locale } = resolvedParams
const search = resolvedSearchParams.search || ''
@@ -27,45 +28,89 @@ export default async function ProjectsPage({
])
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold mb-4"></h1>
<SearchBar locale={locale} />
<main className="container mx-auto px-4 py-12 md:py-16 max-w-7xl relative">
{/* Decorative shapes */}
<div className="absolute top-10 left-0 w-24 h-24 border-2 border-black dark:border-white/20 rounded-full opacity-20 pointer-events-none hidden lg:block"></div>
<div className="absolute top-20 right-10 w-32 h-20 border-2 border-black dark:border-white/20 rounded-[2rem] opacity-20 pointer-events-none transform rotate-12 hidden lg:block"></div>
{/* Page Header */}
<div className="text-center mb-16 relative z-10">
<h1 className="font-display text-5xl md:text-7xl font-bold mb-6 leading-tight tracking-tighter">
PROJECT <br className="md:hidden" />NAVIGATOR
</h1>
<p className="text-lg md:text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto font-light leading-relaxed">
Discover and explore high-quality AI projects from across the web. <br className="hidden md:inline" />
Curated for developers and enthusiasts.
</p>
</div>
<div className="mb-8">
<h2 className="text-xl font-semibold mb-4">{t('browseByTag')}</h2>
<TagCloud tags={tags} locale={locale} />
{/* Search and Filter Section */}
<div className="bg-surface-light dark:bg-surface-dark border-2 border-black dark:border-white/20 p-6 md:p-8 mb-12 shadow-neo dark:shadow-none">
<Suspense fallback={<div className="h-20"></div>}>
<SearchBar locale={locale} />
</Suspense>
<div className="border-t-2 border-gray-100 dark:border-gray-800 pt-6 mt-8">
<h3 className="font-display font-bold uppercase text-sm mb-4 border-b-2 border-black inline-block dark:border-primary pb-1">
Browse by Tags
</h3>
<TagCloud tags={tags} locale={locale} activeTag={tag} />
</div>
</div>
<div>
<ProjectList projects={projectsData.projects} locale={locale} />
{projectsData.pagination.totalPages > 1 && (
<div className="flex justify-center gap-2 mt-8">
{/* Projects Section */}
<div className="flex justify-between items-end mb-8 border-b border-black dark:border-white/20 pb-4">
<h2 className="font-display font-bold text-3xl uppercase tracking-tight">
{tag ? `Tag: ${tag}` : 'All Projects'}
</h2>
<span className="font-display text-xs font-bold uppercase text-gray-500">
{projectsData.pagination.total} projects
</span>
</div>
<ProjectList projects={projectsData.projects} locale={locale} />
{/* Pagination */}
{projectsData.pagination.totalPages > 1 && (
<div className="mt-16 flex justify-center">
<div className="flex border-2 border-black dark:border-white/20 bg-white dark:bg-surface-dark shadow-neo dark:shadow-none">
{page > 1 && (
<a
href={`?search=${search}&tag=${tag}&page=${page - 1}`}
className="px-4 py-2 border rounded-lg hover:bg-secondary"
className="px-4 py-2 border-r border-black dark:border-white/20 hover:bg-gray-100 dark:hover:bg-white/10 transition-colors"
>
</a>
)}
<span className="px-4 py-2">
{page} / {projectsData.pagination.totalPages}
</span>
{Array.from({ length: Math.min(5, projectsData.pagination.totalPages) }, (_, i) => {
const pageNum = Math.max(1, page - 2) + i
if (pageNum > projectsData.pagination.totalPages) return null
return (
<a
key={pageNum}
href={`?search=${search}&tag=${tag}&page=${pageNum}`}
className={`px-4 py-2 font-display font-bold text-sm border-r border-black dark:border-white/20 ${
pageNum === page
? 'bg-primary text-black'
: 'hover:bg-gray-100 dark:hover:bg-white/10'
} transition-colors`}
>
{pageNum}
</a>
)
})}
{page < projectsData.pagination.totalPages && (
<a
href={`?search=${search}&tag=${tag}&page=${page + 1}`}
className="px-4 py-2 border rounded-lg hover:bg-secondary"
className="px-4 py-2 hover:bg-gray-100 dark:hover:bg-white/10 transition-colors"
>
</a>
)}
</div>
)}
</div>
</div>
</div>
)}
</main>
)
}
+3 -2
View File
@@ -64,10 +64,11 @@ export async function POST(request: NextRequest) {
if (!projectValidation.success) {
results.failed++
const firstError = projectValidation.error.errors[0]
results.errors.push({
index: i,
field: projectValidation.error.errors[0].path.join('.'),
message: projectValidation.error.errors[0].message,
field: firstError?.path.join('.') || 'unknown',
message: firstError?.message || 'Validation failed',
value: projectData,
})
continue
+83 -36
View File
@@ -1,59 +1,106 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Space+Mono:wght@400;700&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
/* Neo-brutalism theme colors - Light mode */
--background: 40 20% 95%; /* #F5F2EB */
--foreground: 0 0% 0%; /* #000000 */
--card: 0 0% 100%; /* #FFFFFF */
--card-foreground: 0 0% 0%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--popover-foreground: 0 0% 0%;
--primary: 48 100% 50%; /* #FFD700 */
--primary-foreground: 0 0% 0%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--secondary-foreground: 0 0% 0%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--muted-foreground: 0 0% 40%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--accent-foreground: 0 0% 0%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5.9% 10%;
--radius: 0.5rem;
--border: 0 0% 0%; /* #000000 */
--input: 0 0% 0%;
--ring: 0 0% 0%;
--radius: 0rem; /* Sharp corners for neo-brutalism */
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
/* Neo-brutalism theme colors - Dark mode */
--background: 0 0% 7%; /* #121212 */
--foreground: 40 15% 90%; /* #F5F2EB */
--card: 0 0% 12%; /* #1E1E1E */
--card-foreground: 40 15% 90%;
--popover: 0 0% 12%;
--popover-foreground: 40 15% 90%;
--primary: 48 100% 50%; /* #FFD700 */
--primary-foreground: 0 0% 0%;
--secondary: 0 0% 20%;
--secondary-foreground: 40 15% 90%;
--muted: 0 0% 20%;
--muted-foreground: 40 10% 60%;
--accent: 0 0% 20%;
--accent-foreground: 40 15% 90%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
--border: 0 0% 29%; /* #4a4a4a */
--input: 0 0% 29%;
--ring: 48 100% 50%;
--radius: 0rem;
}
body {
@apply bg-background-light dark:bg-background-dark text-text-light dark:text-text-dark font-sans antialiased min-h-screen transition-colors duration-200;
}
/* Scrollbar styling */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background-color: #cbd5e1;
border-radius: 4px;
}
.dark ::-webkit-scrollbar-thumb {
background-color: #475569;
}
}
@layer base {
* {
@apply border-border;
@layer components {
/* Neo-brutalism border utility */
.neo-border {
@apply border-2 border-black dark:border-gray-600;
}
body {
@apply bg-background text-foreground;
/* Neo-brutalism card */
.neo-card {
@apply bg-white dark:bg-surface-dark border-2 border-black dark:border-gray-600 shadow-neo dark:shadow-none;
}
/* Neo-brutalism button */
.neo-btn {
@apply border-2 border-black dark:border-gray-500 shadow-neo-sm dark:shadow-none
active:shadow-none active:translate-x-[2px] active:translate-y-[2px]
transition-all duration-200 font-display font-bold uppercase;
}
/* Neo-brutalism input */
.neo-input {
@apply border-2 border-black dark:border-gray-600 bg-white dark:bg-surface-dark
focus:ring-0 focus:border-primary dark:focus:border-primary
font-display;
}
/* Neo-brutalism tag */
.neo-tag {
@apply border border-black dark:border-gray-500 px-2 py-1 text-xs
font-display font-bold uppercase bg-white dark:bg-gray-800;
}
}
+10 -5
View File
@@ -1,9 +1,6 @@
import type { Metadata } from "next"
import { Inter } from "next/font/google"
import "./globals.css"
const inter = Inter({ subsets: ["latin"] })
export const metadata: Metadata = {
title: "Agent Park - AI 项目导航",
description: "发现和探索全网优质 AI 项目",
@@ -15,8 +12,16 @@ export default function RootLayout({
children: React.ReactNode
}>) {
return (
<html lang="zh">
<body className={inter.className}>{children}</body>
<html lang="zh" suppressHydrationWarning>
<head>
{/* Load fonts via CSS */}
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet"/>
{/* Material Icons */}
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
</head>
<body className="font-sans antialiased">
{children}
</body>
</html>
)
}