From de73f81a34d170a43f7121434e98087f5a91ae75 Mon Sep 17 00:00:00 2001 From: Gilbert Sanchez Date: Sat, 20 Jun 2026 17:37:45 -0700 Subject: [PATCH 1/7] docs: design author profiles (ADR 0002 + glossary) Capture the decision to enrich author taxonomy term pages with opt-in profile content files, keeping the display name as the frontmatter key with an optional preferred-name override (X over Y). Co-Authored-By: Claude Opus 4.8 --- CONTEXT.md | 19 ++++++++++++++ docs/adr/0002-author-profiles.md | 45 ++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 docs/adr/0002-author-profiles.md diff --git a/CONTEXT.md b/CONTEXT.md index dfc70edc0..0e7b41eb4 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -13,3 +13,22 @@ and used as a site section, "Module" means specifically these org-stewarded proj — not the generic sense of "any PowerShell module you `Install-Module`", which is the subject of the site as a whole. _Avoid (for this concept)_: project, tool, package + +**Author**: +A person credited as a contributor to site content (articles, podcast episodes) via +the `authors:` frontmatter. Each distinct Author is surfaced as a taxonomy term with +its own profile page, and may optionally describe themselves (avatar, tagline, bio, +links). An Author exists the moment they are credited on a piece of content; the +self-description is optional enrichment, not what makes someone an Author. +_Avoid (for this concept)_: contributor, writer, user, account + +The **author name** stored in `authors:` frontmatter is both the display byline and +the source of the Profile URL slug — it is the stable key. An Author may additionally +set a **preferred name** on their Profile, which overrides only how their name is +*displayed*; it never changes the slug or the byline key. Authors without a Profile +display their author name unchanged. + +**Profile**: +The per-Author page at `/authors//` listing that Author's content and, when +provided, their self-description. Distinct from the **Author list** — the index page +at `/authors/` showing every Author as a card. diff --git a/docs/adr/0002-author-profiles.md b/docs/adr/0002-author-profiles.md new file mode 100644 index 000000000..dabf9c00f --- /dev/null +++ b/docs/adr/0002-author-profiles.md @@ -0,0 +1,45 @@ +# Author profiles: enrich taxonomy term pages, keep the display name as the key + +Authors are a Hugo taxonomy derived from the `authors:` frontmatter on ~1,045 articles +and podcast episodes (see [[Author]] in `CONTEXT.md`). The term pages at `/authors//` +are auto-generated and show only a name and a content list. To let an Author describe +themselves (avatar, tagline, bio, links), we enrich each term page with an **optional** +content file at `content/authors//_index.md`. Profiles are **opt-in via PR** — not +pre-scaffolded — so most Authors have no file and must keep rendering correctly. + +The load-bearing decision is what gets stored in every article's `authors:` frontmatter. +We keep the **display name** there (`authors: [James Petty]`), exactly as today. The name +is both the byline and the source of the Profile slug — the stable key. A Profile may set +a **preferred name** that overrides only how the name is *displayed*; it never changes the +slug or the byline. Authors without a Profile render their author name unchanged. + +## Considered options + +- **Slug as the key** (`authors: [james-petty]`, display name rendered from the Profile) — + rejected. It decouples slug from name, but the display name would then live *only* in the + Profile. With ~70 of 80 Authors un-enriched at launch, their bylines and cards would fall + back to a humanized slug — `darren-mar-elia` → "Darren Mar Elia" (wrong hyphen), + `jasonmorgan` → "Jasonmorgan". It also forces a repo-wide migration of all 1,045 files. +- **Display name as the key, with an optional preferred-name override** — chosen. Zero + migration, correct bylines for every Author for free, and the changeable-display-name + benefit is preserved for those who opt in. The only thing it gives up is a slug decoupled + from the original name — but the slug is a URL we want stable anyway, and renaming it is a + redirect-and-find/replace job in *either* model. + +A central `data/authors.yaml` was also rejected in favor of taxonomy term content files: +term files bind to the existing `/authors//` page automatically and carry a Markdown +bio, which a data file cannot do naturally. + +## Consequences + +- The profile content file's directory **must** equal Hugo's slug of the exact author + string; a mismatch yields an orphan page that enriches nothing. A `new-author.ps1` + scaffolder generates the file with the right slug, and a build-time check flags author + content files whose slug matches no taxonomy term. +- Renaming a slug is the rare, deliberate case: `new-author.ps1` rewrites the term across + content and adds an `aliases` redirect on the Profile to preserve the old URL. +- Avatars resolve `avatar` → `gravatar_hash` → `email` → `identicon` fallback. Raw `email` + is convenient but lands in the public repo; `gravatar_hash` lets the privacy-conscious + avoid that, and an explicit `avatar` image bypasses Gravatar entirely. +- The Author list orders enriched profiles first, then by count, so a partially-filled grid + reads as intentional rather than sparse. From 1f2aa1a3f4fcb7fbe927b270325f767152d9395c Mon Sep 17 00:00:00 2001 From: Gilbert Sanchez Date: Sat, 20 Jun 2026 17:46:03 -0700 Subject: [PATCH 2/7] feat(authors): opt-in author profiles with avatars, taglines, bios, links Authors can now describe themselves via an optional taxonomy term content file at content/authors//_index.md: - Avatar resolution avatar -> gravatar_hash -> email -> identicon fallback (author-avatar.html partial; email hashed at build so a raw email is never required in the repo). - Social links partial (website/github/twitter/mastodon/linkedin/bluesky). - preferred_name overrides display only; the authors: byline stays the key, so un-enriched authors render unchanged (no migration of 1,045 files). - List page: enriched profiles sort first, then by count; cards show avatar + tagline + links; search matches name and tagline. - Profile page: avatar + preferred name + links header + Markdown bio. - new-author.ps1 scaffolds a profile at the correct Hugo slug and supports renames (rewrites the byline across content + adds an aliases redirect). - Build-time warning when a profile's slug matches no author (orphan guard). Updated archetypes/authors.md to the full commented schema. See docs/adr/0002-author-profiles.md. Co-Authored-By: Claude Opus 4.8 --- archetypes/authors.md | 38 ++++- .../layouts/_default/authors.html | 66 ++++++--- .../layouts/partials/author-avatar.html | 27 ++++ .../layouts/partials/author-links.html | 35 +++++ .../layouts/taxonomy/author.html | 33 +++-- tools/new-author.ps1 | 133 ++++++++++++++++++ 6 files changed, 295 insertions(+), 37 deletions(-) create mode 100644 themes/powershell-community/layouts/partials/author-avatar.html create mode 100644 themes/powershell-community/layouts/partials/author-links.html create mode 100644 tools/new-author.ps1 diff --git a/archetypes/authors.md b/archetypes/authors.md index 76764ce08..2ec9ed7e7 100644 --- a/archetypes/authors.md +++ b/archetypes/authors.md @@ -1,8 +1,34 @@ --- -title: '{{ replace .File.ContentBaseName "-" " " | title }}' -description: "" -layout: "authors" -website: "" -twitter: "" -github: "" +# The directory this file lives in MUST equal Hugo's slug of your author name +# exactly as it appears in articles' `authors:` frontmatter +# (lowercase, spaces -> hyphens, punctuation dropped). +# Use `tools/new-author.ps1 "Your Name"` to scaffold this with the correct slug. + +# `title` is required by Hugo. Keep it as your author name (the byline key). +title: "{{ replace .File.ContentBaseName "-" " " | title }}" + +# Optional: override only how your name is *displayed* (byline + slug are unchanged). +# preferred_name: "" + +# One short line shown on your card in the author list. +tagline: "" + +# --- Avatar (first match wins) ------------------------------------------------- +# 1. avatar: path or URL to an image you control (bypasses Gravatar) +# 2. gravatar_hash: MD5 of your lowercased email (keeps your email out of the repo) +# 3. email: plain email; hashed to a Gravatar at build time (stored publicly) +# If none are set, a stable identicon is generated from your name. +# avatar: "" +# gravatar_hash: "" +# email: "" + +# --- Links (full URLs) --------------------------------------------------------- +# website: "" +# github: "" +# twitter: "" +# mastodon: "" +# linkedin: "" +# bluesky: "" --- + + diff --git a/themes/powershell-community/layouts/_default/authors.html b/themes/powershell-community/layouts/_default/authors.html index 0e68c1de2..09d570258 100644 --- a/themes/powershell-community/layouts/_default/authors.html +++ b/themes/powershell-community/layouts/_default/authors.html @@ -18,7 +18,7 @@

{{ .Title }}

- @@ -30,36 +30,58 @@

{{ .Title }}

- {{ $authorTaxonomy := .Site.Taxonomies.authors }} - {{ if $authorTaxonomy }} + {{ $pairs := .Site.Taxonomies.authors.ByCount }} + {{ if $pairs }} + {{/* Enriched profiles (backed by a content file) lead, then bare authors, + preserving ByCount order within each group. */}} + {{ $enriched := slice }} + {{ $bare := slice }} + {{ range $pairs }} + {{ if .Page.File }} + {{ $enriched = $enriched | append . }} + {{ else }} + {{ $bare = $bare | append . }} + {{ end }} + {{ end }} + {{ $ordered := $enriched | append $bare }}
- {{ range $authorTaxonomy.ByCount }} -
-
+ {{ range $ordered }} + {{ $page := .Page }} + {{ $name := $page.Title }} + {{ $display := $name }} + {{ with $page.Params.preferred_name }}{{ $display = . }}{{ end }} + {{ $tagline := $page.Params.tagline }} +
+
-
- -
+ {{ partial "author-avatar.html" (dict "page" $page "name" $name "size" 96 "class" "w-20 h-20 rounded-full object-cover ring-2 ring-blue-100") }}
-

- - {{ .Page.Title }} +

+ + {{ $display }}

+ {{ with $tagline }} +

{{ . }}

+ {{ end }} +

{{ .Count }}
- article{{ if gt .Count 1 }}s{{ end }} published + article{{ if ne .Count 1 }}s{{ end }} published

+ {{ partial "author-links.html" (dict "page" $page "class" "flex justify-center gap-4 text-lg mb-4") }} + - + View Profile

@@ -78,16 +100,16 @@

{{ define "scripts" }}