feat(ui): add Container component and replace manual container divs
Implement a reusable Container component with size variants (sm, md, lg, xl, full) to standardize content width across the application. Replace all manual container divs with the new component for consistent styling and maintainability.
This commit is contained in:
@@ -3,6 +3,7 @@ import { personalInfo } from "@/lib/data";
|
||||
import { motion } from "framer-motion";
|
||||
import { useState, useEffect } from "react";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import Container from "./ui/Container";
|
||||
import { type FooterProps } from "@/types";
|
||||
|
||||
export default function Footer({ lang: propLang }: FooterProps) {
|
||||
@@ -20,7 +21,7 @@ export default function Footer({ lang: propLang }: FooterProps) {
|
||||
const t = useTranslations(lang);
|
||||
return (
|
||||
<footer className="border-t border-purple-500/10 py-6 bg-gradient-to-b from-background to-muted/20 backdrop-blur-sm">
|
||||
<div className="container max-w-4xl mx-auto px-6 md:px-4">
|
||||
<Container size="sm">
|
||||
<motion.div
|
||||
className="flex flex-col md:flex-row justify-between items-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
@@ -67,7 +68,7 @@ export default function Footer({ lang: propLang }: FooterProps) {
|
||||
</motion.span>
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</Container>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { personalInfo } from "@/lib/data";
|
||||
import LanguageSwitcher from "./LanguageSwitcher";
|
||||
import ThemeToggle from "./ui/theme-toggle";
|
||||
import Container from "./ui/Container";
|
||||
import { useTranslations, getLocalizedPath, type Lang } from "@/i18n/utils";
|
||||
import { useState, useEffect } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
@@ -43,7 +44,7 @@ export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
|
||||
? 'backdrop-blur-md backdrop-filter bg-white/80 dark:bg-black/80 border-b border-border/30 shadow-lg shadow-black/5 dark:shadow-black/20'
|
||||
: 'bg-transparent'
|
||||
}`}>
|
||||
<div className="container max-w-4xl mx-auto p-4 flex justify-between items-center">
|
||||
<Container size="sm" className="p-4 flex justify-between items-center">
|
||||
<a
|
||||
className="flex items-center text-lg font-medium transition-opacity duration-150 hover:opacity-80"
|
||||
href={getLocalizedPath('/', lang)}
|
||||
@@ -85,7 +86,7 @@ export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
|
||||
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
{/* Mobile Navigation */}
|
||||
<div className={`md:hidden overflow-hidden transition-all duration-200 ease-out ${
|
||||
|
||||
39
src/components/ui/Container.tsx
Normal file
39
src/components/ui/Container.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { type ReactNode } from "react";
|
||||
|
||||
interface ContainerProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
size?: "sm" | "md" | "lg" | "xl" | "full";
|
||||
}
|
||||
|
||||
/**
|
||||
* Container component for consistent content width across the site
|
||||
* @param children - The content to be wrapped
|
||||
* @param className - Additional classes to apply
|
||||
* @param size - Container size variant (sm: max-w-4xl, md: max-w-6xl, lg: max-w-7xl, xl: max-w-screen-2xl, full: w-full)
|
||||
*/
|
||||
export default function Container({
|
||||
children,
|
||||
className,
|
||||
size = "md"
|
||||
}: ContainerProps) {
|
||||
// Map size variants to max-width classes
|
||||
const sizeClasses = {
|
||||
sm: "max-w-4xl", // Same as header/footer (narrower content)
|
||||
md: "max-w-6xl", // Default for most content areas
|
||||
lg: "max-w-7xl", // For wider content like blog posts with sidebars
|
||||
xl: "max-w-screen-2xl", // For very wide content
|
||||
full: "w-full" // No max width constraint
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
"container mx-auto px-4 sm:px-6 lg:px-8",
|
||||
sizeClasses[size],
|
||||
className
|
||||
)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { type Lang, type FrontmatterProps } from '@/types';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '../components/ui/Container';
|
||||
|
||||
// Use Astro's MarkdownLayoutProps for proper type safety
|
||||
export type Props = MarkdownLayoutProps<FrontmatterProps>;
|
||||
@@ -21,7 +22,7 @@ const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
<div class="fixed inset-0 -z-10 h-full w-full bg-background">
|
||||
<!-- Additional subtle gradient for about page -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-50/30 via-transparent to-blue-50/20 dark:from-purple-950/20 dark:via-transparent dark:to-blue-950/10">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Glass Header with navigation -->
|
||||
@@ -29,7 +30,7 @@ const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
|
||||
<!-- Main content with proper spacing for fixed header -->
|
||||
<div class="pt-16 sm:pt-20">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-8">
|
||||
<Container client:load size="md" className="py-8 md:py-12">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<div class="grid grid-cols-1 xl:grid-cols-4 gap-6 lg:gap-8">
|
||||
<!-- Main Content -->
|
||||
|
||||
@@ -10,6 +10,7 @@ import AuthorCard from '@/components/AuthorCard';
|
||||
import TableOfContents from '@/components/layout/TableOfContents.astro';
|
||||
import BlogNavigation from '@/components/layout/BlogNavigation.astro';
|
||||
import PostMeta from '@/components/blog/PostMeta.astro';
|
||||
import Container from '../components/ui/Container';
|
||||
|
||||
// Use Astro's MarkdownLayoutProps for proper type safety
|
||||
export type Props = MarkdownLayoutProps<FrontmatterProps>;
|
||||
@@ -41,7 +42,7 @@ const finalReadingTime = readTime ? parseInt(readTime.replace(/\D/g, '')) : unde
|
||||
|
||||
<!-- Main content with proper spacing for fixed header -->
|
||||
<div class="pt-16 sm:pt-20">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-8">
|
||||
<Container client:load size="md" className="py-6 sm:py-8">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<div class="grid grid-cols-1 xl:grid-cols-4 gap-6 lg:gap-8">
|
||||
<!-- Main Content -->
|
||||
@@ -98,7 +99,7 @@ const finalReadingTime = readTime ? parseInt(readTime.replace(/\D/g, '')) : unde
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
|
||||
@@ -7,6 +7,7 @@ import BlogLayout from './BlogLayout.astro';
|
||||
import BlogList from '../components/blog/BlogList.astro';
|
||||
import CategoryCard from '../components/blog/CategoryCard.astro';
|
||||
import TagCard from '../components/blog/TagCard.astro';
|
||||
import Container from '../components/ui/Container';
|
||||
import { type Lang } from '@/i18n/utils';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
import { type BlogPost } from '@/types';
|
||||
@@ -53,7 +54,7 @@ const localizedText = getLocalizedText();
|
||||
---
|
||||
|
||||
<BlogLayout title={title} description={Astro.props.description}>
|
||||
<div class="container max-w-6xl px-4 py-8">
|
||||
<Container client:load size="md" className="py-8">
|
||||
<!-- Header Section - Centered at the top -->
|
||||
<div class="text-center mb-10">
|
||||
<h1 class="text-4xl font-bold mb-3">
|
||||
@@ -91,5 +92,5 @@ const localizedText = getLocalizedText();
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</BlogLayout>
|
||||
@@ -3,6 +3,7 @@ import BlogLayout from '../../layouts/BlogLayout.astro';
|
||||
import BlogList from '../../components/blog/BlogList.astro';
|
||||
import CategoryCard from '../../components/blog/CategoryCard.astro';
|
||||
import TagCard from '../../components/blog/TagCard.astro';
|
||||
import Container from '../../components/ui/Container';
|
||||
import { type BlogPost } from '@/types';
|
||||
import { type Lang } from '@/i18n/utils';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
@@ -43,7 +44,7 @@ const sortedBlogPosts = sortPostsByDate(blogPosts);
|
||||
<BlogLayout title="Blog - Joy Zhao" description="Dive into my thoughts on coding, tech trends, and developer life. Explore my latest posts below.">
|
||||
<main class="min-h-screen">
|
||||
<!-- Header Section -->
|
||||
<div class="container mx-auto px-4 pt-24 pb-12">
|
||||
<Container client:load size="md" className="pt-24 pb-12">
|
||||
<div class="text-center mb-16">
|
||||
<h1 class="text-5xl md:text-6xl font-bold bg-gradient-to-r from-foreground via-purple-600 to-purple-800 dark:from-foreground dark:via-purple-200 dark:to-purple-300 bg-clip-text text-transparent mb-6">
|
||||
Our <span class="text-purple-500">Latest</span> Blog
|
||||
@@ -52,10 +53,10 @@ const sortedBlogPosts = sortPostsByDate(blogPosts);
|
||||
Dive into my thoughts on coding, tech trends, and developer life. Explore my latest posts below.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="container mx-auto px-4 pb-20">
|
||||
<Container client:load size="md" className="pb-20">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
||||
<!-- 侧边栏 -->
|
||||
<div class="lg:col-span-1 space-y-8">
|
||||
|
||||
@@ -3,6 +3,7 @@ import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import SkillsMarquee from "@/components/SkillsMarquee";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container";
|
||||
import { useTranslations, type Lang } from "@/i18n/utils";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import { personalInfo, services } from "@/lib/data";
|
||||
@@ -14,14 +15,14 @@ const pageTitle = t('site.title');
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<GlassHeader client:only="react" />
|
||||
<GlassHeader client:only="react" lang={lang} />
|
||||
<main class="min-h-screen">
|
||||
<!-- Hero Section - Inlined Content -->
|
||||
<section class="py-32 relative overflow-hidden min-h-screen flex items-center">
|
||||
<section class="py-32 relative overflow-hidden min-h-screen flex flex-col justify-center">
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-900/20 via-purple-800/10 to-purple-700/20 dark:from-purple-900/30 dark:via-purple-800/20 dark:to-purple-700/30"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<!-- Greeting -->
|
||||
<div class="flex items-center justify-center mb-6">
|
||||
@@ -162,14 +163,14 @@ const pageTitle = t('site.title');
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<SkillsMarquee lang={lang} client:only="react" />
|
||||
|
||||
<!-- Services Section - Inlined Content -->
|
||||
<section id="services" class="py-20 px-4 bg-gradient-to-br from-slate-50 to-blue-50 dark:from-slate-900 dark:to-slate-800">
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<section id="services" class="py-20 bg-gradient-to-br from-slate-50 to-blue-50 dark:from-slate-900 dark:to-slate-800">
|
||||
<Container client:load size="md">
|
||||
<h2 class="text-4xl font-bold text-center mb-16 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
|
||||
Services I Offer
|
||||
</h2>
|
||||
@@ -196,7 +197,7 @@ const pageTitle = t('site.title');
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- About Section - Inlined Content -->
|
||||
@@ -204,7 +205,7 @@ const pageTitle = t('site.title');
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-blue-900/10 via-purple-800/5 to-indigo-700/10 dark:from-blue-900/20 dark:via-purple-800/10 dark:to-indigo-700/20"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<!-- Section Title -->
|
||||
<div class="flex items-center justify-center mb-6">
|
||||
@@ -331,7 +332,7 @@ const pageTitle = t('site.title');
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Projects Section - Inlined Content -->
|
||||
@@ -339,7 +340,7 @@ const pageTitle = t('site.title');
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-transparent via-purple-900/5 to-transparent"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-4xl md:text-5xl font-bold mb-4 bg-gradient-to-r from-purple-400 to-purple-600 bg-clip-text text-transparent">
|
||||
Latest Projects
|
||||
@@ -547,7 +548,7 @@ const pageTitle = t('site.title');
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</main>
|
||||
<Footer lang={lang} client:only="react" />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container";
|
||||
import { useTranslations, type Lang } from "@/i18n/utils";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import { projects } from "@/lib/data";
|
||||
@@ -23,7 +24,7 @@ const currentProjects = projects[lang as keyof typeof projects] || projects.en;
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-900/20 via-purple-800/10 to-purple-700/20 dark:from-purple-900/30 dark:via-purple-800/20 dark:to-purple-700/30"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<!-- Main title -->
|
||||
<h1 class="text-5xl md:text-6xl font-bold mb-6 bg-gradient-to-r from-gray-900 via-purple-600 to-purple-800 dark:from-white dark:via-purple-200 dark:to-purple-300 bg-clip-text text-transparent">
|
||||
@@ -35,7 +36,7 @@ const currentProjects = projects[lang as keyof typeof projects] || projects.en;
|
||||
{t('projects.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Projects Grid Section -->
|
||||
@@ -43,7 +44,7 @@ const currentProjects = projects[lang as keyof typeof projects] || projects.en;
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-transparent via-purple-900/5 to-transparent"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 items-stretch">
|
||||
{currentProjects.map((project) => (
|
||||
<div class={`group overflow-hidden border border-${project.color}-500/20 hover:border-${project.color}-500/40 h-full flex flex-col transition-all duration-500 hover:scale-105 bg-white/10 dark:bg-gray-800/50 backdrop-blur-sm rounded-xl relative`}>
|
||||
|
||||
@@ -3,6 +3,7 @@ import BlogLayout from '../../../layouts/BlogLayout.astro';
|
||||
import BlogList from '../../../components/blog/BlogList.astro';
|
||||
import CategoryCard from '../../../components/blog/CategoryCard.astro';
|
||||
import TagCard from '../../../components/blog/TagCard.astro';
|
||||
import Container from '../../../components/ui/Container';
|
||||
import { type BlogPost } from '@/types';
|
||||
import { type Lang } from '@/i18n/utils';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
@@ -55,19 +56,19 @@ const tags = extractTags(allPostsArray);
|
||||
<BlogLayout title="博客 - 赵桂阳" description="深入我对编程、技术趋势和开发者生活的思考。探索我的最新文章。">
|
||||
<main class="min-h-screen">
|
||||
<!-- 头部区域 -->
|
||||
<div class="container mx-auto px-4 pt-24 pb-12">
|
||||
<Container client:load size="md" className="pt-24 pb-12">
|
||||
<div class="text-center mb-16">
|
||||
<h1 class="text-5xl md:text-6xl font-bold bg-gradient-to-r from-foreground via-purple-600 to-purple-800 dark:from-foreground dark:via-purple-200 dark:to-purple-300 bg-clip-text text-transparent mb-6">
|
||||
我的 <span class="text-purple-500">最新</span> 博客
|
||||
我的<span class="text-purple-500">博客</span>
|
||||
</h1>
|
||||
<p class="text-xl text-muted-foreground max-w-3xl mx-auto">
|
||||
深入我对编程、技术趋势和开发者生活的思考。探索我的最新文章。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<div class="container mx-auto px-4 pb-20">
|
||||
<Container client:load size="md" className="pb-20">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
||||
<!-- 侧边栏 -->
|
||||
<div class="lg:col-span-1 space-y-8">
|
||||
|
||||
@@ -3,6 +3,7 @@ import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import SkillsMarquee from "@/components/SkillsMarquee";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container";
|
||||
import { useTranslations, type Lang } from "@/i18n/utils";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import { personalInfo, services } from "@/lib/data";
|
||||
@@ -14,14 +15,14 @@ const pageTitle = t('site.title');
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<GlassHeader client:only="react" />
|
||||
<GlassHeader client:only="react" lang={lang} />
|
||||
<main class="min-h-screen">
|
||||
<!-- Hero Section - Inlined Content -->
|
||||
<section class="py-32 relative overflow-hidden min-h-screen flex items-center">
|
||||
<section class="py-32 relative overflow-hidden min-h-screen flex flex-col justify-center">
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-900/20 via-purple-800/10 to-purple-700/20 dark:from-purple-900/30 dark:via-purple-800/20 dark:to-purple-700/30"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<!-- Greeting -->
|
||||
<div class="flex items-center justify-center mb-6">
|
||||
@@ -106,6 +107,7 @@ const pageTitle = t('site.title');
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
<!-- Terminal mockup -->
|
||||
<div class="max-w-4xl mx-auto">
|
||||
@@ -162,14 +164,14 @@ const pageTitle = t('site.title');
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<SkillsMarquee lang={lang} client:only="react" />
|
||||
|
||||
<!-- Services Section - Inlined Content -->
|
||||
<section id="services" class="py-20 px-4 bg-gradient-to-br from-slate-50 to-blue-50 dark:from-slate-900 dark:to-slate-800">
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<Container client:load size="md">
|
||||
<h2 class="text-4xl font-bold text-center mb-16 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
|
||||
我提供的服务
|
||||
</h2>
|
||||
@@ -196,7 +198,7 @@ const pageTitle = t('site.title');
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- About Section - Inlined Content -->
|
||||
@@ -204,7 +206,7 @@ const pageTitle = t('site.title');
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-blue-900/10 via-purple-800/5 to-indigo-700/10 dark:from-blue-900/20 dark:via-purple-800/10 dark:to-indigo-700/20"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<!-- Section Title -->
|
||||
<div class="flex items-center justify-center mb-6">
|
||||
@@ -331,7 +333,7 @@ const pageTitle = t('site.title');
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Projects Section - Inlined Content -->
|
||||
@@ -339,7 +341,7 @@ const pageTitle = t('site.title');
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-transparent via-purple-900/5 to-transparent"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-4xl md:text-5xl font-bold mb-4 bg-gradient-to-r from-purple-400 to-purple-600 bg-clip-text text-transparent">
|
||||
我的项目
|
||||
@@ -539,7 +541,7 @@ const pageTitle = t('site.title');
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</main>
|
||||
<Footer lang={lang} client:load />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container";
|
||||
import { useTranslations, type Lang } from "@/i18n/utils";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import { projects } from "@/lib/data";
|
||||
@@ -16,14 +17,14 @@ const currentProjects = projects[lang as keyof typeof projects] || projects.en;
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<GlassHeader client:only="react" />
|
||||
<GlassHeader lang={lang} client:only="react" />
|
||||
<main class="min-h-screen">
|
||||
<!-- Projects Hero Section -->
|
||||
<section class="py-24 relative overflow-hidden">
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-900/20 via-purple-800/10 to-purple-700/20 dark:from-purple-900/30 dark:via-purple-800/20 dark:to-purple-700/30"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<!-- Main title -->
|
||||
<h1 class="text-5xl md:text-6xl font-bold mb-6 bg-gradient-to-r from-gray-900 via-purple-600 to-purple-800 dark:from-white dark:via-purple-200 dark:to-purple-300 bg-clip-text text-transparent">
|
||||
@@ -35,7 +36,7 @@ const currentProjects = projects[lang as keyof typeof projects] || projects.en;
|
||||
{t('projects.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Projects Grid Section -->
|
||||
@@ -43,7 +44,7 @@ const currentProjects = projects[lang as keyof typeof projects] || projects.en;
|
||||
<!-- Background gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-transparent via-purple-900/5 to-transparent"></div>
|
||||
|
||||
<div class="container max-w-6xl mx-auto px-6 md:px-4 relative z-10">
|
||||
<Container client:load size="md" className="relative z-10">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 items-stretch">
|
||||
{currentProjects.map((project) => (
|
||||
<div class={`group overflow-hidden border border-${project.color}-500/20 hover:border-${project.color}-500/40 h-full flex flex-col transition-all duration-500 hover:scale-105 bg-white/10 dark:bg-gray-800/50 backdrop-blur-sm rounded-xl relative`}>
|
||||
|
||||
Reference in New Issue
Block a user