diff --git a/.gitignore b/.gitignore index 59a14bf60..49d921109 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ public/docs/ public/anti-patterns/ public/tutorials/ public/visual-mode/ +public/slop/ # Note: harness skill directories (.claude/skills/, .cursor/skills/, etc.) # are intentionally tracked. npx skills reads them from this repo at install diff --git a/content/site/partials/header.html b/content/site/partials/header.html index d28c67058..c2f496acf 100644 --- a/content/site/partials/header.html +++ b/content/site/partials/header.html @@ -9,9 +9,8 @@ Home Designing Docs - Anti-Patterns + Slop Live - Overlay diff --git a/public/css/sub-pages.css b/public/css/sub-pages.css index a4b577f22..0c5874a13 100644 --- a/public/css/sub-pages.css +++ b/public/css/sub-pages.css @@ -904,27 +904,135 @@ main#main { align-items: baseline; justify-content: space-between; gap: var(--spacing-md); - padding-bottom: var(--spacing-sm); + padding-bottom: 10px; border-bottom: 1px solid var(--color-mist); - margin-bottom: var(--spacing-lg); + margin-bottom: var(--spacing-md); } +/* Catalog subsections sit under the "The catalog" h2 on /slop, so they + must read clearly as h3 — smaller, uppercase mono label instead of + display italic, to avoid competing with the parent section heading. */ .anti-patterns-section-title { - font-family: var(--font-display); - font-size: clamp(1.75rem, 3vw, 2.25rem); - font-weight: 500; - font-style: italic; + font-family: var(--font-mono); + font-size: 0.8125rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.12em; color: var(--color-ink); - letter-spacing: -0.01em; } .anti-patterns-section-count { font-family: var(--font-mono); font-size: 0.6875rem; + font-weight: 500; + letter-spacing: 0.08em; + color: var(--color-ash); +} + +/* ============================================ + SLOP PAGE (/slop) + Merger of the former /anti-patterns + /visual-mode. + Reuses .anti-patterns-* for the catalog and .visual-mode-* + for the demo / gallery / methods blocks; the rules below wire + the four numbered top-level sections and the nested sidebar. + ============================================ */ + +.slop-content { width: 100%; } + +.slop-header { + max-width: 720px; +} + +.slop-section { + margin-top: clamp(3rem, 6vw, 4.5rem); + scroll-margin-top: 80px; +} + +.slop-section-heading { + font-family: var(--font-display); + font-size: clamp(2rem, 3.5vw, 2.75rem); + font-weight: 500; + font-style: italic; + color: var(--color-ink); + letter-spacing: -0.015em; + line-height: 1.15; + margin-bottom: var(--spacing-lg); + padding-bottom: var(--spacing-sm); + border-bottom: 1px solid var(--color-mist); + display: flex; + align-items: baseline; + gap: var(--spacing-md); +} + +.slop-section-num { + font-family: var(--font-mono); + font-size: 0.75rem; font-weight: 600; - text-transform: uppercase; + font-style: normal; letter-spacing: 0.12em; color: var(--color-ash); + text-transform: uppercase; + flex-shrink: 0; +} + +.slop-catalog-header { + margin-bottom: var(--spacing-lg); +} + +.slop-catalog-lede { + font-size: 0.9375rem; + line-height: 1.7; + color: var(--color-charcoal); + max-width: 72ch; + margin-top: var(--spacing-sm); +} + +.slop-catalog-lede strong { + color: var(--color-ink); + font-weight: 600; +} + +.slop-catalog-lede a { + color: var(--color-ink); + text-decoration: none; + border-bottom: 1px solid var(--color-accent); + font-family: var(--font-mono); + font-size: 0.875em; + font-weight: 500; +} + +.slop-catalog-lede a:hover { + color: var(--color-accent); +} + +/* Sidebar: nested catalog-section list under "The catalog". */ +.slop-sidebar-sublist { + list-style: none; + margin: 6px 0 4px 0; + padding: 0 0 0 var(--spacing-md); + border-left: 1px solid var(--color-mist); +} + +.slop-sidebar-sublist li { + margin: 0; +} + +.slop-sidebar-sublist a { + display: flex !important; + align-items: baseline; + justify-content: space-between; + gap: var(--spacing-sm); + font-size: 0.8125rem; + color: var(--color-ash); + padding: 4px 0; +} + +.slop-sidebar-sublist a:hover { + color: var(--color-ink); +} + +.slop-sidebar-sublist .anti-patterns-sidebar-count { + font-size: 0.625rem; } .rule-card-grid { @@ -1275,24 +1383,6 @@ main#main { VISUAL MODE PAGE ============================================ */ -.visual-mode-page-body main#main { - /* Reset the docs-layout override; this page uses its own max-width. */ - max-width: none; - margin: 0; - padding: 0; -} - -.visual-mode-page { - max-width: 1100px; - margin: 0 auto; - padding: clamp(2rem, 4vw, 3.5rem) clamp(var(--spacing-md), 4vw, var(--spacing-2xl)) clamp(4rem, 8vw, 6rem); -} - -.visual-mode-page-header { - max-width: 720px; - margin-bottom: clamp(2.5rem, 5vw, 3.5rem); -} - .visual-mode-demo-wrap { margin-bottom: clamp(3rem, 6vw, 4.5rem); } @@ -1305,12 +1395,11 @@ main#main { text-align: center; } -/* Mac-window chrome around the iframe. Scoped to .visual-mode-page so - the sub-page's preview styles don't leak onto the homepage, which - carries its own .visual-mode-preview rules in main.css. The auto - margins below would disable grid-item stretch on the homepage and - collapse the preview to the iframe's 300px intrinsic width. */ -.visual-mode-page .visual-mode-preview { +/* Mac-window chrome around the iframe. Lives inside sub-pages.css, which + is not loaded on the homepage — the homepage has its own copy in + main.css. The max-width + auto margins would disable grid stretch on + the homepage and collapse the preview to the iframe's intrinsic width. */ +.visual-mode-preview { border-radius: 10px; overflow: hidden; border: 1px solid var(--color-mist); @@ -1319,7 +1408,7 @@ main#main { margin: 0 auto; } -.visual-mode-page .visual-mode-preview-header { +.visual-mode-preview-header { display: flex; align-items: center; gap: 6px; @@ -1328,18 +1417,18 @@ main#main { border-bottom: 1px solid var(--color-mist); } -.visual-mode-page .visual-mode-preview-dot { +.visual-mode-preview-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } -.visual-mode-page .visual-mode-preview-dot.red { background: #ff5f56; } -.visual-mode-page .visual-mode-preview-dot.yellow { background: #ffbd2e; } -.visual-mode-page .visual-mode-preview-dot.green { background: #27c93f; } +.visual-mode-preview-dot.red { background: #ff5f56; } +.visual-mode-preview-dot.yellow { background: #ffbd2e; } +.visual-mode-preview-dot.green { background: #27c93f; } -.visual-mode-page .visual-mode-preview-title { +.visual-mode-preview-title { margin-left: auto; font-family: var(--font-mono); font-size: 0.75rem; @@ -1354,21 +1443,8 @@ main#main { background: white; } -/* Three invocation methods. */ -.visual-mode-methods { - margin-bottom: clamp(3rem, 6vw, 4.5rem); -} - -.visual-mode-methods-title { - font-family: var(--font-display); - font-size: clamp(1.75rem, 3vw, 2.25rem); - font-weight: 500; - font-style: italic; - color: var(--color-ink); - letter-spacing: -0.01em; - margin-bottom: var(--spacing-lg); -} - +/* Three invocation methods grid. Section wrapper margins come from + .slop-section; this block only controls the grid of cards. */ .visual-mode-methods-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); @@ -1453,26 +1529,18 @@ main#main { color: var(--color-accent); } -/* Gallery (specimens) section */ +/* Gallery (specimens) section header. Title comes from .slop-section-heading; + this block just scopes the lede + spacing under the heading. */ .visual-mode-gallery-header { margin-bottom: var(--spacing-lg); } -.visual-mode-gallery-title { - font-family: var(--font-display); - font-size: clamp(1.75rem, 3vw, 2.25rem); - font-weight: 500; - font-style: italic; - color: var(--color-ink); - letter-spacing: -0.01em; - margin-bottom: var(--spacing-sm); -} - .visual-mode-gallery-lede { font-size: 0.9375rem; line-height: 1.6; color: var(--color-charcoal); max-width: 60ch; + margin-top: var(--spacing-sm); } /* ============================================ diff --git a/public/index.html b/public/index.html index 86989c01a..6dedd6be4 100644 --- a/public/index.html +++ b/public/index.html @@ -60,9 +60,8 @@ Home Designing Docs - Anti-Patterns + Slop Live - Overlay @@ -338,7 +337,7 @@ Bad Contrast - Browse the full catalog → + Browse the full catalog → @@ -1155,9 +1154,8 @@ diff --git a/public/privacy.html b/public/privacy.html index dd8a16008..21421ef0e 100644 --- a/public/privacy.html +++ b/public/privacy.html @@ -33,8 +33,7 @@ diff --git a/public/sitemap.xml b/public/sitemap.xml index eda7a842d..638e3b3e6 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -22,17 +22,11 @@ 0.9 - https://impeccable.style/anti-patterns - 2026-04-10 + https://impeccable.style/slop + 2026-04-23 weekly 0.9 - - https://impeccable.style/visual-mode - 2026-04-10 - weekly - 0.8 - https://impeccable.style/tutorials 2026-04-10 diff --git a/scripts/build-sub-pages.js b/scripts/build-sub-pages.js index 36ca8a383..b29316d71 100644 --- a/scripts/build-sub-pages.js +++ b/scripts/build-sub-pages.js @@ -1,13 +1,14 @@ /** - * Generate static HTML files for /skills, /anti-patterns, /tutorials. + * Generate static HTML files for /docs, /slop, /tutorials, /live-mode, + * /designing. * * Called from both scripts/build.js (before buildStaticSite) and * server/index.js (at module load), so dev and prod share the same * code path and output shape. * - * Output lives under public/docs/, public/anti-patterns/, - * public/tutorials/, all gitignored. Bun's HTML loader picks them up - * the same way it picks up the hand-authored pages. + * Output lives under public/docs/, public/slop/, public/tutorials/, all + * gitignored. Bun's HTML loader picks them up the same way it picks up + * the hand-authored pages. */ import fs from 'node:fs'; @@ -425,31 +426,42 @@ function groupRulesBySection(rules) { } /** - * Render the anti-patterns sidebar: a table of contents of rule sections - * with per-section rule counts. Every entry anchor-jumps to the section - * in the main column. + * Render the /slop sidebar: a table of contents for the four top-level + * sections (See it / Try it live / The catalog / Run it yourself), with + * the catalog's per-section anchors nested under "The catalog". */ -function renderAntiPatternsSidebar(grouped) { - const entries = grouped.order +function renderSlopSidebar(grouped, gallerySize) { + const catalogEntries = grouped.order .filter((section) => grouped.bySection[section]?.length > 0) .map((section) => { const slug = slugify(section); const count = grouped.bySection[section].length; - return `
  • ${escapeHtml(section)}${count}
  • `; + return `
  • ${escapeHtml(section)}${count}
  • `; }) .join('\n'); + const catalogTotal = grouped.order + .reduce((sum, s) => sum + (grouped.bySection[s]?.length || 0), 0); + return ` -