Add shared site header across all pages

Every hand-authored HTML page now carries the same sticky top nav:
Home / Skills / Anti-Patterns / Tutorials / Gallery / GitHub. The three
future sections (skills, anti-patterns, tutorials) will land behind
these links in subsequent commits.

- Add <header class="site-header"> with a <!-- site-header v1 --> marker
  to index, cheatsheet, gallery, privacy
- Each page links public/css/sub-pages.css so it picks up the shared
  header styling (sticky, backdrop blur, accent-underlined current item)
- Gallery: drop the inline .site-header/.header-brand/.header-nav
  definitions that predate the shared component (class names conflicted)
- Cheatsheet: restructure the page title into .cheatsheet-page-header
  so it doesn't collide with the new <header class="site-header">; move
  the page container max-width/padding off <body> onto .cheatsheet-page-header,
  main, and footer so the shared sticky header spans the full viewport
- Privacy: full styling refresh using sub-pages.css tokens + a small
  inline block for the page body typography
- Add a validateSiteHeader step to scripts/build.js that fails the build
  if any of the four pages drops the marker
- Add /privacy route to server/index.js so extensionless URLs work

All four built pages pass the marker check; dev server serves all four
with 200 and the shared header.
This commit is contained in:
Paul Bakaus
2026-04-08 08:09:41 -07:00
parent 54b687d16f
commit c598a63498
6 changed files with 142 additions and 66 deletions
+39 -1
View File
@@ -158,6 +158,40 @@ function validateAntipatternRules(rootDir) {
return errors;
}
/**
* Validate that every hand-authored HTML page carries the shared site header.
* The partial is stamped with `<!-- site-header v1 -->` so drift is loud.
*
* Returns the number of validation errors. Build fails if > 0.
*/
function validateSiteHeader(rootDir) {
const pages = [
'public/index.html',
'public/cheatsheet.html',
'public/gallery.html',
'public/privacy.html',
];
const marker = '<!-- site-header v1 -->';
let errors = 0;
for (const rel of pages) {
const full = path.join(rootDir, rel);
if (!fs.existsSync(full)) {
console.error(`${rel} is missing`);
errors++;
continue;
}
const src = fs.readFileSync(full, 'utf-8');
if (!src.includes(marker)) {
console.error(`${rel} is missing the shared site header marker '${marker}'`);
errors++;
}
}
if (errors === 0) {
console.log(`✓ Validated site header on ${pages.length} hand-authored pages`);
}
return errors;
}
/**
* Copy directory recursively
*/
@@ -478,7 +512,11 @@ async function build() {
// Cross-validate engine rules against impeccable SKILL.md DON'Ts
const validationErrors = validateAntipatternRules(ROOT_DIR);
if (countErrors > 0 || validationErrors > 0) {
// Verify every hand-authored HTML page carries the shared site header
const headerErrors = validateSiteHeader(ROOT_DIR);
if (countErrors > 0 || validationErrors > 0 || headerErrors > 0) {
process.exit(1);
}