/** * Page template wrapper for generated sub-pages. * * Reads the shared site header partial once and wraps content bodies with * a minimal HTML scaffold that imports tokens.css + sub-pages.css. * * Used by scripts/build-sub-pages.js (wired up in commit 3). */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT_DIR = path.resolve(__dirname, '..', '..'); const HEADER_PARTIAL = path.join(ROOT_DIR, 'content', 'site', 'partials', 'header.html'); let cachedHeader = null; /** * Read the shared site header partial. * Cached after first read. */ export function readHeaderPartial() { if (cachedHeader === null) { cachedHeader = fs.readFileSync(HEADER_PARTIAL, 'utf8').trim(); } return cachedHeader; } /** * Mark a nav item as current by adding aria-current="page" and removing * the default nav href state. Matches on `data-nav="{activeNav}"`. * * @param {string} headerHtml * @param {string} activeNav - one of: home, skills, anti-patterns, tutorials, gallery, github * @returns {string} */ export function applyActiveNav(headerHtml, activeNav) { if (!activeNav) return headerHtml; return headerHtml.replace( new RegExp(`data-nav="${activeNav}"`, 'g'), `data-nav="${activeNav}" aria-current="page"`, ); } /** * Wrap body HTML in a full page shell. * * @param {object} opts * @param {string} opts.title -