From 634cc090072fbc2f99eaef4a19639c016e3e245c Mon Sep 17 00:00:00 2001
From: Patrick Kollitsch <83281+davidsneighbour@users.noreply.github.com>
Date: Sun, 07 Jun 2026 03:47:14 +0000
Subject: [PATCH] fix: make header height configurable (#972)

---
 layouts/_partials/site-header.html |    6 +
 scripts/test-hugo-quickstart.ts    |  133 ++++++++++++++++++++++++++++++++++++++++++++
 layouts/_partials/page-header.html |    3 
 3 files changed, 139 insertions(+), 3 deletions(-)

diff --git a/layouts/_partials/page-header.html b/layouts/_partials/page-header.html
index 10d290e..393988c 100644
--- a/layouts/_partials/page-header.html
+++ b/layouts/_partials/page-header.html
@@ -4,6 +4,7 @@
   {{/* Trimming the slash and adding absURL make sure the image works no matter where our site lives */}}
   {{ $featured_image_class := .Params.featured_image_class | compare.Default "cover bg-center" }}
   {{ $cover_dimming_class := .Params.cover_dimming_class | compare.Default "bg-black-60" }}
+  {{ $header_section_class := .Param "header_section_class" | compare.Default "tc-l pv6 ph3 ph4-ns" }}
   {{ $responsive_image_widths := .Site.Params.ananke.responsive_image_widths | compare.Default (slice 480 960 1440 1920) }}
   {{ $responsive_header_id := "" }}
   {{ $background_image := $featured_image }}
@@ -31,7 +32,7 @@
   <header {{ with $responsive_header_id }}id="{{ . }}" {{ end }}class="{{ $featured_image_class }}" style="background-image: url('{{ $background_image }}');">
     <div class="{{ $cover_dimming_class }}">
       {{ partials.Include "site-navigation.html" . }}
-      <div class="tc-l pv6 ph3 ph4-ns">
+      <div class="{{ $header_section_class }}">
         {{ if not .Params.omit_header_text }}
           <div class="f2 f1-l fw2 white-90 mb0 lh-title">{{ .Title | compare.Default .Site.Title }}</div>
           {{ with .Params.description  }}
diff --git a/layouts/_partials/site-header.html b/layouts/_partials/site-header.html
index 7a67967..19ba931 100644
--- a/layouts/_partials/site-header.html
+++ b/layouts/_partials/site-header.html
@@ -2,11 +2,12 @@
 {{ if $featured_image }}
   {{/* Trimming the slash and adding absURL make sure the image works no matter where our site lives */}}
   {{ $featured_image_class := site.Params.featured_image_class | compare.Default "cover bg-top" }}
+  {{ $header_section_class := .Param "header_section_class" | compare.Default "tc-l pv4 pv6-l ph3 ph4-ns" }}
   <header class="{{ $featured_image_class }}" style="background-image: url('{{ $featured_image }}');">
     {{ $cover_dimming_class := site.Params.cover_dimming_class | compare.Default "bg-black-60" }}
     <div class="{{ $cover_dimming_class }}">
       {{ partials.Include "site-navigation.html" .}}
-      <div class="tc-l pv4 pv6-l ph3 ph4-ns">
+      <div class="{{ $header_section_class }}">
         <h1 class="f2 f-subheadline-l fw2 white-90 mb0 lh-title">
           {{ .Title | compare.Default .Site.Title }}
         </h1>
@@ -22,7 +23,8 @@
   <header>
     <div class="pb3-m pb6-l {{ .Site.Params.background_color_class | compare.Default "bg-black" }}">
       {{ partials.Include "site-navigation.html" . }}
-      <div class="tc-l pv3 ph3 ph4-ns">
+      {{ $header_section_class := .Param "header_section_class" | compare.Default "tc-l pv3 ph3 ph4-ns" }}
+      <div class="{{ $header_section_class }}">
         <h1 class="f2 f-subheadline-l fw2 light-silver mb0 lh-title">
           {{ .Title | compare.Default .Site.Title }}
         </h1>
diff --git a/scripts/test-hugo-quickstart.ts b/scripts/test-hugo-quickstart.ts
index 0c83ad8..d80d64d 100644
--- a/scripts/test-hugo-quickstart.ts
+++ b/scripts/test-hugo-quickstart.ts
@@ -711,6 +711,104 @@
 }
 
 /**
+ * Sentinel CSS class used to verify the configurable hero header spacing.
+ *
+ * Issue #504: the height of the hero header is controlled by the
+ * `header_section_class` parameter. The value is unique so it can only appear in
+ * the output when the front matter override is honoured.
+ */
+const HEADER_SECTION_CLASS_MARKER = "ananke-header-test-pv7";
+
+/**
+ * Default header section spacing rendered by `page-header.html` for a single
+ * page with a featured image when `header_section_class` is not set.
+ */
+const DEFAULT_PAGE_HEADER_SECTION_CLASS = "tc-l pv6 ph3 ph4-ns";
+
+/**
+ * Create two single pages that exercise the configurable header section class:
+ * one overrides `header_section_class` in front matter, the other relies on the
+ * theme default. Both set `featured_image` so the hero header branch renders.
+ *
+ * @param contentDir Absolute path to the project `content` directory.
+ */
+async function writeHeaderSectionClassFixtures(
+	contentDir: string,
+): Promise<void> {
+	const overridePage = [
+		"+++",
+		"title = 'Custom Header Height'",
+		"featured_image = '/images/custom-hero.jpg'",
+		`header_section_class = '${HEADER_SECTION_CLASS_MARKER} ph3 ph4-ns'`,
+		"+++",
+		"",
+		"Body.",
+		"",
+	].join("\n");
+
+	const defaultPage = [
+		"+++",
+		"title = 'Default Header Height'",
+		"featured_image = '/images/default-hero.jpg'",
+		"+++",
+		"",
+		"Body.",
+		"",
+	].join("\n");
+
+	await writeTextFile(join(contentDir, "custom-header.md"), overridePage);
+	await writeTextFile(join(contentDir, "default-header.md"), defaultPage);
+}
+
+/**
+ * Assert that the configurable `header_section_class` parameter is honoured on
+ * hero headers and that omitting it keeps the historical default spacing.
+ *
+ * @param projectRoot Absolute path to the temporary quickstart project.
+ * @throws Error when the override is dropped, leaks, or the default changes.
+ */
+async function assertHeaderSectionClassConfigurable(
+	projectRoot: string,
+): Promise<void> {
+	const failures: string[] = [];
+
+	const overrideHtml = await readTextFile(
+		join(projectRoot, "public", "custom-header", "index.html"),
+	);
+	const defaultHtml = await readTextFile(
+		join(projectRoot, "public", "default-header", "index.html"),
+	);
+
+	if (!overrideHtml.includes(HEADER_SECTION_CLASS_MARKER)) {
+		failures.push(
+			`- custom 'header_section_class' value '${HEADER_SECTION_CLASS_MARKER}' was not applied to the hero header`,
+		);
+	}
+
+	if (defaultHtml.includes(HEADER_SECTION_CLASS_MARKER)) {
+		failures.push(
+			"- custom 'header_section_class' value leaked onto a page that did not set it",
+		);
+	}
+
+	if (!defaultHtml.includes(DEFAULT_PAGE_HEADER_SECTION_CLASS)) {
+		failures.push(
+			`- default header section spacing '${DEFAULT_PAGE_HEADER_SECTION_CLASS}' was missing when 'header_section_class' was not set`,
+		);
+	}
+
+	if (failures.length > 0) {
+		throw new Error(
+			[
+				"Strict assertion failed: configurable header section class did not behave as expected.",
+				"Failed assertions:",
+				...failures,
+			].join("\n"),
+		);
+	}
+}
+
+/**
  * Determine whether a directory is the work tree of a Git repository.
  *
  * @param path Absolute directory path.
@@ -1103,6 +1201,41 @@
 		await assertDraftHiddenInProduction(projectRoot, homepagePath);
 		console.log("[OK ] Production build should exclude draft content");
 
+		console.log("\n[RUN] Configurable hero header section class (issue #504)");
+		await writeHeaderSectionClassFixtures(join(projectRoot, "content"));
+		const headerSectionBuildStep: StepDefinition = {
+			name: "Build site with configurable header section fixtures",
+			command: "hugo",
+			args: [],
+			cwd: projectRoot,
+			expectedFiles: [
+				"public/custom-header/index.html",
+				"public/default-header/index.html",
+			],
+		};
+		const headerSectionBuildReport = await executeHugoBuildStep(
+			headerSectionBuildStep,
+			projectRoot,
+		);
+		reports.push(headerSectionBuildReport);
+
+		if (options.verbose) {
+			console.log(
+				`      ${formatCommand(headerSectionBuildStep.command, headerSectionBuildStep.args)}`,
+			);
+			console.log(
+				`[OK ] ${headerSectionBuildStep.name} (${headerSectionBuildReport.result.durationMs} ms, exit ${String(headerSectionBuildReport.result.code)})`,
+			);
+
+			const trimmedOutput = headerSectionBuildReport.result.combined.trim();
+			if (trimmedOutput) {
+				console.log(trimmedOutput);
+			}
+		}
+
+		await assertHeaderSectionClassConfigurable(projectRoot);
+		console.log("[OK ] Configurable hero header section class (issue #504)");
+
 		console.log("\nResult: PASS");
 
 		if (options.keepOnSuccess) {

--
Gitblit v1.10.0