hugo-theme-devrel

DevRel overlay for the Dream Hugo theme.

This is not a standalone theme. It imports Dream as a Hugo module dependency and overlays layouts for posts, talks, talk templates, map, videos, and about. End users only import devrel — Dream is pulled automatically.

Live demos:

下载主题hugo-theme-devrel-main.zip (访问密码: 3705)

Talks page

TOML
[[module.imports]]
  path = "github.com/dadoonet/hugo-theme-devrel"

Do not set theme = ["devrel", "dream"]. Do not vendor or fork Dream into this repo.

Dream is MIT-licensed (Copyright © 2019 Yue Yang). Attribution is preserved in LICENSE-DREAM and the site footer.

Features

  • Blog posts (content/posts/…)
  • Talks with conference metadata, PDF slides, YouTube, social embeds
  • Talk templates (multilingual abstracts + “Played N times”)
  • Talks map (derived from conference.latitude / longitude)
  • Full-text search via Pagefind Component UI (nav loupe → modal, Cmd/Ctrl+K)
  • Videos listing
  • About page assembled from numbered Markdown sections + data/socials.toml

Screenshots

Captured from the production site david.pilato.fr (no browser chrome). Gallery files follow the Hugo themes spec: images/screenshot.png is 1500×1000 (3:2) and images/tn.png is 900×600 (3:2). README images use absolute raw.githubusercontent.com URLs so they also render on themes.gohugo.io .

Talks hub — /talks

Featured cards for the latest sessions, then a compact archive, video strip, template previews, and a map summary. This is images/screenshot.png.

Talks hub

All talks — /talks/all

The full archive: decade/year jump links with per-year counts, then a card grid (cover, language, slides/video badges, conference, date). Each year also gets its own Leaflet map so you can see where that year happened, not only the global map.

All talks

All talks — per-year map

Videos — /talks/videos

Only talks with a youtube: id. Year navigation (red pills), 16:9 cards with YouTube thumbnails, language flag, event name, and a jump to #video on the talk page.

Videos

Talk templates — /talks/templates and /talks/templates/<slug>

The catalog lists every recurring topic, sorted by last played date, with “Played N times”. Open a template for stats (first/last, video count), EN/FR tabs, Talk vs Raw (CFP paste), and the chronological list of conferences.

Talk templates

One talk template

Search, a talk page, the global map

Search (Ctrl/Cmd+K)Talk page
Search
Talk

Talks map

Also in images/: homepage (home.png), About (about.png), and the dedicated /search page (search-page.png).

Quick start

  1. Initialize your site as a Hugo module (if needed):

    SH
    hugo mod init github.com/you/your-site
  2. Import the theme in hugo.toml:

    TOML
    [module]
      [[module.imports]]
        path = "github.com/dadoonet/hugo-theme-devrel"
  3. Set your identity and optional integrations:

    TOML
    [params]
      author = "Your Name"
      avatar = "/about/you.avif"
      headerTitle = "Your Name"
      motto = "Developer Advocate"
      email = "you@example.org"
      siteStartYear = 2024
    
    [params.talks]
      # Optional remote PDF base (GCS, S3, CDN). Empty = local / page-bundle PDFs.
      pdf_base_url = ""
    
    # Hugo defaults are only tags + categories. Copy this block so series
    # (and cities / languages) generate pages. Series appears in the nav
    # overflow when at least one post sets `series:`; drop "series" from
    # collapseNavItems / reorderNavItems to hide the button.
    [taxonomies]
      category = "categories"
      tag = "tags"
      series = "series"
      city = "cities"
      language = "languages"
    
    # Pagefind search (enabled by default). Build index after hugo:
    #   npx pagefind --site public
    # [params.search]
    #   enabled = true
  4. Search is shipped by the theme (content/search/_index.md). Override that file in your site if you need a custom title or body. Disable with params.search.enabled = false.

  5. Wire Pagefind into your build (site repo, not the theme):

    JSON
    {
      "scripts": {
        "build": "hugo --minify && npx pagefind --site public",
        "index": "npx pagefind --site public"
      },
      "devDependencies": { "pagefind": "^1.5.0" }
    }

    Optional Hugo mount so hugo server can reuse the last index:

    TOML
    [[module.mounts]]
      source = "public/pagefind"
      target = "static/pagefind"
      disableWatch = true
  6. Create content using the conventions below.

Local development against a clone

TOML
# go.mod
replace github.com/dadoonet/hugo-theme-devrel => ../hugo-theme-devrel

Content conventions

Posts

TEXT
content/posts/YYYY-MM-DD-slug/index.md
SH
hugo new posts/YYYY-MM-DD-something-awesome/index.md

Archetypes fill author from site.Params.author. avatar and cover are optional: see inferred fields below. Optional series: groups posts; the post layout lists the other parts and links to /series/<term>/.

Talks

TEXT
content/talks/YYYY/YYYY-MM-DD-event/index.md
content/talks/YYYY/YYYY-MM-DD-event/cover.*   # optional cover image
SH
hugo new talks/YYYY/YYYY-MM-DD-conference-name/index.md

Front matter (essentials):

YAML
title: "Talk Title"
conference:
  name: "Conference Name"
  city: "City"
  country: "Country"
  country_code: "fr"       # ISO or "online"
  url: "https://…"         # optional
  latitude: "48.856614"    # optional — used by the map
  longitude: "2.352222"
  date: YYYY-MM-DD         # event day; falls back to page `date` if omitted
authors:
  - author: "Your Name"            # avatar inferred; set avatar: only to override
date: YYYY-MM-DD                   # announcement / Hugo publish date
talk-lang: en
pdf: "YYYY/YYYY-MM-DD-event.pdf"   # relative to params.talks.pdf_base_url, or site-relative if empty
talk: "Topic Name"                 # groups occurrences + links to template
youtube: "VIDEO_ID"                # optional
links: []                          # optional resources
social: []                         # optional X / Bluesky / LinkedIn post URLs

Drop cover: when the bundle contains cover.*. Drop avatar: when the speaker is params.author (uses params.avatar) or when a file exists at static/speakers/firstname_lastname.{avif,svg,webp,png,jpg,jpeg}. Set those fields only to use a different filename — exampleSite does this on FOSDEM (cover: hero.svg) and for Jordan Blake (avatar: speakers/jordan.svg).

date is when the talk page goes public (Hugo’s publish date). conference.date is when the session happens. Listings, maps, year grouping, the Upcoming table, and the masked permalink all use conference.date. If conference.date is omitted, the theme falls back to date.

Set date to the day you want the announcement live, and conference.date to the event. You do not need --buildFuture for upcoming talks: Hugo generates the page from date, and the theme hides the talk permalink until conference.date. Conference site links stay visible so people can still find the event. Pagefind and the home RSS wait for the event date as well; /talks/index.xml lists upcoming sessions.

--buildFuture remains optional for previewing scheduled posts (a content/posts/ page whose date is still in the future). The theme does not re-filter those posts: without the flag Hugo omits the page; with it (Netlify preview, hugo server) the post is listed and indexed like any other.

social is a list of public post URLs. The theme embeds X, Bluesky, and LinkedIn (see JavaZone in exampleSite).

Talk templates

TEXT
content/talks/templates/<slug>/index.md
YAML
layout: "template"   # required
talk: "Topic Name"   # must match talk: on occurrences
versions:
  - label: "EN"
    flag: "gb"
    title: "…"
    abstract: |
      …
SH
hugo new talks/templates/my-talk/index.md
# then set layout: template and talk: …

Satellite talk pages

Ship these _index.md files (also in exampleSite/):

PathFront matterWhat it shows
content/talks/all/_index.mdlayout: "all"Every talk, grouped by year, plus a map per year
content/talks/map/_index.mdlayout: "map"One global map of all talks with coordinates
content/talks/videos/_index.mdlayout: "videos"Talks that have youtube:, year filters, 16:9 cards
content/talks/templates/_index.mdlayout: "templates"Recurring topics sorted by last played date

About

TEXT
content/about/index.md      # shell page
content/about/10-me.md      # sections sorted by filename
content/about/20-details.md
content/about/you.avif      # avatar
data/socials.toml           # Dream socials format

The About layout lists socials, then each *.md section (except index.md) by name order.

Params reference

ParamRole
params.author / params.avatarDefault speaker identity (archetypes + fallbacks)
params.talks.pdf_base_urlPrefix for talk pdf: paths; empty = local URLs
params.search.enabledPagefind UI (/search + Ctrl/Cmd+K); default true
params.navItems.talks / about_meDream nav entries (defaults provided)
params.collapseNavItemsOverflow menu; default includes series with tags
params.advanced.customCSSIncludes theme css/custom.css by default

Taxonomies (tags, categories, series, cities, languages) must be copied into the site hugo.toml. Hugo’s built-in defaults are only tags and categories; extra taxonomies in the theme module are not applied on their own. series is listed in collapseNavItems by default and the overlay hides that nav entry when no post sets series:.

Dream coupling

Overlays assume Dream’s structure: {{ define "main" }}, dream-grid / daisyUI classes, and Dream partials (paginator.html, socials.html, commentSystemHeads.html, CSS pipeline via assets/css/output.css).

exampleSite

Fictional demo content for Alex Rivera (not a real speaker). Live at devrel.hugo.pilato.fr . For a production site using this theme, see david.pilato.fr .

The example site ships enough pages to exercise every layout:

KindWhat is in exampleSite/
Posts8 page bundles; one dated 2099 (not generated unless –buildFuture); 3-part CFP series
Talks8 sessions; FutureConf announced for 2099 (Upcoming, not indexed); FOSDEM sets cover: hero.svg
Templates3 recurring topics (Search that scales, Observability for humans, Communities that last) with EN/FR copy
Videos2 talks with YouTube ids so /talks/videos/ is not empty
SocialJavaZone lists public X, Bluesky, and LinkedIn URLs for the three embed types
AboutNumbered sections (10-, 20-, 30-) plus data/socials.toml
Co-speakerJ on the Beach; Jordan sets avatar: speakers/jordan.svg (not firstname_lastname)

A GitHub Actions workflow (.github/workflows/pages.yml) builds exampleSite (Hugo + Pagefind) and deploys it to GitHub Pages on every push to main. Pull requests are not built there: Netlify serves the deploy preview (netlify.toml, without --buildFuture). A separate workflow (.github/workflows/test-buildfuture.yml) builds with --buildFuture on PRs so scheduled posts stay visible in preview.

One-time Pages + DNS setup (needed because david.pilato.fr is already the custom domain of the user site dadoonet.github.io, which would otherwise redirect project URLs to a 404):

  1. DNS: CNAME devrel.hugo.pilato.fr → dadoonet.github.io
  2. Repo Settings → Pages → Source = GitHub Actions
  3. Repo Settings → Pages → Custom domain = devrel.hugo.pilato.fr, then enable Enforce HTTPS
  4. Confirm exampleSite/static/CNAME contains devrel.hugo.pilato.fr (shipped in this repo)

One-time Netlify setup (this is the only exampleSite build on PRs; same pattern as david.pilato.fr ):

  1. In Netlify: Add new project → Import an existing project → GitHub → dadoonet/hugo-theme-devrel
  2. Leave build settings to netlify.toml (command, publish directory, env)
  3. Deploy. The first production build is skipped on purpose (ignore = "exit 0" on main and branch deploys). After that, each PR gets a preview URL from the Netlify GitHub App

Keeping CI tools up to date

WhatHow
GitHub ActionsDependabot (.github/dependabot.yml), weekly
pagefind (npm)Dependabot on exampleSite/, weekly
Go modules (Dream, …)Dependabot on / and exampleSite/, weekly
Hugo / Go / Node pins.github/versions.env and netlify.toml via update-tool-versions, weekly PR

Dependabot cannot rewrite arbitrary HUGO_VERSION= strings in workflows; those pins are centralized in versions.env (and copied to netlify.toml) by the scheduled workflow above.

SH
cd exampleSite
hugo mod tidy
hugo --minify
npx --yes pagefind --site public
bash scripts/assert-svg-utf8.sh content
bash scripts/assert-svg-utf8.sh public
bash scripts/assert-pagefind-skips-future.sh public
hugo --minify --buildFuture --destination public-buildFuture
npx --yes pagefind --site public-buildFuture
bash scripts/assert-svg-utf8.sh public-buildFuture
bash scripts/assert-pagefind-skips-future.sh public-buildFuture --buildFuture
hugo server

Use a replace in exampleSite/go.mod pointing at the parent theme while developing.

Pagefind indexes pages marked with data-pagefind-body (posts, talks, talk templates, about). Upcoming talks (event date still in the future) omit that attribute in production. Scheduled posts follow Hugo: without --buildFuture the page is not generated; with it, the post is listed and indexed. Talks no longer need --buildFuture: set date to the announcement and conference.date to the event so Hugo generates the page while listings mask the permalink until the session. --buildFuture remains optional for previewing scheduled posts. The same event-date filter is used on the homepage RSS (upcoming talks omitted there); /talks/index.xml still lists upcoming sessions. Talk templates and about pages are always indexed. Filters: section:posts, section:talks, section:templates, section:videos, section:about. Cover images (front matter, cover.*, or YouTube thumbnail) are exposed as result images. Indexed pages emit data-pagefind-sort="date:YYYY-MM-DD" (talks use the event date); empty queries (browse / filter alone) sort by date descending, while non-empty queries keep Pagefind relevance scoring. The nav shows a search icon that opens a centered modal; the section filter appears on the same row as the query once you type. Contextual presets apply on /posts* and /talks* (including templates/videos). A basic /search page remains; a richer dedicated search UI may come later.

License

MIT — see LICENSE. Includes Dream under MIT — see LICENSE-DREAM.