mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-15 07:36:50 +03:00
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:
+37
-25
@@ -17,7 +17,8 @@
|
||||
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Instrument+Sans:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="./css/sub-pages.css">
|
||||
<style>
|
||||
:root {
|
||||
--font-body: 'Instrument Sans', system-ui, sans-serif;
|
||||
@@ -38,19 +39,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; }
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
background: var(--color-paper);
|
||||
color: var(--color-ink);
|
||||
line-height: 1.5;
|
||||
padding: 2rem;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
.cheatsheet-page-header,
|
||||
main,
|
||||
footer {
|
||||
max-width: 900px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
.cheatsheet-page-header {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: 1px solid var(--color-mist);
|
||||
@@ -67,18 +74,6 @@
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: inline-block;
|
||||
margin-top: 0.75rem;
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.category {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
@@ -152,8 +147,11 @@
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { padding: 1rem; max-width: none; }
|
||||
.back-link { display: none; }
|
||||
body { padding: 1rem; }
|
||||
.site-header { display: none; }
|
||||
.cheatsheet-page-header,
|
||||
main,
|
||||
footer { max-width: none; padding-left: 0; padding-right: 0; }
|
||||
footer { display: none; }
|
||||
.command { page-break-inside: avoid; }
|
||||
}
|
||||
@@ -166,12 +164,26 @@
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<body class="sub-page">
|
||||
<!-- site-header v1 -->
|
||||
<header class="site-header" data-site-header>
|
||||
<a href="/" class="site-header-brand" aria-label="Impeccable home">
|
||||
<span class="site-header-brand-name">Impeccable</span>
|
||||
</a>
|
||||
<nav class="site-header-nav" aria-label="Primary">
|
||||
<a href="/" data-nav="home">Home</a>
|
||||
<a href="/skills" data-nav="skills">Skills</a>
|
||||
<a href="/anti-patterns" data-nav="anti-patterns">Anti-Patterns</a>
|
||||
<a href="/tutorials" data-nav="tutorials">Tutorials</a>
|
||||
<a href="/gallery" data-nav="gallery">Gallery</a>
|
||||
<a href="https://github.com/pbakaus/impeccable" target="_blank" rel="noopener" data-nav="github">GitHub</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="cheatsheet-page-header">
|
||||
<h1>Impeccable Commands</h1>
|
||||
<p class="subtitle">Quick reference for all 21 design commands</p>
|
||||
<a href="/" class="back-link">← Back to impeccable.style</a>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<main id="commands-container">
|
||||
<p style="color: var(--color-ash);">Loading commands...</p>
|
||||
|
||||
+16
-31
@@ -16,6 +16,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400;1,600&family=Instrument+Sans:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="./css/sub-pages.css">
|
||||
<style>
|
||||
:root {
|
||||
--font-display: 'Cormorant Garamond', Georgia, serif;
|
||||
@@ -50,28 +51,8 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; }
|
||||
a:hover { color: var(--color-accent-hover); }
|
||||
|
||||
/* Header */
|
||||
.site-header {
|
||||
padding: var(--spacing-sm) var(--spacing-lg);
|
||||
border-bottom: 1px solid var(--color-mist);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.site-header a { text-decoration: none; }
|
||||
.header-brand {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
color: var(--color-ink);
|
||||
}
|
||||
.header-nav { display: flex; gap: var(--spacing-md); font-size: 0.875rem; }
|
||||
.header-nav a { color: var(--color-ash); }
|
||||
.header-nav a:hover { color: var(--color-ink); }
|
||||
.gallery-content a { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; }
|
||||
.gallery-content a:hover { color: var(--color-accent-hover); }
|
||||
|
||||
/* Hero */
|
||||
.hero {
|
||||
@@ -201,19 +182,23 @@
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0 var(--spacing-sm) var(--spacing-lg);
|
||||
}
|
||||
.site-header { padding: var(--spacing-sm); }
|
||||
.header-nav { gap: var(--spacing-sm); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<body class="sub-page gallery-content">
|
||||
|
||||
<header class="site-header">
|
||||
<a href="/" class="header-brand">Impeccable</a>
|
||||
<nav class="header-nav">
|
||||
<a href="/#antidote">Anti-Patterns</a>
|
||||
<a href="/cheatsheet">Cheatsheet</a>
|
||||
<a href="https://github.com/pbakaus/impeccable">GitHub</a>
|
||||
<!-- site-header v1 -->
|
||||
<header class="site-header" data-site-header>
|
||||
<a href="/" class="site-header-brand" aria-label="Impeccable home">
|
||||
<span class="site-header-brand-name">Impeccable</span>
|
||||
</a>
|
||||
<nav class="site-header-nav" aria-label="Primary">
|
||||
<a href="/" data-nav="home">Home</a>
|
||||
<a href="/skills" data-nav="skills">Skills</a>
|
||||
<a href="/anti-patterns" data-nav="anti-patterns">Anti-Patterns</a>
|
||||
<a href="/tutorials" data-nav="tutorials">Tutorials</a>
|
||||
<a href="/gallery" data-nav="gallery" aria-current="page">Gallery</a>
|
||||
<a href="https://github.com/pbakaus/impeccable" target="_blank" rel="noopener" data-nav="github">GitHub</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
+17
-1
@@ -38,7 +38,8 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400&family=Instrument+Sans:wght@400;500;600;700&family=Inter:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="./css/main.css">
|
||||
|
||||
<link rel="stylesheet" href="./css/sub-pages.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- Skip to main content link for keyboard users -->
|
||||
@@ -47,6 +48,21 @@
|
||||
<!-- Grain Overlay -->
|
||||
<div class="grain-overlay" aria-hidden="true"></div>
|
||||
|
||||
<!-- site-header v1 -->
|
||||
<header class="site-header" data-site-header>
|
||||
<a href="/" class="site-header-brand" aria-label="Impeccable home">
|
||||
<span class="site-header-brand-name">Impeccable</span>
|
||||
</a>
|
||||
<nav class="site-header-nav" aria-label="Primary">
|
||||
<a href="/" data-nav="home" aria-current="page">Home</a>
|
||||
<a href="/skills" data-nav="skills">Skills</a>
|
||||
<a href="/anti-patterns" data-nav="anti-patterns">Anti-Patterns</a>
|
||||
<a href="/tutorials" data-nav="tutorials">Tutorials</a>
|
||||
<a href="/gallery" data-nav="gallery">Gallery</a>
|
||||
<a href="https://github.com/pbakaus/impeccable" target="_blank" rel="noopener" data-nav="github">GitHub</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- Sticky Section Nav -->
|
||||
<nav class="section-nav" id="section-nav" aria-label="Page sections">
|
||||
<a href="#foundation" class="section-nav-item" data-section="foundation"><span class="section-nav-num">01</span><span class="section-nav-label">Foundation</span></a>
|
||||
|
||||
+31
-8
@@ -6,17 +6,39 @@
|
||||
<title>Privacy Policy - Impeccable</title>
|
||||
<meta name="robots" content="noindex">
|
||||
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600&family=Instrument+Sans:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="./css/sub-pages.css">
|
||||
<style>
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 680px; margin: 0 auto; padding: 2rem 1.5rem; line-height: 1.6; color: #1a1a1a; }
|
||||
h1 { font-size: 1.8rem; margin-bottom: 0.5rem; }
|
||||
h2 { font-size: 1.2rem; margin-top: 2rem; }
|
||||
.updated { color: #666; font-size: 0.9rem; margin-bottom: 2rem; }
|
||||
a { color: #1a1a1a; }
|
||||
.back { display: inline-block; margin-bottom: 2rem; font-size: 0.9rem; }
|
||||
.privacy-content { max-width: 680px; margin: 0 auto; padding: 2rem 1.5rem 4rem; }
|
||||
.privacy-content h1 { font-family: var(--font-display); font-size: 2.25rem; margin-bottom: 0.5rem; letter-spacing: -0.01em; }
|
||||
.privacy-content h2 { font-size: 1.125rem; margin-top: 2rem; font-weight: 600; }
|
||||
.privacy-content p, .privacy-content ul { color: var(--color-charcoal); margin-top: 0.5rem; }
|
||||
.privacy-content ul { padding-left: 1.25rem; }
|
||||
.privacy-content li { margin-top: 0.25rem; }
|
||||
.privacy-content a { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; }
|
||||
.privacy-content a:hover { color: var(--color-accent-hover); }
|
||||
.privacy-content .updated { color: var(--color-ash); font-size: 0.875rem; margin-bottom: 2rem; font-style: italic; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/" class="back">← Back to impeccable.style</a>
|
||||
<body class="sub-page">
|
||||
<!-- site-header v1 -->
|
||||
<header class="site-header" data-site-header>
|
||||
<a href="/" class="site-header-brand" aria-label="Impeccable home">
|
||||
<span class="site-header-brand-name">Impeccable</span>
|
||||
</a>
|
||||
<nav class="site-header-nav" aria-label="Primary">
|
||||
<a href="/" data-nav="home">Home</a>
|
||||
<a href="/skills" data-nav="skills">Skills</a>
|
||||
<a href="/anti-patterns" data-nav="anti-patterns">Anti-Patterns</a>
|
||||
<a href="/tutorials" data-nav="tutorials">Tutorials</a>
|
||||
<a href="/gallery" data-nav="gallery">Gallery</a>
|
||||
<a href="https://github.com/pbakaus/impeccable" target="_blank" rel="noopener" data-nav="github">GitHub</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="privacy-content">
|
||||
<h1>Privacy Policy</h1>
|
||||
<p class="updated">Last updated: April 6, 2026</p>
|
||||
|
||||
@@ -48,5 +70,6 @@
|
||||
|
||||
<h2>Contact</h2>
|
||||
<p>Questions about this policy? Open an issue on <a href="https://github.com/pbakaus/impeccable">GitHub</a> or reach out to <a href="https://x.com/pbakaus">@pbakaus</a>.</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+39
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { serve, file } from "bun";
|
||||
import homepage from "../public/index.html";
|
||||
import cheatsheet from "../public/cheatsheet.html";
|
||||
import gallery from "../public/gallery.html";
|
||||
import privacy from "../public/privacy.html";
|
||||
import {
|
||||
getSkills,
|
||||
getCommands,
|
||||
@@ -18,6 +19,7 @@ const server = serve({
|
||||
"/": homepage,
|
||||
"/cheatsheet": cheatsheet,
|
||||
"/gallery": gallery,
|
||||
"/privacy": privacy,
|
||||
|
||||
// Static assets - all public subdirectories
|
||||
"/assets/*": async (req) => {
|
||||
|
||||
Reference in New Issue
Block a user