diff --git a/public/css/sub-pages.css b/public/css/sub-pages.css index b8be102ad..a91019a6e 100644 --- a/public/css/sub-pages.css +++ b/public/css/sub-pages.css @@ -8,6 +8,31 @@ @import "./tokens.css"; +/* ============================================ + SKIP LINK (a11y) + ============================================ */ + +.skip-link { + position: absolute; + top: -100%; + left: 50%; + transform: translateX(-50%); + z-index: 10000; + padding: var(--spacing-sm) var(--spacing-lg); + background: var(--color-ink); + color: var(--color-paper); + font-weight: 600; + text-decoration: none; + border-radius: 0 0 8px 8px; + transition: top 0.2s ease; +} + +.skip-link:focus { + top: 0; + outline: 2px solid var(--color-accent); + outline-offset: 2px; +} + /* ============================================ BASE ============================================ */ @@ -272,86 +297,290 @@ main#main { } /* ============================================ - SKILLS INDEX + DOCS-BROWSER LAYOUT (used by /skills) ============================================ */ -.skills-category { - margin-top: clamp(3rem, 6vw, 4.5rem); +:root { + --site-header-height: 62px; + --skills-sidebar-width: 260px; } -.skills-category-header { - margin-bottom: var(--spacing-lg); - padding-bottom: var(--spacing-sm); - border-bottom: 1px solid var(--color-mist); +.skills-layout-page main#main { + /* Reset: the docs layout provides its own max-width container. */ + max-width: none; + margin: 0; + padding: 0; } -.skills-category-title { - font-family: var(--font-display); - font-size: 1.75rem; - font-weight: 500; - font-style: italic; - color: var(--color-ink); - letter-spacing: -0.01em; - line-height: 1.1; - margin-bottom: 6px; +.skills-layout { + display: grid; + grid-template-columns: var(--skills-sidebar-width) minmax(0, 1fr); + gap: clamp(var(--spacing-lg), 4vw, var(--spacing-2xl)); + max-width: var(--width-max); + margin: 0 auto; + padding: 0 clamp(1.25rem, 3vw, 2.5rem); + align-items: start; } -.skills-category-desc { - font-size: 0.9375rem; +/* ============================================ + SIDEBAR + ============================================ */ + +.skills-sidebar { + position: sticky; + top: var(--site-header-height); + align-self: start; + max-height: calc(100vh - var(--site-header-height)); + overflow-y: auto; + padding: var(--spacing-lg) 0 var(--spacing-2xl); + border-right: 1px solid var(--color-mist); + scrollbar-width: thin; + scrollbar-color: var(--color-mist) transparent; +} + +.skills-sidebar::-webkit-scrollbar { + width: 6px; +} + +.skills-sidebar::-webkit-scrollbar-thumb { + background: var(--color-mist); + border-radius: 3px; +} + +.skills-sidebar-inner { + padding-right: var(--spacing-md); +} + +.skills-sidebar-label { + font-family: var(--font-mono); + font-size: 0.625rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.14em; color: var(--color-ash); - max-width: 56ch; + margin-bottom: var(--spacing-md); + padding-left: var(--spacing-sm); } -.skills-category-list { +.skills-sidebar-group { + margin-bottom: var(--spacing-lg); +} + +.skills-sidebar-group-title { + font-family: var(--font-body); + font-size: 0.6875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--color-ink); + margin-bottom: 6px; + padding: 0 var(--spacing-sm); +} + +.skills-sidebar-list { list-style: none; padding: 0; margin: 0; } -.skills-category-item { - border-bottom: 1px solid var(--color-mist); +.skills-sidebar-list li { + margin: 0; } -.skills-category-item:last-child { - border-bottom: none; +.skills-sidebar-list a { + display: block; + padding: 6px var(--spacing-sm); + font-family: var(--font-mono); + font-size: 0.8125rem; + font-weight: 500; + color: var(--color-charcoal); + text-decoration: none; + border-radius: 4px; + transition: color var(--duration-fast) var(--ease-out), + background var(--duration-fast) var(--ease-out); } -.skills-category-link { - display: grid; - grid-template-columns: 180px 1fr; - gap: var(--spacing-md); - align-items: baseline; - padding: 18px 0; +.skills-sidebar-list a:hover { + color: var(--color-accent); + background: var(--color-cream); +} + +.skills-sidebar-list a[aria-current="page"] { color: var(--color-ink); + font-weight: 600; + background: var(--color-cream); + box-shadow: inset 2px 0 0 var(--color-accent); +} + +/* ============================================ + OVERVIEW MAIN CONTENT + ============================================ */ + +.skills-main { + min-width: 0; + padding: clamp(2rem, 4vw, 3.5rem) 0 clamp(4rem, 8vw, 6rem); +} + +.skills-overview-content { + max-width: 720px; +} + +.skills-overview-header { + margin-bottom: clamp(2.5rem, 5vw, 4rem); +} + +.skills-overview-header .sub-page-lede a { + color: var(--color-ink); + text-decoration: underline; + text-decoration-thickness: 1px; + text-decoration-color: var(--color-accent); + text-underline-offset: 4px; + font-family: var(--font-mono); + font-weight: 500; +} + +.skills-overview-howto { + padding: var(--spacing-lg) var(--spacing-lg); + background: var(--color-cream); + border: 1px solid var(--color-mist); + border-radius: 10px; + margin-bottom: clamp(2.5rem, 5vw, 4rem); +} + +.skills-overview-howto-title { + font-family: var(--font-display); + font-size: 1.25rem; + font-style: italic; + font-weight: 500; + color: var(--color-ink); + margin-bottom: var(--spacing-sm); +} + +.skills-overview-howto p { + font-size: 0.9375rem; + line-height: 1.7; + color: var(--color-charcoal); + max-width: 60ch; +} + +.skills-overview-howto a { + color: var(--color-ink); + font-family: var(--font-mono); + font-size: 0.875em; + font-weight: 500; + text-decoration: none; + border-bottom: 1px solid var(--color-accent); transition: color var(--duration-fast) var(--ease-out); } -.skills-category-link:hover { +.skills-overview-howto a:hover { color: var(--color-accent); } -.skills-category-name { - font-family: var(--font-mono); - font-size: 1rem; - font-weight: 600; +.skills-overview-categories { + display: flex; + flex-direction: column; + gap: clamp(2rem, 4vw, 3rem); +} + +.skills-overview-category { + padding-bottom: clamp(2rem, 4vw, 2.5rem); + border-bottom: 1px solid var(--color-mist); +} + +.skills-overview-category:last-child { + border-bottom: none; +} + +.skills-overview-category-meta { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: var(--spacing-md); + margin-bottom: 6px; +} + +.skills-overview-category-title { + font-family: var(--font-display); + font-size: clamp(1.5rem, 3vw, 2rem); + font-weight: 500; + font-style: italic; color: var(--color-ink); + letter-spacing: -0.01em; } -.skills-category-link:hover .skills-category-name { - color: var(--color-accent); +.skills-overview-category-count { + font-family: var(--font-mono); + font-size: 0.6875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--color-ash); } -.skills-category-desc-text { +.skills-overview-category-desc { font-size: 0.9375rem; - line-height: 1.55; + line-height: 1.7; color: var(--color-charcoal); + max-width: 58ch; + margin-bottom: var(--spacing-md); } -@media (max-width: 640px) { - .skills-category-link { +.skills-overview-chips { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.skills-overview-chip { + display: inline-flex; + align-items: center; + padding: 6px 12px; + font-family: var(--font-mono); + font-size: 0.8125rem; + font-weight: 500; + color: var(--color-ink); + background: var(--color-paper); + border: 1px solid var(--color-mist); + border-radius: 99px; + text-decoration: none; + transition: border-color var(--duration-fast) var(--ease-out), + color var(--duration-fast) var(--ease-out), + background var(--duration-fast) var(--ease-out); +} + +.skills-overview-chip:hover { + color: var(--color-accent); + border-color: var(--color-accent); + background: var(--color-cream); +} + +/* ============================================ + MOBILE: collapse sidebar into inline block + ============================================ */ + +@media (max-width: 920px) { + .skills-layout { grid-template-columns: 1fr; - gap: 4px; - padding: 14px 0; + gap: 0; + } + + .skills-sidebar { + position: static; + max-height: none; + overflow: visible; + border-right: none; + border-bottom: 1px solid var(--color-mist); + padding: var(--spacing-md) 0 var(--spacing-lg); + margin-bottom: var(--spacing-lg); + } + + .skills-sidebar-inner { + padding-right: 0; + } + + .skills-sidebar-group { + margin-bottom: var(--spacing-md); } } diff --git a/scripts/build-sub-pages.js b/scripts/build-sub-pages.js index 9485f1993..aff0a8b1d 100644 --- a/scripts/build-sub-pages.js +++ b/scripts/build-sub-pages.js @@ -108,47 +108,110 @@ ${bodyHtml} } /** - * Render the /skills index page body. + * Render the left sidebar used across the /skills section. + * Shows every skill grouped by category. Pass the current skill id to + * mark it with aria-current="page". */ -function renderSkillsIndex(skillsByCategory) { +function renderSkillsSidebar(skillsByCategory, currentSkillId = null) { let html = ` -
-
-

22 commands

-

Skills

-

One skill (/impeccable) teaches your AI design. Twenty-one commands steer the result. Each one is a small, opinionated tool that knows how to fix one specific thing.

-
+
`; + html += ` + +`; return html; } +/** + * Render the /skills overview main column content (not the sidebar). + * This is the orientation piece — what are skills, how to pick one, + * the six categories explained with inline cross-links to the detail pages. + */ +function renderSkillsOverviewMain(skillsByCategory) { + const totalSkills = Object.values(skillsByCategory).reduce( + (sum, list) => sum + list.length, + 0, + ); + + let categoriesHtml = ''; + for (const category of CATEGORY_ORDER) { + const list = skillsByCategory[category] || []; + if (list.length === 0) continue; + + const skillChips = list + .map( + (s) => + `/${escapeHtml(s.id)}`, + ) + .join(''); + + categoriesHtml += ` +
+
+

${escapeHtml(CATEGORY_LABELS[category])}

+

${list.length} ${list.length === 1 ? 'skill' : 'skills'}

+
+

${escapeHtml(CATEGORY_DESCRIPTIONS[category])}

+
+${skillChips} +
+
+`; + } + + return ` +
+
+

${totalSkills} commands

+

Skills

+

One skill (/impeccable) teaches your AI design. Twenty more commands steer the result. Each one is a small, opinionated tool that knows how to fix one specific thing — pick the one that matches the moment.

+
+ +
+

How to pick one

+

Every skill is named after the intent you bring to it. If you’re reviewing something, reach for /critique or /audit. If you’re improving type, reach for /typeset. If you want a last-mile pass before shipping, reach for /polish. The categories below group them by the job you’re doing.

+
+ +
+${categoriesHtml} +
+
`; +} + +/** + * Wrap sidebar + main content in the docs-browser layout shell. + */ +function wrapInDocsLayout(sidebarHtml, mainHtml) { + return ` +
+ ${sidebarHtml} +
+${mainHtml} +
+
`; +} + /** * Entry point. Generates all sub-page HTML files. * @@ -171,15 +234,18 @@ export async function generateSubPages(rootDir) { const generated = []; - // Skills index + // Skills index — docs-browser layout with sticky sidebar. { + const sidebar = renderSkillsSidebar(data.skillsByCategory, null); + const main = renderSkillsOverviewMain(data.skillsByCategory); const html = renderPage({ title: 'Skills — Impeccable', description: - '22 commands that teach your AI harness how to design. Browse by category: create, evaluate, refine, simplify, harden, system.', - bodyHtml: renderSkillsIndex(data.skillsByCategory), + '21 commands that teach your AI harness how to design. Browse by category: create, evaluate, refine, simplify, harden, system.', + bodyHtml: wrapInDocsLayout(sidebar, main), activeNav: 'skills', canonicalPath: '/skills', + bodyClass: 'sub-page skills-layout-page', }); const out = path.join(outDirs.skills, 'index.html'); fs.writeFileSync(out, html, 'utf-8');