From d7f0df9d3e9922d6e9bf3d9c896509f284b642dd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2026 01:58:34 +0000 Subject: [PATCH] fix: add Open Graph and Twitter Card metadata to all pages - closes #2698 Phase 1 of fixing missing OG metadata: - Add metadataBase, default openGraph, and twitter to app/layout.tsx so every route inherits OG title, description, image, and Twitter card - Override openGraph/twitter per-page in app/[filename]/page.tsx for both rule pages (type: article) and category pages (type: website) - Import siteDescription, siteTitle, social from site-config for DRY defaults Note: public/og-image.jpg (1200x630) must be added before the OG image renders. See Phase 1 task checklist in #2698. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- app/[filename]/page.tsx | 48 +++++++++++++++++++++++++++++++---------- app/layout.tsx | 27 ++++++++++++++++++++--- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/app/[filename]/page.tsx b/app/[filename]/page.tsx index c114d2347..1df3a03b4 100644 --- a/app/[filename]/page.tsx +++ b/app/[filename]/page.tsx @@ -2,7 +2,7 @@ import React from "react"; import categoryTitleIndex from "@/category-uri-title-map.json"; import { Section } from "@/components/layout/section"; import { extractBodyPreview } from "@/lib/bodyUtils"; -import { siteUrl } from "@/site-config"; +import { siteDescription, siteUrl } from "@/site-config"; import client from "@/tina/__generated__/client"; import { CategoryWithRulesQueryDocument } from "@/tina/__generated__/types"; import ClientFallbackPage from "./ClientFallbackPage"; @@ -338,31 +338,57 @@ export async function generateMetadata({ params }: { params: Promise<{ filename: const category = await getCategoryData(filename); if (category?.data?.category && category.data.category.__typename === "CategoryCategory") { const categoryData = category.data.category as any; + const title = `${categoryData.title} | SSW.Rules`; + const description = categoryData.seoDescription || siteDescription; + const url = `${siteUrl}/${filename}`; const metadata: any = { - title: `${categoryData.title} | SSW.Rules`, + title, + description, alternates: { - canonical: `${siteUrl}/${filename}`, + canonical: url, + }, + openGraph: { + title, + description, + url, + type: "website", + images: [`${siteUrl}/og-image.jpg`], + }, + twitter: { + card: "summary_large_image", + title, + description, }, }; - if (categoryData.seoDescription) { - metadata.description = categoryData.seoDescription; - } - return metadata; } const rule = await getRuleData(filename); if (rule?.data?.rule?.title) { + const title = `${rule.data.rule.title} | SSW.Rules`; + const description = rule.data.rule.seoDescription || extractBodyPreview(rule.data.rule.body) || siteDescription; + const url = `${siteUrl}/${filename}`; const metadata: any = { - title: `${rule.data.rule.title} | SSW.Rules`, + title, + description, alternates: { - canonical: `${siteUrl}/${filename}`, + canonical: url, + }, + openGraph: { + title, + description, + url, + type: "article", + images: [`${siteUrl}/og-image.jpg`], + }, + twitter: { + card: "summary_large_image", + title, + description, }, }; - metadata.description = rule.data.rule.seoDescription || extractBodyPreview(rule.data.rule.body) || undefined; - if (rule.data.rule.isArchived) { metadata.robots = { index: false, follow: true }; } diff --git a/app/layout.tsx b/app/layout.tsx index caa097c9a..fc5aba528 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -8,6 +8,7 @@ import "@/styles.css"; import UserClientProvider from "@/components/auth/UserClientProvider"; import AppInsightsProvider from "@/components/providers/AppInsightsProvider"; import { TailwindIndicator } from "@/components/ui/breakpoint-indicator"; +import { siteDescription, siteTitle, siteUrl, social } from "@/site-config"; const fontSans = FontSans({ subsets: ["latin"], @@ -26,9 +27,29 @@ const lato = Lato({ }); export const metadata: Metadata = { - title: "SSW.Rules | Secret Ingredients to Quality Software (Open Source on GitHub)", - description: - "Secret Ingredients to Quality Software | SSW Rules provides best practices for developing secure, reliable, and efficient .NET, Azure, CRM, Angular, React, Dynamics, and AI applications. Learn more today!", + metadataBase: new URL(siteUrl), + title: `${siteTitle} | Secret Ingredients to Quality Software (Open Source on GitHub)`, + description: siteDescription, + openGraph: { + title: `${siteTitle} | Secret Ingredients to Quality Software (Open Source on GitHub)`, + description: siteDescription, + url: siteUrl, + siteName: siteTitle, + type: "website", + images: [ + { + url: `${siteUrl}/og-image.jpg`, + width: 1200, + height: 630, + alt: "SSW Rules - Secret Ingredients to Quality Software", + }, + ], + }, + twitter: { + card: "summary_large_image", + site: `@${social.twitter}`, + creator: `@${social.twitter}`, + }, }; const jsonLd = [