Skip to content

Commit 5c97ab9

Browse files
committed
feat: add OG images and SEO enhancements for plan pages
- Add default OG image assets (PNG and SVG formats) - Create PlanLayout component with structured data support - Add head slot to BaseLayout for custom meta injections - Implement Schema.org SoftwareApplication and BreadcrumbList markup
1 parent 6fa8d69 commit 5c97ab9

File tree

4 files changed

+207
-0
lines changed

4 files changed

+207
-0
lines changed

public/images/og-default.png

174 KB
Loading

public/images/og-default.svg

Lines changed: 85 additions & 0 deletions
Loading

src/layouts/BaseLayout.astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const canonicalURL = new URL(Astro.url.pathname, site);
3737
<link rel="preconnect" href="https://fonts.googleapis.com" />
3838
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3939
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
40+
41+
<slot name="head" />
4042
</head>
4143
<body class="font-sans antialiased">
4244
<slot />

src/layouts/PlanLayout.astro

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
---
2+
import BaseLayout from './BaseLayout.astro';
3+
import Navbar from '../components/Navbar.astro';
4+
import Footer from '../components/Footer.astro';
5+
6+
interface Props {
7+
title: string;
8+
description?: string;
9+
planName: string;
10+
planSlug: string;
11+
planProvider?: string;
12+
planPrice?: number;
13+
planBadge?: string;
14+
}
15+
16+
const {
17+
title,
18+
description,
19+
planName,
20+
planSlug,
21+
planProvider = '',
22+
planPrice,
23+
planBadge,
24+
} = Astro.props;
25+
26+
const site = 'https://mycodingplan.com';
27+
const canonicalURL = new URL(Astro.url.pathname, site);
28+
29+
// Schema.org SoftwareApplication structured data
30+
const structuredData = {
31+
'@context': 'https://schema.org',
32+
'@type': 'SoftwareApplication',
33+
name: planName,
34+
applicationCategory: 'DeveloperApplication',
35+
operatingSystem: 'Web',
36+
url: canonicalURL.toString(),
37+
...(planProvider && { author: { '@type': 'Organization', name: planProvider } }),
38+
...(planPrice !== undefined && {
39+
offers: {
40+
'@type': 'Offer',
41+
price: planPrice,
42+
priceCurrency: 'USD',
43+
availability: 'https://schema.org/InStock',
44+
},
45+
}),
46+
};
47+
48+
// Breadcrumb structured data
49+
const breadcrumbData = {
50+
'@context': 'https://schema.org',
51+
'@type': 'BreadcrumbList',
52+
itemListElement: [
53+
{
54+
'@type': 'ListItem',
55+
position: 1,
56+
name: 'Home',
57+
item: site,
58+
},
59+
{
60+
'@type': 'ListItem',
61+
position: 2,
62+
name: 'Plans',
63+
item: `${site}/plans`,
64+
},
65+
{
66+
'@type': 'ListItem',
67+
position: 3,
68+
name: planName,
69+
item: canonicalURL.toString(),
70+
},
71+
],
72+
};
73+
---
74+
75+
<BaseLayout title={title} description={description}>
76+
<!-- Structured Data -->
77+
<Fragment slot="head">
78+
<script type="application/ld+json" set:html={JSON.stringify(structuredData)} />
79+
<script type="application/ld+json" set:html={JSON.stringify(breadcrumbData)} />
80+
</Fragment>
81+
82+
<Navbar />
83+
84+
<main class="min-h-screen">
85+
<!-- Breadcrumbs -->
86+
<nav aria-label="Breadcrumb" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
87+
<ol class="breadcrumbs text-sm">
88+
<li>
89+
<a href="/" class="link link-hover text-base-content/60 hover:text-primary transition-colors">
90+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline-block mr-1 -mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
91+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
92+
</svg>
93+
Home
94+
</a>
95+
</li>
96+
<li class="text-base-content/40 mx-2" aria-hidden="true">
97+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline-block" fill="none" viewBox="0 0 24 24" stroke="currentColor">
98+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
99+
</svg>
100+
</li>
101+
<li>
102+
<a href="/plans" class="link link-hover text-base-content/60 hover:text-primary transition-colors">Plans</a>
103+
</li>
104+
<li class="text-base-content/40 mx-2" aria-hidden="true">
105+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline-block" fill="none" viewBox="0 0 24 24" stroke="currentColor">
106+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
107+
</svg>
108+
</li>
109+
<li>
110+
<span class="text-base-content font-medium">{planName}</span>
111+
</li>
112+
</ol>
113+
</nav>
114+
115+
<!-- Page Content -->
116+
<slot />
117+
</main>
118+
119+
<Footer />
120+
</BaseLayout>

0 commit comments

Comments
 (0)