feat(i18n): implement comprehensive blog post enhancements
- Add avatar to personal info in data.ts - Remove redundant headings from blog posts - Reorder imports in utils.ts for consistency - Implement new blog layout components including: - PostMeta for displaying post metadata - TableOfContents for navigation - BlogNavigation for post pagination - ShareButtons for social sharing - AuthorCard for author information - Enhance BlogPostLayout with: - Improved typography and spacing - Responsive sidebar layout - Dark mode support - Better code block styling - Remove outdated i18n guide documentation - Add comprehensive styling for all new components
This commit is contained in:
@@ -1,24 +1,49 @@
|
||||
---
|
||||
import type { MarkdownLayoutProps } from 'astro';
|
||||
import { type Lang } from '@/i18n/utils';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
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 "../styles/global.css";
|
||||
|
||||
export interface Props {
|
||||
// Define the frontmatter structure
|
||||
interface FrontmatterProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
publishDate?: string;
|
||||
date?: string; // Alternative field name for publish date
|
||||
author?: string;
|
||||
tags?: string[];
|
||||
category?: string | string[];
|
||||
readingTime?: number;
|
||||
readTime?: string; // Alternative field name for reading time
|
||||
}
|
||||
|
||||
// Use Astro's MarkdownLayoutProps for proper type safety
|
||||
export type Props = MarkdownLayoutProps<FrontmatterProps>;
|
||||
|
||||
// Access frontmatter data correctly for markdown layouts
|
||||
const { frontmatter } = Astro.props;
|
||||
const {
|
||||
title,
|
||||
description = 'Explore my latest thoughts on coding, tech trends, and developer life.',
|
||||
description,
|
||||
publishDate,
|
||||
author = 'Zhao Guiyang'
|
||||
} = Astro.props;
|
||||
date,
|
||||
tags,
|
||||
category,
|
||||
readTime,
|
||||
} = frontmatter;
|
||||
|
||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
|
||||
// Handle different field names for backward compatibility
|
||||
const finalPublishDate = publishDate || date;
|
||||
const finalReadingTime = readTime ? parseInt(readTime.replace(/\D/g, '')) : undefined;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
@@ -45,33 +70,63 @@ const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
<!-- Glass Header with navigation -->
|
||||
<GlassHeader lang={lang} client:load />
|
||||
|
||||
|
||||
|
||||
<!-- Main content with proper spacing for fixed header -->
|
||||
<div class="pt-16">
|
||||
<main class="container mx-auto px-4 py-8 max-w-4xl">
|
||||
<!-- Blog post header -->
|
||||
<header class="mb-8 text-center">
|
||||
<h1 class="text-4xl font-bold text-foreground mb-4">{title}</h1>
|
||||
{publishDate && (
|
||||
<div class="text-muted-foreground mb-2">
|
||||
<time datetime={publishDate}>{new Date(publishDate).toLocaleDateString(lang === 'zh' ? 'zh-CN' : 'en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}</time>
|
||||
</div>
|
||||
)}
|
||||
{author && (
|
||||
<div class="text-muted-foreground">
|
||||
By {author}
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
|
||||
<!-- Blog post content with typography styles -->
|
||||
<article class="prose prose-lg dark:prose-invert max-w-none prose-headings:text-foreground prose-p:text-muted-foreground prose-strong:text-foreground prose-code:text-foreground prose-pre:bg-muted prose-blockquote:border-l-primary prose-blockquote:text-muted-foreground prose-a:text-primary hover:prose-a:text-primary/80 prose-li:text-muted-foreground prose-img:rounded-lg prose-img:shadow-lg">
|
||||
<slot />
|
||||
</article>
|
||||
</main>
|
||||
<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">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<div class="grid grid-cols-1 xl:grid-cols-4 gap-6 lg:gap-8">
|
||||
<!-- Main Content -->
|
||||
<main class="xl:col-span-3 order-2 xl:order-1">
|
||||
<!-- Blog post header -->
|
||||
<header class="mb-10">
|
||||
<h1 class="text-3xl sm:text-4xl lg:text-5xl font-bold text-foreground mb-6 leading-tight tracking-tight">
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
{description && (
|
||||
<p class="text-lg sm:text-xl text-muted-foreground mb-8 leading-relaxed max-w-4xl">
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<PostMeta
|
||||
lang={lang}
|
||||
publishDate={finalPublishDate}
|
||||
readingTime={finalReadingTime}
|
||||
tags={tags}
|
||||
category={category}
|
||||
className="justify-start"
|
||||
/>
|
||||
</header>
|
||||
|
||||
<!-- Blog post content with typography styles -->
|
||||
<article class="prose prose-lg dark:prose-invert max-w-none prose-headings:scroll-mt-24 prose-headings:font-bold prose-headings:tracking-tight prose-h1:text-2xl sm:prose-h1:text-3xl prose-h2:text-xl sm:prose-h2:text-2xl prose-h3:text-lg sm:prose-h3:text-xl prose-h4:text-base sm:prose-h4:text-lg prose-p:text-base sm:prose-p:text-lg prose-p:leading-relaxed prose-p:mb-6 prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-strong:text-foreground prose-code:text-sm prose-code:px-2 prose-code:py-1 prose-code:rounded prose-pre:bg-muted prose-pre:border prose-pre:text-sm prose-blockquote:border-l-primary prose-blockquote:bg-muted/30 prose-blockquote:text-base prose-li:text-base sm:prose-li:text-lg prose-li:leading-relaxed">
|
||||
<slot />
|
||||
</article>
|
||||
|
||||
<!-- Blog Navigation -->
|
||||
<div class="mt-8 sm:mt-12">
|
||||
<BlogNavigation />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="xl:col-span-1 order-1 xl:order-2">
|
||||
<div class="xl:sticky xl:top-24 space-y-6 sm:space-y-8 xl:space-y-12 xl:max-h-[calc(100vh-6rem)] xl:overflow-y-auto">
|
||||
<!-- Table of Contents -->
|
||||
<div class="bg-card/50 backdrop-blur-sm rounded-2xl border border-border h-auto xl:h-[400px] lg:h-[500px] overflow-y-auto">
|
||||
<TableOfContents lang={lang} />
|
||||
</div>
|
||||
|
||||
<!-- Author Card -->
|
||||
<AuthorCard lang={lang} />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
@@ -122,4 +177,147 @@ const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
background-color var(--transition-standard),
|
||||
color var(--transition-standard);
|
||||
}
|
||||
|
||||
/* Enhanced prose styles for better readability */
|
||||
.prose {
|
||||
line-height: 1.75;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.prose {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
.prose h1,
|
||||
.prose h2,
|
||||
.prose h3,
|
||||
.prose h4 {
|
||||
scroll-margin-top: 6rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.025em;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Hide the first H1 in markdown content to avoid duplicate titles */
|
||||
.prose h1:first-of-type {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.prose h1 {
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.prose h1 {
|
||||
font-size: 1.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.prose h2 {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.prose h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.prose h3 {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.prose h3 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.prose h4 {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.prose h4 {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
.prose p {
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.prose li {
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.prose img {
|
||||
margin: 2rem auto;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.prose pre {
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid hsl(var(--border));
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.prose code {
|
||||
font-size: 0.875rem;
|
||||
padding: 0.125rem 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.prose blockquote {
|
||||
border-left: 4px solid hsl(var(--primary));
|
||||
background: hsl(var(--muted) / 0.3);
|
||||
border-radius: 0 0.5rem 0.5rem 0;
|
||||
padding: 1rem 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.prose blockquote {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive sidebar */
|
||||
@media (max-width: 1023px) {
|
||||
aside {
|
||||
order: -1;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
position: static !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Line clamp utilities */
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.line-clamp-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user