Dev Log
SEO baseline: WestBilt image, default OG, title+description tightening, sitewide JSON-LD
What was done
1. WestBilt image wired in
- New asset
src/assets/community/westbilt.png(filename matches the slug). /community/westbilt/hero: replaced the placeholder fake-CSS-gradient div with an<Image>of the site plan, kept the dark-gradient overlay for white-text readability./community/card: added acardImagesslug→ImageMetadata map; westbilt now renders an<Image>with the status badge sitting on top via z-index. Pattern is extensible — additional community projects just add a key.
2. Default OG image (site-wide)
og-nowords.pngchosen over the worded version: one OG image is used across 160+ pages, so a wordless brand image avoids context mismatch when shared from any page (the page’s own title/description renders alongside the image on every platform).- New asset
src/assets/og-default.png.BaseLayout.astronow imports it viagetImage({ width: 1200, height: 630, format: "jpg" })for max platform compatibility. JPG output for Slack/iMessage/LinkedIn/Facebook/Twitter universal support. - All pages now emit
og:image,og:image:width,og:image:height,og:site_name, andtwitter:image(was 0/160 before this change). - Per-page override still supported via the
ogImageprop on BaseLayout.
3. Title-suffix budget enforcement
- BaseLayout now drops the
— Bailey Engineeringsuffix when appending it would push the visible title over 65 chars (Google’s truncation threshold sits around 60-70 depending on character widths). - Result: 43 over-70-char titles dropped to 13. The remaining 13 are all dev log entries with intrinsically long descriptive titles (deferred to the open BACKLOG
/log/visibility decision — the right fix isnoindex-ing devlogs at production launch, not editing the historical record). - Two FAQ titles also tightened in source for cases where they were long even without the suffix.
4. Meta description tightening (24 pages)
Tightened from 200+ chars (Google truncates around 160) to 150-200 chars on every non-devlog over-length page:
- 13 service pages (commercial-industrial-site-design family + sub-pages, multifamily family + sub-pages, master-planned-communities + large-scale-planning, land-planning, plus the family hubs)
- 6 city pages (Eagle, Garden City, Kuna, Caldwell, Middleton, Parma) — common pattern was tightening “Civil engineering, planning, and entitlement intelligence” → “Civil engineering and entitlement intelligence”
- 2 FAQ deck strings (commercial-industrial-site-design, master-planned-communities)
- 2 dev-iq pages (rs4-nampa, ai-in-land-development)
- Common compression: “Southern Idaho’s Treasure Valley” (32 chars) → “the Treasure Valley” (19 chars), saved consistently across descriptions
44 over-length descriptions dropped to 20, of which all 20 remaining are dev log entries (same noindex deferral as titles).
5. BreadcrumbList JSON-LD on every page with breadcrumbs
src/components/Breadcrumbs.astronow emits aBreadcrumbListschema in addition to its visual breadcrumbs. Synthetic “Home” added as position 1 to satisfy Google’s spec. Final crumb correctly omitsitem(current page).- 158 of 161 pages now ship BreadcrumbList (homepage and 404 don’t have breadcrumbs by design).
6. Service schema auto-emit (49 pages: 24 service + 25 city)
- BaseLayout now auto-detects page type from URL path and emits a
ServiceJSON-LD withprovider: ProfessionalService (Bailey Engineering):/services/<family>/and/services/<family>/<sub>/→ Service withareaServed: { AdministrativeArea, name: "Treasure Valley, Idaho" }/service-areas/<county>/<city>/→ Service withareaServed: { City, name, containedInPlace: county }
- Auto-detection avoids touching every page individually. New service pages get the schema for free.
- Skips: homepage (own ProfessionalService + WebSite),
/services/index, county hub pages, project detail pages (own CreativeWork), all other pages.
Why
Pre-launch SEO baseline. Snapshot before:
- 0/160 pages had og:image (LinkedIn/iMessage/Slack shares were blank)
- 7/160 pages had any JSON-LD schema
- 43 titles over 70 chars (truncated in SERP)
- 43 meta descriptions over 200 chars (truncated below the fold)
Snapshot after:
- 161/161 pages have og:image + twitter:image
- 159/161 pages have JSON-LD (BreadcrumbList + per-page-type schema)
- 13 over-length titles remaining (all devlogs, deferred)
- 20 over-length descriptions remaining (all devlogs, deferred)
Architectural notes
- Auto-detection over per-page schema imports. Considered making each service/city page import a
ServiceSchemacomponent. Rejected because adding ~50 imports across files is brittle (easy to forget on new pages) and inconsistent with the BreadcrumbList path (which lives in the shared Breadcrumbs component). Path-based detection in BaseLayout means the schema is correct by default for any new page in the existing URL structure. - OG image as JPG, not PNG/WebP. PNG → larger file. WebP → not universally supported by older preview crawlers. JPG at 1200×630 is the universal floor and what every platform reliably renders.
- JPG vs WebP for OG specifically: WebP IS supported by major platforms today (Facebook, Twitter, LinkedIn, Slack), but iMessage and some legacy crawlers still prefer JPG for
og:image. JPG is the safer default; pages that want sharp art can override viaogImageprop. - Title budget at 65, not 60 or 70. Google’s truncation cliff varies by pixel width (lowercase letters narrower than uppercase), but 60-70 chars is the common sweet spot. 65 splits the difference: keeps brand suffix on most pages while preventing truncation on the long end.
Files affected
Created
src/assets/community/westbilt.pngsrc/assets/og-default.pngsrc/content/devlog/2026-04-23-seo-baseline-westbilt-og-titles-descriptions-schema.md(this entry)
Modified
src/layouts/BaseLayout.astro— title-budget logic, OG defaults, auto-emitted Service schema for service+city pagessrc/components/Breadcrumbs.astro— BreadcrumbList JSON-LD emissionsrc/pages/community/westbilt.astro— Image import + render, hero CSSsrc/pages/community/index.astro— cardImages map + Image render in card- 13 service-page descriptions tightened
- 6 city-page descriptions tightened
- 2 FAQ markdown decks tightened
- 2 dev-iq page descriptions tightened
- 2 FAQ titles tightened
Verification
- Build verify: 20/20 PASS
- Coverage:
- og:image: 161/161 (was 0)
- JSON-LD: 159/161 (was 7)
- BreadcrumbList: 158
- Service: 49 (24 service + 25 city)
- Title length distribution after fix: 30-60 chars: 123, 61-70: 33, OVER 70: 13 (all devlog)
- Description length distribution after fix: only 20 over 200 (all devlog)
Out of scope / pending
/log/noindex decision — open BACKLOG item. The right cleanup for the remaining 13 over-length titles + 20 over-length descriptions, all on devlog pages.- FAQPage schema — already in BACKLOG as Phase C; would be a third batch of JSON-LD adding
FAQPageschema to the 15 FAQ pages. - OG image dimension — current source PNG isn’t strictly 1.91:1; some platforms may letterbox. Future work: re-export at exactly 1200×630.
- Per-page OG overrides for high-value landing pages (e.g., the with-words OG on the homepage) — easy to add later by passing
ogImageto BaseLayout from those pages. /community/index.astroComing soonternary — never fires today (only project isbuilt: true), but kept for future stub projects.