Release v1.2.0: Newspaper-style layout with major UI refinements

This release transforms PING into a sophisticated newspaper-style digital
publication with enhanced readability and professional presentation.

Major Features:
- New FeaturedPostHero component with full-width newspaper design
- Completely redesigned homepage with responsive newspaper grid layout
- Enhanced PostCard component with refined typography and spacing
- Improved mobile-first responsive design (mobile → tablet → desktop → 2XL)
- Archive section with multi-column layout for deeper content discovery

Technical Improvements:
- Enhanced blog post validation and error handling in lib/blog.ts
- Better date handling and normalization for scheduled posts
- Improved Dockerfile with correct content volume mount paths
- Fixed port configuration (3025 throughout stack)
- Updated Tailwind config with refined typography and newspaper aesthetics
- Added getFeaturedPost() function for hero selection

UI/UX Enhancements:
- Professional newspaper-style borders and dividers
- Improved dark mode styling throughout
- Better content hierarchy and visual flow
- Enhanced author bylines and metadata presentation
- Refined color palette with newspaper sophistication

Documentation:
- Added DESIGN_BRIEF_NEWSPAPER_LAYOUT.md detailing design principles
- Added TESTING_RESULTS_25_POSTS.md with test scenarios

This release establishes PING as a premium publication platform for
AI orchestration and contextual intelligence thought leadership.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-10-19 00:23:51 +11:00
parent 796924499d
commit 5e0be60c30
40 changed files with 1865 additions and 324 deletions

View File

@@ -1,6 +1,6 @@
import { notFound } from 'next/navigation'
import { MDXRemote } from 'next-mdx-remote/rsc'
import { getPostData, getAllPostSlugs } from '@/lib/blog'
import { getPostData } from '@/lib/blog'
import BlogHeader from '@/components/BlogHeader'
import BlogFooter from '@/components/BlogFooter'
import Link from 'next/link'
@@ -15,10 +15,8 @@ interface PostPageProps {
}>
}
export async function generateStaticParams() {
const slugs = getAllPostSlugs()
return slugs.map(({ params }) => ({ slug: params.slug }))
}
// Force dynamic rendering - no static generation
export const dynamic = 'force-dynamic'
export async function generateMetadata({ params }: PostPageProps) {
const { slug } = await params
@@ -102,38 +100,38 @@ export default async function PostPage({ params }: PostPageProps) {
))}
</div>
<h1 className="text-h1 font-logo text-carbon-950 dark:text-mulberry-100 mb-6">
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-h1 font-bold text-carbon-950 dark:text-mulberry-100 mb-6 leading-tight">
{post.title}
</h1>
<p className="text-xl text-carbon-700 dark:text-carbon-300 leading-relaxed mb-8">
<p className="text-base sm:text-lg md:text-xl text-carbon-700 dark:text-carbon-300 leading-relaxed mb-8">
{post.description}
</p>
<div className="flex items-center justify-between border-b border-carbon-300 dark:border-carbon-800 pb-8">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between border-b border-carbon-300 dark:border-carbon-800 pb-8 gap-4">
<div className="flex items-center space-x-4">
<div className="w-12 h-12 bg-gradient-to-br from-mulberry-400 to-ocean-500 rounded-full flex items-center justify-center">
<span className="text-carbon-950 font-semibold">
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-br from-mulberry-400 to-ocean-500 rounded-full flex items-center justify-center">
<span className="text-carbon-950 font-semibold text-sm sm:text-base">
{post.author.name.charAt(0)}
</span>
</div>
<div>
<p className="text-carbon-950 dark:text-carbon-200 font-medium">
<p className="text-carbon-950 dark:text-carbon-200 font-medium text-sm sm:text-base">
{post.author.name}
</p>
{post.author.role && (
<p className="text-carbon-600 dark:text-carbon-500 text-sm">
<p className="text-carbon-600 dark:text-carbon-500 text-xs sm:text-sm">
{post.author.role}
</p>
)}
</div>
</div>
<div className="blog-meta text-right">
<time dateTime={post.date} className="block">
<div className="blog-meta sm:text-right">
<time dateTime={post.date} className="block text-sm sm:text-base">
{formattedDate}
</time>
<span className="text-carbon-600 dark:text-carbon-600">
<span className="text-carbon-600 dark:text-carbon-600 text-xs sm:text-sm">
{post.readingTime} min read
</span>
</div>
@@ -141,7 +139,7 @@ export default async function PostPage({ params }: PostPageProps) {
</header>
{/* Article content */}
<div className="prose prose-lg max-w-none">
<div className="prose prose-lg max-w-none dark:prose-invert">
<MDXRemote source={post.content} options={mdxOptions} />
</div>