feat(seo): enhancements for rich Google and LLM results

Add WebPage/CollectionPage schema, BreadcrumbList, and configurable llms.txt output for better rich results and AI crawling.

New `marketing.seo.llm` params added to `params.yaml` to config llm scraping prefs.
This commit is contained in:
George Cushen committed 2025-11-23 03:42:43 +00:00
1 parent 1bff23752e
commit e9a47baa66
25 files changed
+204 -25

No files matched your search

@@ -39,7 +39,7 @@ sections:
phone: "+1 (555) 123-4567"
social:
- icon: brands/x
url: https://twitter.com/SmithLabResearch
url: https://x.com/SmithLabResearch
- icon: brands/linkedin
url: https://linkedin.com/company/smith-lab
- icon: brands/github
@@ -63,7 +63,7 @@ social:
link: 'mailto:jane@example.edu'
- icon: twitter
icon_pack: fab
link: https://twitter.com/janedoe
link: https://x.com/janedoe
- icon: github
icon_pack: fab
link: https://github.com/janedoe
+1 -1
View File
@@ -4,7 +4,7 @@
links:
- id: x
url: 'https://twitter.com/intent/tweet?url={url}&text={title}'
url: 'https://x.com/intent/tweet?url={url}&text={title}'
title: X
icon: brands/x
enable: true
+12
View File
@@ -78,6 +78,18 @@ outputFormats:
baseName: backlinks
isPlainText: true
notAlternative: true
LLM:
mediaType: text/plain
baseName: llms
isPlainText: true
notAlternative: true
permalinkable: true
outputs:
home:
- HTML
- RSS
- LLM
imaging:
# High quality defaults for crisp avatar display
quality: 90
@@ -0,0 +1,71 @@
{{- $page := .page -}}
{{- $summary := .summary -}}
{{- $language := $page.Language.LanguageCode | default site.LanguageCode | default "en" -}}
{{- $description := $summary | default ($page.Params.summary | default $page.Summary) -}}
{{- $description = $description | plainify -}}
{{- $listed_pages := $page.Pages -}}
{{- if $page.IsHome -}}
{{- $listed_pages = slice -}}
{{- else if $listed_pages -}}
{{- $listed_pages = $listed_pages.ByDate.Reverse -}}
{{- else -}}
{{- $listed_pages = slice -}}
{{- end -}}
{{- $items := slice -}}
{{- $max_items := 10 -}}
{{- range $index, $item := $listed_pages -}}
{{- if ge $index $max_items -}}
{{- break -}}
{{- end -}}
{{- $items = $items | append (dict
"@type" "ListItem"
"position" (add $index 1)
"item" (dict
"@type" "WebPage"
"@id" $item.Permalink
"name" ($item.LinkTitle | default $item.Title | plainify)
"url" $item.Permalink
)
) -}}
{{- end -}}
{{- $item_list := dict
"@type" "ItemList"
"itemListOrder" "Descending"
"numberOfItems" (len $items)
"itemListElement" $items
-}}
{{- $collection := dict
"@context" "https://schema.org"
"@type" "CollectionPage"
"@id" $page.Permalink
"url" $page.Permalink
"name" ($page.LinkTitle | default $page.Title | plainify)
"inLanguage" $language
"isPartOf" (dict
"@type" "WebSite"
"@id" site.BaseURL
"url" site.BaseURL
"name" site.Title
)
-}}
{{- if $description }}
{{- $collection = merge $collection (dict "description" $description) -}}
{{- end -}}
{{- if not $page.Lastmod.IsZero }}
{{- $collection = merge $collection (dict "dateModified" ($page.Lastmod.Format "2006-01-02T15:04:05Z07:00")) -}}
{{- end -}}
{{- if gt (len $items) 0 }}
{{- $collection = merge $collection (dict "mainEntity" $item_list) -}}
{{- end -}}
<script type="application/ld+json">
{{ $collection | jsonify (dict "indent" " ") | safeJS }}
</script>
@@ -30,6 +30,12 @@
{{ partial "jsonld/faqpage.html" (dict "page" $page "summary" $summary) }}
{{ end }}
{{ partial "jsonld/webpage.html" (dict "page" $page "summary" $summary) }}
{{- else -}}
{{ partial "jsonld/collectionpage.html" (dict "page" $page "summary" $summary) }}
{{- end }}
{{ if not $page.IsHome }}
@@ -0,0 +1,56 @@
{{- $page := .page -}}
{{- $summary := .summary -}}
{{- $language := $page.Language.LanguageCode | default site.LanguageCode | default "en" -}}
{{- $description := $summary | default ($page.Params.summary | default $page.Summary) -}}
{{- $description = $description | plainify -}}
{{- $image := partial "functions/get_featured_image" $page -}}
{{- if not $image -}}
{{- $image = partial "functions/get_site_icon" 512 -}}
{{- end -}}
{{- $image_obj := dict -}}
{{- with $image -}}
{{- $image_obj = dict
"@type" "ImageObject"
"url" .Permalink
-}}
{{- with .Width }}{{ $image_obj = merge $image_obj (dict "width" .) }}{{ end -}}
{{- with .Height }}{{ $image_obj = merge $image_obj (dict "height" .) }}{{ end -}}
{{- end -}}
{{- $webpage := dict
"@context" "https://schema.org"
"@type" "WebPage"
"@id" $page.Permalink
"url" $page.Permalink
"name" ($page.LinkTitle | default $page.Title | plainify)
"inLanguage" $language
"isPartOf" (dict
"@type" "WebSite"
"@id" site.BaseURL
"url" site.BaseURL
"name" site.Title
)
-}}
{{- if $description }}
{{- $webpage = merge $webpage (dict "description" $description) -}}
{{- end -}}
{{- if not $page.Date.IsZero }}
{{- $webpage = merge $webpage (dict "datePublished" ($page.Date.Format "2006-01-02T15:04:05Z07:00")) -}}
{{- end -}}
{{- if not $page.Lastmod.IsZero }}
{{- $webpage = merge $webpage (dict "dateModified" ($page.Lastmod.Format "2006-01-02T15:04:05Z07:00")) -}}
{{- end -}}
{{- with $image_obj }}
{{- $webpage = merge $webpage (dict "primaryImageOfPage" .) -}}
{{- end -}}
<script type="application/ld+json">
{{ $webpage | jsonify (dict "indent" " ") | safeJS }}
</script>
@@ -0,0 +1,20 @@
{{- $cfg := site.Params.marketing.seo.llm | default dict -}}
{{- $allows := $cfg.allow | default (slice "/") -}}
{{- $disallows := $cfg.disallow | default slice -}}
# llms.txt generated by Hugo Blox
{{- with $cfg.contact }}
contact: {{ . }}
{{- end }}
{{- with $cfg.policy_url }}
policy: {{ . }}
{{- end }}
{{- with $cfg.note }}
note: {{ . | plainify }}
{{- end }}
{{- range $allows }}
allow: {{ . }}
{{- end }}
{{- range $disallows }}
disallow: {{ . }}
{{- end }}