feat(blog): add blog feature with layout, list component and i18n support

- Create BlogLayout for consistent blog page structure
- Implement BlogList component with responsive design and line-clamp
- Add blog navigation to header with proper routing
- Include i18n support for both English and Chinese
- Add sample blog pages with mock data
This commit is contained in:
joyzhao
2025-06-16 21:52:16 +08:00
parent f31fbb20a6
commit b4b2153bde
7 changed files with 487 additions and 3 deletions

View File

@@ -0,0 +1,94 @@
---
import { type Lang } from '@/i18n/utils';
import { defaultLang } from '@/i18n/ui';
import GlassHeader from '@/components/GlassHeader';
import Footer from '@/components/Footer';
import "../styles/global.css";
export interface Props {
title: string;
description?: string;
}
const { title, description = 'Explore my latest thoughts on coding, tech trends, and developer life.' } = Astro.props;
const lang = Astro.currentLocale as Lang || defaultLang;
---
<!doctype html>
<html lang={lang}>
<head>
<meta charset="UTF-8" />
<meta name="description" content={description} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
</head>
<body class="min-h-screen bg-background font-sans antialiased selection:bg-purple-500/20 selection:text-purple-500">
<div
class="fixed inset-0 -z-10 h-full w-full bg-background bg-[radial-gradient(ellipse_80%_80%_at_50%_-20%,rgba(120,119,198,0.3),rgba(255,255,255,0))]"
>
</div>
<!-- Glass Header with navigation -->
<GlassHeader lang={lang} client:load />
<!-- Main content with proper spacing for fixed header -->
<div class="pt-16">
<slot />
</div>
<!-- Footer -->
<Footer lang={lang} client:load />
</body>
</html>
<script is:inline>
const getThemePreference = () => {
if (typeof localStorage !== "undefined" && localStorage.getItem("theme")) {
return localStorage.getItem("theme");
}
return window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
};
const isDark = getThemePreference() === "dark";
document.documentElement.classList[isDark ? "add" : "remove"]("dark");
if (typeof localStorage !== "undefined") {
const observer = new MutationObserver(() => {
const isDark = document.documentElement.classList.contains("dark");
localStorage.setItem("theme", isDark ? "dark" : "light");
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["class"],
});
}
</script>
<style>
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
scroll-behavior: smooth;
}
:root {
--transition-standard: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
body {
transition:
background-color var(--transition-standard),
color var(--transition-standard);
}
</style>