From 5deaa22e3895dfe679cb8c56d49f41cad87c2680 Mon Sep 17 00:00:00 2001 From: Vigilio Desto Date: Fri, 10 Apr 2026 18:42:21 +0200 Subject: [PATCH] Port head partials: SEO, OG, JSON-LD, AI disclosure, tag-nav - layouts/partials/head.html: full partial with meta partials + CSS pipeline - layouts/partials/meta/seo.html: canonical URL + robots directives - layouts/partials/meta/og.html: Open Graph + Twitter Card meta tags - layouts/partials/meta/ai-disclosure.html: AI content disclosure (EU AI Act) - layouts/partials/meta/json-ld.html: Schema.org JSON-LD structured data - layouts/partials/tag-nav.html: site-wide tag cloud partial - layouts/_default/baseof.html: simplified to use partial head.html - layouts/_default/single.html: removed inline head block (now in head.html) Verified: canonical, robots, OG tags, ai-content-disclosure on live pages. 96 pages build clean. Closes: asw#15 --- layouts/_default/baseof.html | 7 +- layouts/_default/single.html | 33 -------- layouts/partials/head.html | 26 ++++++ layouts/partials/meta/ai-disclosure.html | 25 ++++++ layouts/partials/meta/json-ld.html | 101 +++++++++++++++++++++++ layouts/partials/meta/og.html | 41 +++++++++ layouts/partials/meta/seo.html | 16 ++++ layouts/partials/tag-nav.html | 21 +++++ 8 files changed, 231 insertions(+), 39 deletions(-) create mode 100644 layouts/partials/head.html create mode 100644 layouts/partials/meta/ai-disclosure.html create mode 100644 layouts/partials/meta/json-ld.html create mode 100644 layouts/partials/meta/og.html create mode 100644 layouts/partials/meta/seo.html create mode 100644 layouts/partials/tag-nav.html diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 40f2da5..a22063b 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -1,12 +1,7 @@ - - - {{ block "title" . }}{{ .Title }} — {{ .Site.Title }}{{ end }} - {{ $css := resources.Get "css/asw-built.css" | minify | fingerprint }} - - {{ block "head" . }}{{ end }} + {{- partial "head.html" . -}} {{ block "main" . }}{{ end }} diff --git a/layouts/_default/single.html b/layouts/_default/single.html index 60f14a3..8cff90b 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -1,36 +1,3 @@ -{{ define "head" }} - - - - - - - - - - - - - - - - - -{{ end }} - {{ define "main" }} {{ partial "nav.html" . }} diff --git a/layouts/partials/head.html b/layouts/partials/head.html new file mode 100644 index 0000000..ee55932 --- /dev/null +++ b/layouts/partials/head.html @@ -0,0 +1,26 @@ + + + + + {{- if .IsHome -}} + {{ .Site.Title }} + {{- else -}} + {{ .Title }} · {{ .Site.Title }} + {{- end -}} + +{{- with .Description }}{{- end }} +{{- if not .Description }}{{- with .Site.Params.description }}{{- end }}{{- end }} + +{{- /* ── Meta partials ─────────────────────────────────────────── */}} +{{- partial "meta/seo.html" . -}} +{{- partial "meta/og.html" . -}} +{{- partial "meta/ai-disclosure.html" . -}} +{{- partial "meta/json-ld.html" . -}} + +{{- /* ── CSS — fingerprinted via Hugo Pipes ─────────────────────── */}} +{{ $css := resources.Get "css/asw-built.css" | minify | fingerprint }} + + +{{- range .AlternativeOutputFormats -}} + +{{- end }} diff --git a/layouts/partials/meta/ai-disclosure.html b/layouts/partials/meta/ai-disclosure.html new file mode 100644 index 0000000..5b1fb7e --- /dev/null +++ b/layouts/partials/meta/ai-disclosure.html @@ -0,0 +1,25 @@ +{{- /* + partials/meta/ai-disclosure.html — AI-generated content disclosure + + Emits the proposed W3C/WHATWG AI content disclosure meta tag and the + corresponding HTTP-equiv header that satisfies EU AI Act Article 50 + obligations for AI-generated/AI-assisted web content. + + The disclosure level is controlled per-page via front matter: + ai_content: "generated" — fully AI-generated content + ai_content: "assisted" — human-written with AI assistance (default) + ai_content: "reviewed" — AI-generated, human-reviewed and edited + ai_content: "none" — no AI involvement (suppresses the tag) + + Site-wide default in hugo.toml: + [params] + ai_content = "assisted" # default for all pages + + Reference: + https://github.com/nickvdyck/ai-content-disclosure + EU AI Act Article 50 (transparency obligations, Chapter IV) +*/ -}} +{{- $level := or .Params.ai_content .Site.Params.ai_content "assisted" -}} +{{- if ne $level "none" -}} + +{{- end }} diff --git a/layouts/partials/meta/json-ld.html b/layouts/partials/meta/json-ld.html new file mode 100644 index 0000000..cb20176 --- /dev/null +++ b/layouts/partials/meta/json-ld.html @@ -0,0 +1,101 @@ +{{- /* + partials/meta/json-ld.html — JSON-LD structured data (Schema.org) + + Emits one " ($data | jsonify) | safeHTML -}} + +{{- else -}} + +{{- /* ── Article / TechArticle / WebPage ─────────────────────────── */}} +{{- $schemaType := "WebPage" -}} +{{- if and .IsPage (not .IsSection) -}} + {{- if eq .Params.type "docs" -}} + {{- $schemaType = "TechArticle" -}} + {{- else if .Date -}} + {{- $schemaType = "Article" -}} + {{- end -}} +{{- end -}} + +{{- $publisher := dict "@type" "Organization" "name" $author -}} +{{- with $logo -}} + {{- $publisher = merge $publisher (dict "logo" (dict "@type" "ImageObject" "url" (. | absURL))) -}} +{{- end -}} + +{{- $data := dict + "@context" "https://schema.org" + "@type" $schemaType + "headline" .Title + "description" $desc + "url" .Permalink + "author" $publisher + "publisher" $publisher +-}} + +{{- with $image -}} + {{- $data = merge $data (dict "image" (. | absURL)) -}} +{{- end -}} + +{{- if and .Date (not .IsSection) -}} + {{- $data = merge $data (dict + "datePublished" (.Date.Format "2006-01-02T15:04:05Z07:00") + "dateModified" (.Lastmod.Format "2006-01-02T15:04:05Z07:00") + ) -}} +{{- end -}} + +{{- /* BreadcrumbList: .Ancestors is nearest→root; iterate by index to reverse */}} +{{- with .Ancestors -}} + {{- $ancs := . -}} + {{- $len := len $ancs -}} + {{- $items := slice -}} + {{- range $i := seq $len -}} + {{- $a := index $ancs (sub $len $i) -}} + {{- $item := dict "@type" "ListItem" "position" $i "name" $a.Title "item" $a.Permalink -}} + {{- $items = $items | append $item -}} + {{- end -}} + {{- $last := dict "@type" "ListItem" "position" (add $len 1) "name" $.Title "item" $.Permalink -}} + {{- $items = $items | append $last -}} + {{- $crumb := dict "@type" "BreadcrumbList" "itemListElement" $items -}} + {{- $data = merge $data (dict "breadcrumb" $crumb) -}} +{{- end -}} + +{{- printf "" ($data | jsonify) | safeHTML -}} +{{- end }} diff --git a/layouts/partials/meta/og.html b/layouts/partials/meta/og.html new file mode 100644 index 0000000..61828a6 --- /dev/null +++ b/layouts/partials/meta/og.html @@ -0,0 +1,41 @@ +{{- /* + partials/meta/og.html — Open Graph + Twitter Card meta tags + + og:title Page title (site title for home page) + og:description .Description → .Site.Params.description → "" + og:url .Permalink + og:type "website" (home) | "article" (everything else) + og:site_name .Site.Title + og:image .Params.image → .Site.Params.og_image → absent + twitter:card "summary_large_image" when image present, else "summary" + twitter:site .Site.Params.twitter (optional @handle) + + Configure in hugo.toml: + [params] + og_image = "/images/og-default.png" # fallback OG image + twitter = "@yourhandle" # omit if not on Twitter/X +*/ -}} +{{- $title := cond .IsHome .Site.Title (printf "%s · %s" .Title .Site.Title) -}} +{{- $desc := or .Description .Site.Params.description "" -}} +{{- $image := or .Params.image .Site.Params.og_image "" -}} +{{- $type := cond .IsHome "website" "article" -}} + + + + + + +{{- with $image }} + +{{- end }} + +{{- /* Twitter / X Card */}} + + + +{{- with $image }} + +{{- end }} +{{- with .Site.Params.twitter }} + +{{- end }} diff --git a/layouts/partials/meta/seo.html b/layouts/partials/meta/seo.html new file mode 100644 index 0000000..6e896c7 --- /dev/null +++ b/layouts/partials/meta/seo.html @@ -0,0 +1,16 @@ +{{- /* + partials/meta/seo.html — canonical URL + robots directives + + Outputs: + Always emitted; uses .Permalink (absolute). + "noindex, nofollow" on draft/future pages, + "index, follow" otherwise. + + No params required. Works from any page context. +*/ -}} + +{{- if or .Draft (gt .Date now) }} + +{{- else }} + +{{- end }} diff --git a/layouts/partials/tag-nav.html b/layouts/partials/tag-nav.html new file mode 100644 index 0000000..bc11797 --- /dev/null +++ b/layouts/partials/tag-nav.html @@ -0,0 +1,21 @@ +{{- /* + tag-nav.html — renders all site tags as a navigable tag cloud. + + Usage in a layout: + {{ partial "tag-nav.html" . }} + + Outputs: