[object Object]

← back to Wallco Ai

wallco.ai · OG/Twitter enrich — og:type=product on /design/:id + image:alt + product:* tags

284e3ddbf2c97206144f28ddb269f88251cd390d · 2026-05-12 06:21:38 -0700 · SteveStudio2

htmlHead() already emitted basic OG + Twitter. Three gaps closed:
1. og:type was hardcoded "website" everywhere → now per-route param.
   /design/:id passes ogType:'product' so Facebook/Pinterest treat shares
   as product pins (richer card layout, "in stock" badge, brand label).
2. og:image:alt + twitter:image:alt missing → now built from hue + category
   + motifs, e.g. "Rose Origin No.72 — rose mixed wallpaper pattern
   featuring floral, peony, rose, leaf" (accessibility + LinkedIn pull).
3. og:image:secure_url + og:locale added (LinkedIn/Slack expects them).
   Facebook product-OG namespace: product:availability="in stock",
   product:retailer_item_id, product:brand, product:category — drops in
   automatically when productOG arg present.

Non-design pages unchanged (default ogType="website", no product:* tags).
Reversible: signature backwards-compatible (all new params default null).

Files touched

Diff

commit 284e3ddbf2c97206144f28ddb269f88251cd390d
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Tue May 12 06:21:38 2026 -0700

    wallco.ai · OG/Twitter enrich — og:type=product on /design/:id + image:alt + product:* tags
    
    htmlHead() already emitted basic OG + Twitter. Three gaps closed:
    1. og:type was hardcoded "website" everywhere → now per-route param.
       /design/:id passes ogType:'product' so Facebook/Pinterest treat shares
       as product pins (richer card layout, "in stock" badge, brand label).
    2. og:image:alt + twitter:image:alt missing → now built from hue + category
       + motifs, e.g. "Rose Origin No.72 — rose mixed wallpaper pattern
       featuring floral, peony, rose, leaf" (accessibility + LinkedIn pull).
    3. og:image:secure_url + og:locale added (LinkedIn/Slack expects them).
       Facebook product-OG namespace: product:availability="in stock",
       product:retailer_item_id, product:brand, product:category — drops in
       automatically when productOG arg present.
    
    Non-design pages unchanged (default ogType="website", no product:* tags).
    Reversible: signature backwards-compatible (all new params default null).
---
 server.js | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/server.js b/server.js
index 68f4d76..6a91e8d 100644
--- a/server.js
+++ b/server.js
@@ -491,9 +491,20 @@ const GTAG = GA4_ID ? `
   gtag('config', '${GA4_ID}');
 </script>` : '';
 
-function htmlHead({ title, description, canonical, ogImage = '' }) {
+function htmlHead({ title, description, canonical, ogImage = '', ogType = 'website', ogImageAlt = '', productOG = null }) {
   const siteUrl = `https://${SITE}`;
   const ogImg = ogImage || `${siteUrl}/og-default.png`;
+  const imgAlt = ogImageAlt || title;
+  // product:* tags add Facebook/Pinterest product-pin metadata when ogType==='product'.
+  // productOG = { availability, price, currency, retailer_item_id } — all optional.
+  const productTags = ogType === 'product' && productOG ? [
+    productOG.availability && `<meta property="product:availability" content="${productOG.availability}">`,
+    productOG.retailer_item_id && `<meta property="product:retailer_item_id" content="${productOG.retailer_item_id}">`,
+    productOG.price && productOG.currency && `<meta property="product:price:amount" content="${productOG.price}">`,
+    productOG.price && productOG.currency && `<meta property="product:price:currency" content="${productOG.currency}">`,
+    productOG.brand && `<meta property="product:brand" content="${productOG.brand}">`,
+    productOG.category && `<meta property="product:category" content="${productOG.category}">`
+  ].filter(Boolean).join('\n') : '';
   return `<!doctype html>
 <html lang="en">
 <head>
@@ -503,17 +514,22 @@ function htmlHead({ title, description, canonical, ogImage = '' }) {
 <meta name="description" content="${description}">
 <link rel="canonical" href="${canonical}">
 <!-- Open Graph -->
-<meta property="og:type" content="website">
+<meta property="og:type" content="${ogType}">
 <meta property="og:site_name" content="wallco.ai">
 <meta property="og:title" content="${title}">
 <meta property="og:description" content="${description}">
 <meta property="og:url" content="${canonical}">
 <meta property="og:image" content="${ogImg}">
+<meta property="og:image:secure_url" content="${ogImg}">
+<meta property="og:image:alt" content="${imgAlt}">
+<meta property="og:locale" content="en_US">
+${productTags}
 <!-- 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="${ogImg}">
+<meta name="twitter:image:alt" content="${imgAlt}">
 ${GTAG}
 <!-- Dark/Light anti-flash + Age-band early apply (must be first JS in <head>) -->
 <script>
@@ -2524,7 +2540,15 @@ app.get('/design/:id', (req, res) => {
     title: `${design.title} — wallco.ai`,
     description: `wallco.ai original ${design.category} wallpaper. Pattern No.${design.id} — hand-curated, unique, never repeated. Free sample available.`,
     canonical: `https://wallco.ai/design/${design.id}`,
-    ogImage: `https://wallco.ai${design.image_url}`
+    ogImage: `https://wallco.ai${design.image_url}`,
+    ogType: 'product',
+    ogImageAlt: `${design.title} — ${_hueName} ${design.category} wallpaper pattern${_motifList ? ' featuring ' + _motifList : ''}`,
+    productOG: {
+      availability: 'in stock',
+      retailer_item_id: design.handle || `wallco-${String(design.id).padStart(4,'0')}`,
+      brand: 'wallco.ai',
+      category: design.category
+    }
   })}
 <body>
 ${htmlHeader('/designs')}

← 149bfe0 wallco.ai · /design/:id JSON-LD — enriched Product schema wi  ·  back to Wallco Ai  ·  security — close path traversal + shell injection in /api/st 70b4570 →