Skip to content

Commit b209ebe

Browse files
ericyangpanclaude
andcommitted
refactor: use PageHeader component across all pages
- Replace inline page header markup with shared PageHeader component - Improve consistency across listing pages (CLIs, Extensions, IDEs, Models, etc.) - Apply PageHeader to content pages (Articles, Curated Collections, Manifesto, etc.) - Standardize page title and subtitle rendering throughout the application 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent a90d1f4 commit b209ebe

File tree

12 files changed

+48
-106
lines changed

12 files changed

+48
-106
lines changed

src/app/[locale]/ai-coding-landscape/page.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getTranslations } from 'next-intl/server'
22
import { BackToNavigation } from '@/components/controls/BackToNavigation'
33
import Footer from '@/components/Footer'
44
import Header from '@/components/Header'
5+
import PageHeader from '@/components/PageHeader'
56
import { buildVendorMatrix } from '@/lib/landscape-data'
67
import { buildCanonicalUrl, buildOpenGraph, buildTitle, buildTwitterCard } from '@/lib/metadata'
78
import VendorMatrix from './components/VendorMatrix'
@@ -56,15 +57,7 @@ export default async function Page({ params }: Props) {
5657
<>
5758
<Header />
5859
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
59-
{/* Page Header */}
60-
<div className="mb-[var(--spacing-lg)]">
61-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em] mb-[var(--spacing-sm)]">
62-
{tNav('aiCodingLandscape')}
63-
</h1>
64-
<p className="text-base text-[var(--color-text-secondary)] font-light">
65-
{tNav('aiCodingLandscapeDesc')}
66-
</p>
67-
</div>
60+
<PageHeader title={tNav('aiCodingLandscape')} subtitle={tNav('aiCodingLandscapeDesc')} />
6861

6962
{/* Vendor Matrix */}
7063
<VendorMatrix matrixData={matrixData} locale={locale} />

src/app/[locale]/ai-coding-stack/page.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { getTranslations } from 'next-intl/server'
22
import Footer from '@/components/Footer'
33
import Header from '@/components/Header'
4+
import PageHeader from '@/components/PageHeader'
45
import { Link } from '@/i18n/navigation'
56
import { buildCanonicalUrl, buildOpenGraph, buildTitle, buildTwitterCard } from '@/lib/metadata'
67

@@ -52,15 +53,7 @@ export default async function AICodingStackPage({ params }: Props) {
5253

5354
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
5455
<main>
55-
{/* Hero Section */}
56-
<section className="mb-[var(--spacing-lg)]">
57-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em] mb-[var(--spacing-sm)]">
58-
{t('title')}
59-
</h1>
60-
<p className="text-base text-[var(--color-text-secondary)] font-light">
61-
{t('subtitle')}
62-
</p>
63-
</section>
56+
<PageHeader title={t('title')} subtitle={t('subtitle')} />
6457

6558
{/* Stacks Grid Section */}
6659
<section className="mb-[var(--spacing-xl)]">
@@ -79,7 +72,7 @@ export default async function AICodingStackPage({ params }: Props) {
7972
className="block border border-[var(--color-border)] p-[var(--spacing-md)] hover:border-[var(--color-border-strong)] transition-all hover:-translate-y-0.5 group"
8073
>
8174
<div className="flex justify-between items-start mb-[var(--spacing-md)]">
82-
<h3 className="text-[1.5rem] font-semibold tracking-tight">
75+
<h3 className="text-2xl font-semibold tracking-tight">
8376
{t(`${stack.key}.title`)}
8477
</h3>
8578
<span className="text-2xl text-[var(--color-text-muted)] group-hover:text-[var(--color-text)] group-hover:translate-x-1 transition-all">

src/app/[locale]/articles/page.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { getTranslations } from 'next-intl/server'
22
import Footer from '@/components/Footer'
33
import Header from '@/components/Header'
4+
import PageHeader from '@/components/PageHeader'
45
import { Link } from '@/i18n/navigation'
56
import { getArticles } from '@/lib/generated/articles'
67
import { buildCanonicalUrl, buildOpenGraph, buildTitle, buildTwitterCard } from '@/lib/metadata'
@@ -44,21 +45,14 @@ type Props = {
4445

4546
export default async function ArticlesPage({ params }: Props) {
4647
const { locale } = await params
48+
const t = await getTranslations({ locale, namespace: 'pages.articles' })
4749
const articles = getArticles(locale)
4850
return (
4951
<>
5052
<Header />
5153

52-
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-xl)]">
53-
{/* Page Header */}
54-
<div className="text-center mb-[var(--spacing-xl)]">
55-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em] mb-[var(--spacing-sm)]">
56-
Articles
57-
</h1>
58-
<p className="text-base text-[var(--color-text-secondary)] font-light">
59-
Insights, tutorials, and deep dives into the AI coding ecosystem
60-
</p>
61-
</div>
54+
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
55+
<PageHeader title={t('title')} subtitle={t('subtitle')} />
6256

6357
{/* Articles Grid */}
6458
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-[var(--spacing-md)]">

src/app/[locale]/clis/page.client.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import FilterSortBar from '@/components/controls/FilterSortBar'
66
import Footer from '@/components/Footer'
77
import Header from '@/components/Header'
88
import StackTabs from '@/components/navigation/StackTabs'
9+
import PageHeader from '@/components/PageHeader'
910
import type { Locale } from '@/i18n/config'
1011
import { Link } from '@/i18n/navigation'
1112
import { clisData } from '@/lib/generated'
@@ -98,20 +99,18 @@ export default function CLIsPageClient({ locale }: Props) {
9899
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
99100
{/* Main Content */}
100101
<main className="w-full">
101-
<div className="mb-[var(--spacing-lg)]">
102-
<div className="flex items-start justify-between mb-[var(--spacing-sm)]">
103-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em]">{t('title')}</h1>
102+
<PageHeader
103+
title={t('title')}
104+
subtitle={t('subtitle')}
105+
action={
104106
<Link
105107
href={`/${locale}/clis/comparison`}
106108
className="text-sm px-[var(--spacing-md)] py-[var(--spacing-xs)] border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors"
107109
>
108110
{t('compareAll')}
109111
</Link>
110-
</div>
111-
<p className="text-base text-[var(--color-text-secondary)] font-light">
112-
{t('subtitle')}
113-
</p>
114-
</div>
112+
}
113+
/>
115114

116115
<StackTabs activeStack="clis" locale={locale} />
117116

src/app/[locale]/curated-collections/page.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getTranslations } from 'next-intl/server'
22
import CollectionScrollbar from '@/components/CollectionScrollbar'
33
import Footer from '@/components/Footer'
44
import Header from '@/components/Header'
5+
import PageHeader from '@/components/PageHeader'
56
import { getCollections } from '@/lib/collections'
67
import { buildCanonicalUrl, buildOpenGraph, buildTitle, buildTwitterCard } from '@/lib/metadata'
78

@@ -53,13 +54,7 @@ export default async function CuratedCollectionsPage({ params }: Props) {
5354
<Header />
5455

5556
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
56-
{/* Page Header */}
57-
<div className="text-center mb-[var(--spacing-xl)]">
58-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em] mb-[var(--spacing-sm)]">
59-
{t('title')}
60-
</h1>
61-
<p className="text-base text-[var(--color-text-secondary)] font-light">{t('subtitle')}</p>
62-
</div>
57+
<PageHeader title={t('title')} subtitle={t('subtitle')} />
6358

6459
{/* Main Content with Sidebar */}
6560
<div className="flex gap-[var(--spacing-lg)]">

src/app/[locale]/extensions/page.client.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import FilterSortBar from '@/components/controls/FilterSortBar'
66
import Footer from '@/components/Footer'
77
import Header from '@/components/Header'
88
import StackTabs from '@/components/navigation/StackTabs'
9+
import PageHeader from '@/components/PageHeader'
910
import type { Locale } from '@/i18n/config'
1011
import { Link } from '@/i18n/navigation'
1112
import { extensionsData } from '@/lib/generated'
@@ -99,20 +100,18 @@ export default function ExtensionsPageClient({ locale }: Props) {
99100
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
100101
{/* Main Content */}
101102
<main className="w-full">
102-
<div className="mb-[var(--spacing-lg)]">
103-
<div className="flex items-start justify-between mb-[var(--spacing-sm)]">
104-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em]">{t('title')}</h1>
103+
<PageHeader
104+
title={t('title')}
105+
subtitle={t('subtitle')}
106+
action={
105107
<Link
106108
href={`/${locale}/extensions/comparison`}
107109
className="text-sm px-[var(--spacing-md)] py-[var(--spacing-xs)] border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors"
108110
>
109111
{t('compareAll')}
110112
</Link>
111-
</div>
112-
<p className="text-base text-[var(--color-text-secondary)] font-light">
113-
{t('subtitle')}
114-
</p>
115-
</div>
113+
}
114+
/>
116115

117116
<StackTabs activeStack="extensions" locale={locale} />
118117

src/app/[locale]/ides/page.client.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import FilterSortBar from '@/components/controls/FilterSortBar'
66
import Footer from '@/components/Footer'
77
import Header from '@/components/Header'
88
import StackTabs from '@/components/navigation/StackTabs'
9+
import PageHeader from '@/components/PageHeader'
910
import type { Locale } from '@/i18n/config'
1011
import { Link } from '@/i18n/navigation'
1112
import { idesData } from '@/lib/generated'
@@ -98,20 +99,18 @@ export default function IDEsPageClient({ locale }: Props) {
9899
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
99100
{/* Main Content */}
100101
<main className="w-full">
101-
<div className="mb-[var(--spacing-lg)]">
102-
<div className="flex items-start justify-between mb-[var(--spacing-sm)]">
103-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em]">{t('title')}</h1>
102+
<PageHeader
103+
title={t('title')}
104+
subtitle={t('subtitle')}
105+
action={
104106
<Link
105107
href={`/${locale}/ides/comparison`}
106108
className="text-sm px-[var(--spacing-md)] py-[var(--spacing-xs)] border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors"
107109
>
108110
{t('compareAll')}
109111
</Link>
110-
</div>
111-
<p className="text-base text-[var(--color-text-secondary)] font-light">
112-
{t('subtitle')}
113-
</p>
114-
</div>
112+
}
113+
/>
115114

116115
<StackTabs activeStack="ides" locale={locale} />
117116

src/app/[locale]/manifesto/page.tsx

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { getTranslations } from 'next-intl/server'
22
import Footer from '@/components/Footer'
33
import Header from '@/components/Header'
4+
import PageHeader from '@/components/PageHeader'
45
import { Link } from '@/i18n/navigation'
56
import { getManifestoComponent } from '@/lib/manifesto'
67
import { buildCanonicalUrl, buildOpenGraph, buildTitle, buildTwitterCard } from '@/lib/metadata'
@@ -54,19 +55,7 @@ export default async function ManifestoPage({ params }: Props) {
5455

5556
<div className="max-w-5xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
5657
<main>
57-
{/* Hero Section */}
58-
<section className="mb-[var(--spacing-2xl)]">
59-
<div className="space-y-[var(--spacing-md)]">
60-
<h1 className="text-[2.5rem] md:text-[2rem] font-bold tracking-[-0.03em] leading-[1.15]">
61-
{t('title')}
62-
</h1>
63-
<div className="bg-[var(--color-hover)] p-[var(--spacing-md)]">
64-
<div className="text-[1.5rem] md:text-[1.25rem] tracking-[-0.01em] font-semibold text-[var(--color-text-secondary)]">
65-
{t('slogan')}
66-
</div>
67-
</div>
68-
</div>
69-
</section>
58+
<PageHeader title={t('title')} subtitle={t('slogan')} />
7059

7160
{/* Manifesto Content */}
7261
<section className="prose prose-neutral dark:prose-invert max-w-none mb-[var(--spacing-xl)]">
@@ -81,7 +70,7 @@ export default async function ManifestoPage({ params }: Props) {
8170
>
8271
<div className="flex items-center justify-between">
8372
<div>
84-
<h2 className="text-[1.5rem] font-semibold tracking-[-0.02em] mb-[var(--spacing-xs)]">
73+
<h2 className="text-2xl font-semibold tracking-[-0.02em] mb-[var(--spacing-xs)]">
8574
{tStack('title')}
8675
</h2>
8776
<p className="text-sm text-[var(--color-text-secondary)]">{tStack('subtitle')}</p>

src/app/[locale]/model-providers/page.client.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useMemo, useState } from 'react'
55
import Footer from '@/components/Footer'
66
import Header from '@/components/Header'
77
import StackTabs from '@/components/navigation/StackTabs'
8+
import PageHeader from '@/components/PageHeader'
89
import type { Locale } from '@/i18n/config'
910
import { Link } from '@/i18n/navigation'
1011
import { providersData } from '@/lib/generated'
@@ -67,14 +68,7 @@ export default function ModelProvidersPageClient({ locale }: Props) {
6768
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
6869
{/* Main Content */}
6970
<main className="w-full">
70-
<div className="mb-[var(--spacing-lg)]">
71-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em] mb-[var(--spacing-sm)]">
72-
{t('title')}
73-
</h1>
74-
<p className="text-base text-[var(--color-text-secondary)] font-light">
75-
{t('subtitle')}
76-
</p>
77-
</div>
71+
<PageHeader title={t('title')} subtitle={t('subtitle')} />
7872

7973
<StackTabs activeStack="model-providers" locale={locale} />
8074

src/app/[locale]/models/page.client.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useMemo, useState } from 'react'
55
import Footer from '@/components/Footer'
66
import Header from '@/components/Header'
77
import StackTabs from '@/components/navigation/StackTabs'
8+
import PageHeader from '@/components/PageHeader'
89
import type { Locale } from '@/i18n/config'
910
import { Link } from '@/i18n/navigation'
1011
import { modelsData } from '@/lib/generated'
@@ -62,20 +63,18 @@ export default function ModelsPageClient({ locale }: Props) {
6263
<div className="max-w-8xl mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
6364
{/* Main Content */}
6465
<main className="w-full">
65-
<div className="mb-[var(--spacing-lg)]">
66-
<div className="flex items-start justify-between mb-[var(--spacing-sm)]">
67-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em]">{t('title')}</h1>
66+
<PageHeader
67+
title={t('title')}
68+
subtitle={t('subtitle')}
69+
action={
6870
<Link
6971
href={`/${locale}/models/comparison`}
7072
className="text-sm px-[var(--spacing-md)] py-[var(--spacing-xs)] border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors"
7173
>
7274
{t('compareAll')}
7375
</Link>
74-
</div>
75-
<p className="text-base text-[var(--color-text-secondary)] font-light">
76-
{t('subtitle')}
77-
</p>
78-
</div>
76+
}
77+
/>
7978

8079
<StackTabs activeStack="models" locale={locale} />
8180

0 commit comments

Comments
 (0)