[object Object]

← back to Stars of Design

starsofdesign: BreadcrumbList JSON-LD across all 3 detail page types — /firms/:slug (Home > Firms > {firm}), /designers/:slug (Home > Designers > {name}), /clients/:slug (Home > Clients > {name}). Packaged in @graph alongside entity schema. /firms also now emits employee→Person list for each associated designer.

ed2f2d8b5a115efe4828d86277f61c9c3f461ceb · 2026-05-12 19:29:40 -0700 · Steve Abrams

Files touched

Diff

commit ed2f2d8b5a115efe4828d86277f61c9c3f461ceb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue May 12 19:29:40 2026 -0700

    starsofdesign: BreadcrumbList JSON-LD across all 3 detail page types — /firms/:slug (Home > Firms > {firm}), /designers/:slug (Home > Designers > {name}), /clients/:slug (Home > Clients > {name}). Packaged in @graph alongside entity schema. /firms also now emits employee→Person list for each associated designer.
---
 routes/public.js | 137 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 90 insertions(+), 47 deletions(-)

diff --git a/routes/public.js b/routes/public.js
index 66fefd7..766bad6 100644
--- a/routes/public.js
+++ b/routes/public.js
@@ -108,18 +108,30 @@ router.get('/designers/:slug', (req, res, next) => {
     const designerVideos = data.videosByDesigner(slug);
     const json_ld = {
       '@context': 'https://schema.org',
-      '@type': 'Person',
-      name: d.name,
-      url: `https://starsofdesign.com/designers/${d.slug || slug}`,
-      ...(d.headline ? { description: d.headline } : {}),
-      ...(d.city ? {
-        homeLocation: {
-          '@type': 'Place',
-          name: d.city,
-        }
-      } : {}),
-      ...(d.styles && d.styles.length ? { knowsAbout: d.styles } : {}),
-      jobTitle: 'Interior Designer',
+      '@graph': [
+        {
+          '@type': 'Person',
+          name: d.name,
+          url: `https://starsofdesign.com/designers/${d.slug || slug}`,
+          ...(d.headline ? { description: d.headline } : {}),
+          ...(d.city ? {
+            homeLocation: {
+              '@type': 'Place',
+              name: d.city,
+            }
+          } : {}),
+          ...(d.styles && d.styles.length ? { knowsAbout: d.styles } : {}),
+          jobTitle: 'Interior Designer',
+        },
+        {
+          '@type': 'BreadcrumbList',
+          itemListElement: [
+            { '@type': 'ListItem', position: 1, name: 'Home',      item: 'https://starsofdesign.com/' },
+            { '@type': 'ListItem', position: 2, name: 'Designers', item: 'https://starsofdesign.com/designers' },
+            { '@type': 'ListItem', position: 3, name: d.name,      item: `https://starsofdesign.com/designers/${d.slug || slug}` },
+          ],
+        },
+      ],
     };
     res.render('public/designer', {
       title: `${d.name} — Profile, Signature Style & Wallcovering Pairings | Stars of Design`,
@@ -169,23 +181,35 @@ router.get('/clients/:slug', async (req, res, next) => {
     // only, never street/zip/phone — schema mirrors that exactly.
     const json_ld = {
       '@context': 'https://schema.org',
-      '@type': 'Person',
-      name: c.full_name,
-      url: `https://starsofdesign.com/clients/${c.slug || slug}`,
-      jobTitle: c.role || 'Interior Designer',
-      ...(c.city || c.state ? {
-        homeLocation: {
-          '@type': 'Place',
-          name: [c.city, c.state].filter(Boolean).join(', '),
-        }
-      } : {}),
-      ...(c.firm ? {
-        worksFor: {
-          '@type': 'Organization',
-          name: c.firm.name,
-          ...(c.firm.slug ? { url: `https://starsofdesign.com/firms/${c.firm.slug}` } : {}),
-        }
-      } : {}),
+      '@graph': [
+        {
+          '@type': 'Person',
+          name: c.full_name,
+          url: `https://starsofdesign.com/clients/${c.slug || slug}`,
+          jobTitle: c.role || 'Interior Designer',
+          ...(c.city || c.state ? {
+            homeLocation: {
+              '@type': 'Place',
+              name: [c.city, c.state].filter(Boolean).join(', '),
+            }
+          } : {}),
+          ...(c.firm ? {
+            worksFor: {
+              '@type': 'Organization',
+              name: c.firm.name,
+              ...(c.firm.slug ? { url: `https://starsofdesign.com/firms/${c.firm.slug}` } : {}),
+            }
+          } : {}),
+        },
+        {
+          '@type': 'BreadcrumbList',
+          itemListElement: [
+            { '@type': 'ListItem', position: 1, name: 'Home',    item: 'https://starsofdesign.com/' },
+            { '@type': 'ListItem', position: 2, name: 'Clients', item: 'https://starsofdesign.com/clients' },
+            { '@type': 'ListItem', position: 3, name: c.full_name, item: `https://starsofdesign.com/clients/${c.slug || slug}` },
+          ],
+        },
+      ],
     };
     res.render('public/dwclient', {
       title: `${c.full_name} — DW Client Designer | Stars of Design`,
@@ -226,26 +250,45 @@ router.get('/firms/:slug', async (req, res, next) => {
     }
     const f = await firmsLib.getFirm(slug);
     if (!f) return res.status(404).render('public/404', { title: 'Not found — Stars of Design' });
-    // JSON-LD Organization (design firm) — Schema.org primary entity is
-    // Organization with subtype `Architectural` only when applicable; we
-    // stay generic since the firm may be interiors or architecture or both.
+    // JSON-LD @graph — Organization (the firm itself) + BreadcrumbList
+    // (Home > Firms > {firm}) + ItemList of associated designers, all in
+    // one script tag for cleaner head injection.
     const json_ld = {
       '@context': 'https://schema.org',
-      '@type': 'Organization',
-      name: f.name,
-      url: `https://starsofdesign.com/firms/${f.slug}`,
-      ...(f.website ? { sameAs: [f.website] } : {}),
-      ...(f.founded_year ? { foundingDate: String(f.founded_year) } : {}),
-      ...(f.bio ? { description: f.bio.slice(0, 800) } : {}),
-      ...(f.city || f.state ? {
-        address: {
-          '@type': 'PostalAddress',
-          ...(f.city ? { addressLocality: f.city } : {}),
-          ...(f.state ? { addressRegion: f.state } : {}),
-          ...(f.country ? { addressCountry: f.country } : {}),
-        }
-      } : {}),
-      ...(f.areas && f.areas.length ? { areaServed: f.areas } : {}),
+      '@graph': [
+        {
+          '@type': 'Organization',
+          name: f.name,
+          url: `https://starsofdesign.com/firms/${f.slug}`,
+          ...(f.website ? { sameAs: [f.website] } : {}),
+          ...(f.founded_year ? { foundingDate: String(f.founded_year) } : {}),
+          ...(f.bio ? { description: f.bio.slice(0, 800) } : {}),
+          ...(f.city || f.state ? {
+            address: {
+              '@type': 'PostalAddress',
+              ...(f.city ? { addressLocality: f.city } : {}),
+              ...(f.state ? { addressRegion: f.state } : {}),
+              ...(f.country ? { addressCountry: f.country } : {}),
+            }
+          } : {}),
+          ...(f.areas && f.areas.length ? { areaServed: f.areas } : {}),
+          ...(f.designers && f.designers.length ? {
+            employee: f.designers.map(d => ({
+              '@type': 'Person',
+              name: d.full_name,
+              url: `https://starsofdesign.com/clients/${d.slug}`,
+            })),
+          } : {}),
+        },
+        {
+          '@type': 'BreadcrumbList',
+          itemListElement: [
+            { '@type': 'ListItem', position: 1, name: 'Home',  item: 'https://starsofdesign.com/' },
+            { '@type': 'ListItem', position: 2, name: 'Firms', item: 'https://starsofdesign.com/firms' },
+            { '@type': 'ListItem', position: 3, name: f.name,  item: `https://starsofdesign.com/firms/${f.slug}` },
+          ],
+        },
+      ],
     };
     res.render('public/firm', {
       title: `${f.name} — Design Firm | Stars of Design`,

← 7348d91 starsofdesign: home page — WebSite + Organization JSON-LD wi  ·  back to Stars of Design  ·  starsofdesign: GET /privacy + /terms — minimal legal pages ( 00a2a60 →