Files
zhaoguiyang.site/src/layouts/BlogPostLayout.astro

280 lines
8.2 KiB
Plaintext

---
import Layout from './Layout.astro';
import type { MarkdownLayoutProps } from 'astro';
import { type Lang } from '@/types/i18n';
import { type FrontmatterProps } from '@/types';
import { defaultLang } from '@/i18n/ui';
import { useTranslations } from '@/i18n/utils';
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 Container from '../components/ui/Container';
import GiscusComments from '@/components/blog/GiscusComments';
// 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,
publishDate,
date,
tags,
tagId,
category,
categoryId,
readTime,
} = frontmatter;
const lang = Astro.currentLocale as Lang || defaultLang;
const t = useTranslations(lang);
const blogListUrl = lang === 'zh' ? '/zh/blog' : '/blog';
// Handle different field names for backward compatibility
const finalPublishDate = publishDate || date;
const finalReadingTime = readTime ? parseInt(readTime.replace(/\D/g, '')) : undefined;
---
<Layout title={title} description={description}>
<!-- Glass Header with navigation -->
<GlassHeader lang={lang} client:load />
<!-- Main content with proper spacing for fixed header -->
<div class="pt-16 sm:pt-20">
<Container client:load 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 -->
<main class="xl:col-span-3 order-2 xl:order-1">
<!-- Blog post header -->
<header class="mb-10">
<a
href={blogListUrl}
class="inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-primary transition-colors duration-200 mb-6 group"
>
<svg
width="16"
height="16"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class="group-hover:-translate-x-1 transition-transform duration-200"
>
<path d="M6.85355 3.85355C7.04882 3.65829 7.04882 3.34171 6.85355 3.14645C6.65829 2.95118 6.34171 2.95118 6.14645 3.14645L2.14645 7.14645C1.95118 7.34171 1.95118 7.65829 2.14645 7.85355L6.14645 11.8536C6.34171 12.0488 6.65829 12.0488 6.85355 11.8536C7.04882 11.6583 7.04882 11.3417 6.85355 11.1464L3.20711 7.5L6.85355 3.85355Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path>
</svg>
{t('blog.backToList')}
</a>
<PostMeta
lang={lang}
publishDate={finalPublishDate}
readingTime={finalReadingTime}
tags={tags}
tagId={tagId}
category={category}
categoryId={categoryId}
className="justify-start mb-4"
/>
<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>
)}
</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
prose-code:before:content-none prose-code:after:content-none">
<slot />
</article>
<!-- Blog Navigation -->
<div class="mt-8 sm:mt-12">
<BlogNavigation />
</div>
<!-- Comments Section -->
<div class="mt-10 sm:mt-16 border-t border-border pt-10">
<GiscusComments client:only="react" lang={lang} />
</div>
<!-- Author Card moved to bottom with enhanced styling -->
<div class="mt-10 sm:mt-16 border-t border-border pt-10">
<div class="bg-card/30 backdrop-blur-sm rounded-2xl border border-border p-6 shadow-sm hover:shadow-md transition-all duration-300">
<AuthorCard lang={lang} />
</div>
</div>
</main>
<!-- Sidebar with Table of Contents -->
<aside class="xl:col-span-1 order-1 xl:order-2 md:mb-0 mb-6">
<div class="xl:sticky xl:top-24">
<!-- Table of Contents without height restriction -->
<TableOfContents lang={lang} />
</div>
</aside>
</div>
</div>
</Container>
</div>
<!-- Footer -->
<Footer lang={lang} client:load />
</Layout>
<style>
/* 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>