name: seo-metadata description: Implement SEO and Open Graph metadata for digital invitations. Ensure attractive social media previews when invitations are shared on WhatsApp, Facebook, and Instagram. domain: growth version: 1.0.0 when_to_use: - Creating or updating invitation pages with share metadata - Reviewing Open Graph, Twitter, or structured page metadata preconditions: - Read AGENTS.md - Read .agent/rules/gatekeeper.md inputs: - Page metadata, invitation routes, assets, and social sharing requirements outputs: - Metadata requirements and implementation guidance for discoverability and sharing related_skills: [] related_docs: - docs/core/project-conventions.md
SEO & Open Graph Metadata
Apply this skill when creating or updating invitation pages to ensure optimal social media previews.
1. Open Graph Tags
The live invitation metadata surface is the shared Layout.astro contract plus the invitation route at src/pages/[eventType]/[slug].astro. Invitation pages must publish title, description, canonical URL, and absolute social-image URLs through those surfaces.
---
// src/layouts/Layout.astro
interface Props {
title: string; // "XV Años de María Elena"
description: string; // "Te invitamos a celebrar • 15 de marzo, 2026 • Salón Los Arcos"
image: string; // Absolute URL to OG image
url: string; // Canonical URL
type?: 'website' | 'event';
}
const { title, description, image, url, type = 'website' } = Astro.props;
const ogImage = new URL(image, Astro.url);
const canonicalUrl = import.meta.env.PROD ? Astro.url : new URL('https://celebra-me.com/');
---
<!-- Open Graph -->
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImage} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:type" content={type} />
<meta property="og:site_name" content="Celebra-me" />
<meta property="og:locale" content="es_MX" />
Content Guidelines
| Tag | Max Length | Example |
|---|---|---|
og:title |
60 chars | "XV Años de María Elena" |
og:description |
155 chars | "15 de marzo, 2026 • Salón Los Arcos, Guadalajara" |
2. Twitter Cards
Fallback for Twitter/X sharing:
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={absoluteImage} />
<meta name="twitter:image:alt" content={`Invitación: ${title}`} />
3. Astro Implementation
Live layout integration:
---
// src/pages/[eventType]/[slug].astro
import Layout from '@/layouts/Layout.astro';
interface Props {
layout: {
title: string;
description: string;
image: string;
className: string;
};
}
const { layout } = Astro.props;
---
<Layout
title={layout.title}
description={layout.description}
image={layout.image}
hideHeader={true}
>
<slot />
</Layout>
4. Image Requirements
| Property | Requirement |
|---|---|
| Dimensions | 1200×630 px (1.91:1 ratio) |
| Format | JPG or PNG, prefer WebP with JPG fallback |
| File size | < 300 KB for fast loading |
| Alt text | Descriptive, include event type |
| Safe zone | Keep text within center 80% |
OG Image Generation
---
// Generate OG-optimized image at build time
import { getImage } from 'astro:assets';
import heroSrc from '../assets/hero.jpg';
const ogImage = await getImage({
src: heroSrc,
width: 1200,
height: 630,
format: 'jpg',
quality: 80,
});
---
<meta property="og:image" content={new URL(ogImage.src, Astro.url)} />
5. Structured Data (JSON-LD)
Add Schema.org Event markup for rich search results:
---
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Event',
name: event.title,
startDate: event.isoDate, // "2026-03-15T18:00:00-06:00"
endDate: event.isoEndDate,
eventStatus: 'https://schema.org/EventScheduled',
eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',
location: {
'@type': 'Place',
name: event.venue.name,
address: {
'@type': 'PostalAddress',
addressLocality: event.venue.city,
addressCountry: 'MX',
},
},
image: absoluteImage,
description: metaDescription,
organizer: {
'@type': 'Person',
name: event.hosts?.[0] ?? event.title,
},
};
---
<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
6. Platform-Specific Tips
- Caches previews aggressively; append
?v=2to image URL after updates - Test with
https://wa.me/?text=URLbefore sharing
- Use Sharing Debugger to clear cache
- Scrape URL after any metadata changes
- Only shows previews in bio links and stories, not DMs
- Ensure image has high contrast for small thumbnails
iMessage
- Uses
og:imagewith proper aspect ratio - Falls back to page screenshot if image fails
7. Anti-patterns
| ❌ Don't | ✅ Do |
|---|---|
| Images < 600px wide | Use 1200×630 minimum |
| Generic descriptions | Include date + venue |
| Relative image URLs | Always use absolute URLs |
| Descriptions > 200 chars | Keep under 155 chars |
Missing og:url |
Always include canonical |
| Same image for all events | Unique hero per invitation |
| Text outside safe zone | Center important content |
8. Verification Checklist
Before deploying any invitation:
-
og:titleis unique and under 60 chars -
og:descriptionincludes date and venue, under 155 chars -
og:imageis 1200×630, absolute URL, < 300 KB -
og:urlmatches canonical URL - Twitter Card tags present
- JSON-LD validates at Schema.org Validator
- Test in Facebook Debugger
- Test WhatsApp preview on mobile
-
<html lang="es-MX">is set - No console errors for missing images