From d85efab51f23aae991d0222225008e9b78074bab Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Tue, 7 Apr 2026 18:37:08 -0700 Subject: [PATCH] Refine homepage layout: install, Stay updated, Changelog/FAQ, Visual Mode - Restructure install section into a split primary card with the recommended path on the left and other install methods on the right - Drop the prefix toggle UI; bundle name is now baked into data-bundle - Balance the Stay updated step as paired Substack iframe + X follow card with matching height, border, and treatment - Pair Changelog and FAQ side-by-side on desktop with a centered divider and shared border-top; section nav highlights both when in view - Tone down the Visual Mode demo by removing side-tab borders, stacked card icons, and tiny body text so the overlay reads cleanly - Refine the Chrome extension callout: drop the pill badge, use an editorial eyebrow above the title, enlarge the thumbnail - Exclude private evals/ directory from version control --- .gitignore | 3 + .../visual-mode-demo.html | 45 +- public/app.js | 23 +- public/css/main.css | 529 +++++++++++------- public/css/styles.css | 469 ++++++++++------ public/css/workflow.css | 28 + public/index.html | 230 ++++---- public/js/components/section-nav.js | 16 +- 8 files changed, 824 insertions(+), 519 deletions(-) diff --git a/.gitignore b/.gitignore index 536039899..66b81a81e 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ extension/detector/ # User design context .impeccable.md + +# Evals (private, commercial) +evals/ diff --git a/public/antipattern-examples/visual-mode-demo.html b/public/antipattern-examples/visual-mode-demo.html index 6834bac1d..b9561496a 100644 --- a/public/antipattern-examples/visual-mode-demo.html +++ b/public/antipattern-examples/visual-mode-demo.html @@ -118,39 +118,32 @@ background: rgba(255,255,255,0.06); border-radius: 12px; padding: 20px 16px; - border-left: 4px solid #8b5cf6; backdrop-filter: blur(8px); } - .card-icon { - width: 32px; - height: 32px; - border-radius: 8px; - background: linear-gradient(135deg, #8b5cf6, #a855f7); - margin-bottom: 12px; + .card-heading { display: flex; align-items: center; - justify-content: center; - font-size: 14px; + gap: 10px; + margin-bottom: 8px; + } + + .card-icon { + font-size: 18px; + line-height: 1; } .card h3 { font-size: 14px; font-weight: 700; - margin-bottom: 6px; } .card p { - font-size: 11px; - color: rgba(255,255,255,0.5); + font-size: 13px; + color: rgba(255,255,255,0.55); line-height: 1.5; } - .card:nth-child(2) { border-left-color: #ec4899; } - .card:nth-child(2) .card-icon { background: linear-gradient(135deg, #ec4899, #f472b6); } - .card:nth-child(3) { border-left-color: #06b6d4; } - .card:nth-child(3) .card-icon { background: linear-gradient(135deg, #06b6d4, #22d3ee); } - /* Stats */ .stats { display: flex; @@ -195,18 +188,24 @@
-
-

Fast Performance

+
+ +

Fast Performance

+

Blazing fast inference with our optimized pipeline.

-
🔒
-

Secure & Safe

+
+ 🔒 +

Secure & Safe

+

Enterprise-grade security with encrypted data.

-
📈
-

Analytics

+
+ 📈 +

Analytics

+

Powerful insights to help you make better decisions.

diff --git a/public/app.js b/public/app.js index 4d71387e3..280189f5b 100644 --- a/public/app.js +++ b/public/app.js @@ -169,28 +169,13 @@ function renderPatternsWithTabs(patterns, antipatterns) { // EVENT HANDLERS // ============================================ -// Sync prefix radio buttons to hidden checkbox + update download button label -document.querySelectorAll('input[name="prefix-choice"]').forEach((radio) => { - radio.addEventListener('change', () => { - const prefixToggle = document.getElementById('prefix-toggle'); - if (prefixToggle) prefixToggle.checked = radio.value === 'prefixed'; - const btnLabel = document.querySelector('#download-zip-btn span'); - if (btnLabel) { - btnLabel.textContent = radio.value === 'prefixed' - ? 'Download prefixed zip' - : 'Download universal zip'; - } - }); -}); - -// Handle bundle download clicks via event delegation +// Handle bundle download clicks via event delegation. +// Each download button carries the full bundle name in data-bundle (e.g. +// "universal" or "universal-prefixed") so the handler is just a redirect. document.addEventListener("click", (e) => { const bundleBtn = e.target.closest("[data-bundle]"); if (bundleBtn) { - const provider = bundleBtn.dataset.bundle; - const prefixToggle = document.getElementById('prefix-toggle'); - const usePrefixed = prefixToggle && prefixToggle.checked; - const bundleName = usePrefixed ? `${provider}-prefixed` : provider; + const bundleName = bundleBtn.dataset.bundle; window.location.href = `/api/download/bundle/${bundleName}`; } diff --git a/public/css/main.css b/public/css/main.css index 2709df9a9..77903781c 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -2633,12 +2633,14 @@ code { gap: var(--spacing-md); text-decoration: none; color: inherit; + padding: var(--spacing-sm) 0; + border-top: 1px solid var(--color-mist); } .detection-callout-image { display: block; - width: 88px; - height: 88px; + width: 132px; + height: 112px; flex-shrink: 0; object-fit: cover; object-position: top left; @@ -2657,20 +2659,29 @@ code { flex-direction: column; align-items: flex-start; justify-content: center; - gap: 8px; + gap: 6px; } -.detection-badge { +.detection-eyebrow { font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600; text-transform: uppercase; - letter-spacing: 0.05em; + letter-spacing: 0.08em; + color: var(--color-accent); +} + +.detection-callout-title { + font-family: var(--font-display); + font-size: 1.25rem; + font-weight: 600; + color: var(--color-ink); + letter-spacing: -0.01em; + line-height: 1.15; +} + +.detection-callout:hover .detection-callout-title { color: var(--color-accent); - border: 1px solid var(--color-accent); - padding: 3px 10px; - border-radius: 99px; - flex-shrink: 0; } .detection-desc { @@ -2820,12 +2831,118 @@ code { } /* Two-path install layout */ -.install-paths { +/* Row 1: full-width primary install card with internal 2-column split */ +.install-row-primary { display: grid; - grid-template-columns: 5fr 3fr 3fr; - gap: 0 var(--spacing-xl); + grid-template-columns: 1.1fr 0.9fr; + gap: var(--spacing-xl); align-items: start; margin: 0 0 var(--spacing-xl); + background: white; + border-radius: 16px; + padding: var(--spacing-lg); + min-width: 0; +} + +.install-primary-main { + display: flex; + flex-direction: column; + min-width: 0; +} + +.install-primary-alts { + display: flex; + flex-direction: column; + gap: var(--spacing-lg); + min-width: 0; + padding-left: var(--spacing-xl); + border-left: 1px solid var(--color-mist); +} + +.install-alts-label { + display: block; + font-family: var(--font-mono); + font-size: 0.625rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--color-ash); +} + +.install-alt-sublabel { + font-family: var(--font-body); + font-size: 0.75rem; + font-weight: 400; + color: var(--color-ash); + text-transform: none; + letter-spacing: 0; + margin-left: 4px; +} + +/* Compact zip download links — replaces the old big black download button. + Two equal-weight options stacked vertically, each is its own affordance. */ +.install-zip-links { + display: flex; + flex-direction: column; + gap: 6px; + margin-top: var(--spacing-xs); +} + +.install-zip-link { + display: flex; + align-items: center; + gap: var(--spacing-sm); + padding: 8px 12px; + font-family: var(--font-body); + font-size: 0.8125rem; + color: var(--color-charcoal); + background: transparent; + border: 1px solid var(--color-mist); + border-radius: 6px; + cursor: pointer; + text-align: left; + transition: border-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out); +} + +.install-zip-link:hover { + border-color: var(--color-accent); + color: var(--color-accent); +} + +.install-zip-link svg { + flex-shrink: 0; + color: var(--color-ash); +} + +.install-zip-link:hover svg { + color: var(--color-accent); +} + +.install-zip-link-name { + font-weight: 600; + color: var(--color-ink); + white-space: nowrap; +} + +.install-zip-link:hover .install-zip-link-name { + color: var(--color-accent); +} + +.install-zip-link-examples { + display: flex; + flex-wrap: nowrap; + gap: 4px; + margin-left: auto; +} + +.install-alt-method .install-zip-link-examples code { + font-family: var(--font-mono); + font-size: 0.6875rem; + background: var(--color-accent-dim); + color: var(--color-accent); + padding: 2px 6px; + border-radius: 3px; + white-space: nowrap; } .install-path { @@ -2840,28 +2957,82 @@ code { padding: var(--spacing-lg); } -.install-path-label { - font-family: var(--font-mono); - font-size: 0.625rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.14em; - color: var(--color-accent); +/* Steps 2-4: vertical accordion. Native
+ gives mutual exclusion (opening one closes the others). Items are + separated by hairlines, not card chrome. */ +.install-accordion { + display: flex; + flex-direction: column; + margin: 0 0 var(--spacing-xl); } -.install-path:not(.install-path-primary) { - padding-top: var(--spacing-lg); +.install-step { + border-top: 1px solid var(--color-mist); } -.install-path-secondary .install-path-label { +.install-step:last-child { + border-bottom: 1px solid var(--color-mist); +} + +.install-step-summary { + display: flex; + align-items: center; + gap: var(--spacing-md); + padding: var(--spacing-md) var(--spacing-xs); + cursor: pointer; + list-style: none; + transition: background var(--duration-fast) var(--ease-out); +} + +.install-step-summary::-webkit-details-marker { + display: none; +} + +.install-step-summary:hover { + background: var(--color-cream); +} + +.install-step-summary h3 { + flex: 1; + margin: 0; +} + +.install-step-arrow { + flex-shrink: 0; color: var(--color-ash); + transition: transform var(--duration-base) var(--ease-out); +} + +.install-step[open] .install-step-arrow { + transform: rotate(90deg); +} + +.install-step-body { + padding: 0 var(--spacing-xs) var(--spacing-md); + display: flex; + flex-direction: column; +} + +.install-step-body .install-path-desc { + margin-top: 0; +} + +.install-step-status { + font-size: 0.8125rem; + color: var(--color-ash); + margin: 0 0 var(--spacing-sm); + font-style: italic; } .install-path-title { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 10px; font-family: var(--font-display); font-size: 1.5rem; font-weight: 600; - margin: var(--spacing-xs) 0 var(--spacing-sm); + margin: 0 0 var(--spacing-sm); line-height: 1.2; } @@ -2985,6 +3156,66 @@ code { text-decoration: underline; } +/* Lightweight terminal block — used in row 2 (CLI) where the heavy + skeuomorphic glass-terminal would be visually too much. Just a thin + bordered code line with a copy button and an optional note below. */ +.install-cmd-block { + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + margin-bottom: var(--spacing-sm); +} + +.install-cmd-line { + display: flex; + align-items: center; + gap: var(--spacing-sm); + padding: 10px 14px; + background: var(--color-cream); + border: 1px solid var(--color-mist); + border-radius: 6px; + min-width: 0; +} + +.install-cmd-line .terminal-prompt { + flex-shrink: 0; + color: var(--color-accent); + font-family: var(--font-mono); + font-size: 0.8125rem; +} + +.install-cmd-line code { + flex: 1; + font-family: var(--font-mono); + font-size: 0.8125rem; + color: var(--color-ink); + background: transparent; + padding: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.install-cmd-line .copy-btn { + flex-shrink: 0; +} + +.install-cmd-note { + font-size: 0.75rem; + color: var(--color-ash); + line-height: 1.5; +} + +.install-cmd-note code { + font-family: var(--font-mono); + font-size: 0.6875rem; + background: var(--color-mist); + padding: 2px 5px; + border-radius: 3px; + color: var(--color-ink); +} + + /* Better together callout */ .install-together { max-width: 960px; @@ -3021,47 +3252,90 @@ code { margin: 0; } -/* Inline step numbers in path labels */ +/* Inline step numbers — sit to the left of the title text inside each h3. + The h3 uses flex with align-items: center so the step is centered with + the text without needing vertical-align fiddling. Margin/gap is handled + by the parent flex's gap. */ .install-path-step { display: inline-flex; align-items: center; justify-content: center; - width: 18px; - height: 18px; + width: 22px; + height: 22px; border-radius: 50%; - border: 1.5px solid currentColor; - font-size: 0.5rem; + border: 1.5px solid var(--color-accent); + color: var(--color-accent); + font-family: var(--font-mono); + font-size: 0.625rem; font-weight: 700; line-height: 1; - margin-right: 4px; - vertical-align: 1px; + flex-shrink: 0; } /* Stay updated (third column) */ .install-updated-subscribe { - display: flex; - flex-direction: column; + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--spacing-sm); margin-bottom: var(--spacing-md); + align-items: stretch; } -.install-updated-subscribe iframe { +.install-updated-substack { display: block; - max-width: 100%; + width: 100%; + min-width: 0; + height: 130px; + border: 1px solid var(--color-mist); + border-radius: 8px; + background: var(--color-paper); } .install-updated-x { - display: inline-flex; - align-items: center; - gap: 6px; - font-size: 0.8125rem; - font-weight: 600; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: space-between; + gap: var(--spacing-xs); + height: 130px; + padding: var(--spacing-sm) var(--spacing-md); + border: 1px solid var(--color-mist); + border-radius: 8px; + background: var(--color-paper); color: var(--color-ink); text-decoration: none; - transition: color var(--duration-fast) var(--ease-out); + transition: border-color var(--duration-fast) var(--ease-out), + background var(--duration-fast) var(--ease-out), + transform var(--duration-fast) var(--ease-out); } .install-updated-x:hover { + border-color: var(--color-ink); + background: var(--color-cream); +} + +.install-updated-x-icon { + color: var(--color-ink); + flex-shrink: 0; +} + +.install-updated-x-label { + font-size: 0.6875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--color-ash); + font-family: var(--font-mono); +} + +.install-updated-x-handle { + font-size: 1rem; + font-weight: 600; + color: var(--color-ink); + letter-spacing: -0.01em; +} + +.install-updated-x:hover .install-updated-x-handle { color: var(--color-accent); } @@ -3123,12 +3397,19 @@ a.install-updated-ref:hover { } @media (max-width: 900px) { - .install-paths { + .install-row-primary { grid-template-columns: 1fr; - gap: var(--spacing-xl); + gap: var(--spacing-lg); max-width: calc(100vw - var(--spacing-md) * 2); } + .install-primary-alts { + padding-left: 0; + padding-top: var(--spacing-lg); + border-left: none; + border-top: 1px solid var(--color-mist); + } + .install-path-primary { margin-bottom: var(--spacing-sm); padding: var(--spacing-md); @@ -3149,10 +3430,27 @@ a.install-updated-ref:hover { gap: var(--spacing-xs); } - .install-updated-subscribe iframe { + .install-updated-subscribe { + grid-template-columns: 1fr; + } + + .install-updated-substack { height: 130px; } + .install-updated-x { + height: auto; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: var(--spacing-sm); + padding: var(--spacing-sm) var(--spacing-md); + } + + .install-updated-x-label { + display: none; + } + .install-terminal-cmd code { font-size: 0.75rem; white-space: normal; @@ -3420,142 +3718,7 @@ a.install-updated-ref:hover { color: var(--color-ink); } -.install-alt-download { - display: flex; - align-items: center; - gap: var(--spacing-sm); -} - -.install-alt-divider { - height: 1px; - background: var(--color-mist); -} - -.install-prefix-options { - display: flex; - align-items: flex-start; - gap: var(--spacing-sm); - margin-top: var(--spacing-md); -} - -.install-prefix-option { - flex: 1; - display: flex; - align-items: flex-start; - gap: 8px; - padding: 10px 12px; - border: 1.5px solid var(--color-mist); - border-radius: 8px; - cursor: pointer; - transition: all var(--duration-fast) var(--ease-out); - line-height: 1; -} - -.install-prefix-option:hover { - border-color: var(--color-charcoal); -} - -.install-prefix-option:has(input:checked) { - border-color: var(--color-accent); -} - -.install-prefix-option input { - appearance: none; - -webkit-appearance: none; - width: 16px; - height: 16px; - border: 1.5px solid var(--color-mist); - border-radius: 50%; - flex-shrink: 0; - margin: 0; - transition: all var(--duration-fast) var(--ease-out); - position: relative; -} - -.install-prefix-option input:checked { - border-color: var(--color-accent); - background: var(--color-accent); -} - -.install-prefix-option input:checked::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 6px; - height: 6px; - background: var(--color-paper); - border-radius: 50%; -} - -.install-prefix-option input:focus-visible { - outline: 2px solid var(--color-accent); - outline-offset: 2px; -} - -.install-prefix-option-content { - display: flex; - flex-direction: column; - gap: 3px; - padding-top: 1px; -} - -.install-prefix-option-label { - font-size: 0.8125rem; - font-weight: 600; - color: var(--color-ink); - line-height: 1; -} - -.install-prefix-option-examples { - display: flex; - flex-wrap: wrap; - gap: 4px; -} - -.install-prefix-option-examples code { - font-family: var(--font-mono); - font-size: 0.6875rem; - background: var(--color-accent-dim); - color: var(--color-accent); - padding: 2px 6px; - border-radius: 3px; -} - -.install-prefix-option-wrap { - flex: 1; - display: flex; - flex-direction: column; - gap: 4px; - align-self: flex-start; -} - -.install-prefix-reason { - font-size: 0.6875rem; - color: var(--color-ash); - line-height: 1.4; - padding-left: 2px; -} - -.install-prefix-option-label code { - font-family: var(--font-mono); - font-size: 0.6875rem; - background: var(--color-accent-dim); - color: var(--color-accent); - padding: 1px 5px; - border-radius: 3px; -} - -.install-alt-method .install-prefix-options { - margin-top: var(--spacing-sm); -} - -.install-alt-method .install-alt-download { - margin-top: var(--spacing-sm); -} - -/* Hidden prefix checkbox for download logic */ +/* Visually hidden helper (kept for accessibility patterns elsewhere) */ .sr-only { position: absolute; width: 1px; @@ -3569,14 +3732,6 @@ a.install-updated-ref:hover { } @media (max-width: 600px) { - .install-prefix-options { - flex-direction: column; - } - - .install-alt-method { - padding: var(--spacing-sm) var(--spacing-md); - } - .install-alt-method code { font-size: 0.75rem; } diff --git a/public/css/styles.css b/public/css/styles.css index 885af675b..8aee30fb1 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -1005,6 +1005,25 @@ color: var(--color-accent); border-bottom-color: var(--color-accent); } +.changelog-faq-row { + position: relative; +} +@media (min-width: 1024px) { + .changelog-faq-row { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + column-gap: calc(var(--spacing-xl) * 1.5); + border-top: 1px solid var(--color-mist); + } + .changelog-faq-row > .changelog-section, .changelog-faq-row > .faq-section { + border-top: none; + } + .changelog-faq-row > .faq-section { + border-left: 1px solid var(--color-mist); + padding-left: calc(var(--spacing-xl) * 0.75); + margin-left: calc(var(--spacing-xl) * -0.75); + } +} .changelog-section { position: relative; padding: var(--spacing-xl) 0; @@ -4715,11 +4734,13 @@ code { gap: var(--spacing-md); text-decoration: none; color: inherit; + padding: var(--spacing-sm) 0; + border-top: 1px solid var(--color-mist); } .detection-callout-image { display: block; - width: 88px; - height: 88px; + width: 132px; + height: 112px; flex-shrink: 0; object-fit: cover; object-position: top left; @@ -4736,19 +4757,26 @@ code { flex-direction: column; align-items: flex-start; justify-content: center; - gap: 8px; + gap: 6px; } -.detection-badge { +.detection-eyebrow { font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600; text-transform: uppercase; - letter-spacing: 0.05em; + letter-spacing: 0.08em; + color: var(--color-accent); +} +.detection-callout-title { + font-family: var(--font-display); + font-size: 1.25rem; + font-weight: 600; + color: var(--color-ink); + letter-spacing: -0.01em; + line-height: 1.15; +} +.detection-callout:hover .detection-callout-title { color: var(--color-accent); - border: 1px solid var(--color-accent); - padding: 3px 10px; - border-radius: 99px; - flex-shrink: 0; } .detection-desc { flex: 1; @@ -4864,12 +4892,102 @@ code { .platforms-section .section-subtitle { max-width: 60ch; } -.install-paths { +.install-row-primary { display: grid; - grid-template-columns: 5fr 3fr 3fr; - gap: 0 var(--spacing-xl); + grid-template-columns: 1.1fr 0.9fr; + gap: var(--spacing-xl); align-items: start; margin: 0 0 var(--spacing-xl); + background: white; + border-radius: 16px; + padding: var(--spacing-lg); + min-width: 0; +} +.install-primary-main { + display: flex; + flex-direction: column; + min-width: 0; +} +.install-primary-alts { + display: flex; + flex-direction: column; + gap: var(--spacing-lg); + min-width: 0; + padding-left: var(--spacing-xl); + border-left: 1px solid var(--color-mist); +} +.install-alts-label { + display: block; + font-family: var(--font-mono); + font-size: 0.625rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--color-ash); +} +.install-alt-sublabel { + font-family: var(--font-body); + font-size: 0.75rem; + font-weight: 400; + color: var(--color-ash); + text-transform: none; + letter-spacing: 0; + margin-left: 4px; +} +.install-zip-links { + display: flex; + flex-direction: column; + gap: 6px; + margin-top: var(--spacing-xs); +} +.install-zip-link { + display: flex; + align-items: center; + gap: var(--spacing-sm); + padding: 8px 12px; + font-family: var(--font-body); + font-size: 0.8125rem; + color: var(--color-charcoal); + background: transparent; + border: 1px solid var(--color-mist); + border-radius: 6px; + cursor: pointer; + text-align: left; + transition: border-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out); +} +.install-zip-link:hover { + border-color: var(--color-accent); + color: var(--color-accent); +} +.install-zip-link svg { + flex-shrink: 0; + color: var(--color-ash); +} +.install-zip-link:hover svg { + color: var(--color-accent); +} +.install-zip-link-name { + font-weight: 600; + color: var(--color-ink); + white-space: nowrap; +} +.install-zip-link:hover .install-zip-link-name { + color: var(--color-accent); +} +.install-zip-link-examples { + display: flex; + flex-wrap: nowrap; + gap: 4px; + margin-left: auto; +} +.install-alt-method .install-zip-link-examples code { + font-family: var(--font-mono); + font-size: 0.6875rem; + background: var(--color-accent-dim); + color: var(--color-accent); + padding: 2px 6px; + border-radius: 3px; + white-space: nowrap; } .install-path { display: flex; @@ -4881,25 +4999,67 @@ code { border-radius: 16px; padding: var(--spacing-lg); } -.install-path-label { - font-family: var(--font-mono); - font-size: 0.625rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.14em; - color: var(--color-accent); +.install-accordion { + display: flex; + flex-direction: column; + margin: 0 0 var(--spacing-xl); } -.install-path:not(.install-path-primary) { - padding-top: var(--spacing-lg); +.install-step { + border-top: 1px solid var(--color-mist); } -.install-path-secondary .install-path-label { +.install-step:last-child { + border-bottom: 1px solid var(--color-mist); +} +.install-step-summary { + display: flex; + align-items: center; + gap: var(--spacing-md); + padding: var(--spacing-md) var(--spacing-xs); + cursor: pointer; + list-style: none; + transition: background var(--duration-fast) var(--ease-out); +} +.install-step-summary::-webkit-details-marker { + display: none; +} +.install-step-summary:hover { + background: var(--color-cream); +} +.install-step-summary h3 { + flex: 1; + margin: 0; +} +.install-step-arrow { + flex-shrink: 0; color: var(--color-ash); + transition: transform var(--duration-base) var(--ease-out); +} +.install-step[open] .install-step-arrow { + transform: rotate(90deg); +} +.install-step-body { + padding: 0 var(--spacing-xs) var(--spacing-md); + display: flex; + flex-direction: column; +} +.install-step-body .install-path-desc { + margin-top: 0; +} +.install-step-status { + font-size: 0.8125rem; + color: var(--color-ash); + margin: 0 0 var(--spacing-sm); + font-style: italic; } .install-path-title { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 10px; font-family: var(--font-display); font-size: 1.5rem; font-weight: 600; - margin: var(--spacing-xs) 0 var(--spacing-sm); + margin: 0 0 var(--spacing-sm); line-height: 1.2; } .install-path-badge { @@ -5000,6 +5160,55 @@ code { .install-path-link a:hover { text-decoration: underline; } +.install-cmd-block { + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + margin-bottom: var(--spacing-sm); +} +.install-cmd-line { + display: flex; + align-items: center; + gap: var(--spacing-sm); + padding: 10px 14px; + background: var(--color-cream); + border: 1px solid var(--color-mist); + border-radius: 6px; + min-width: 0; +} +.install-cmd-line .terminal-prompt { + flex-shrink: 0; + color: var(--color-accent); + font-family: var(--font-mono); + font-size: 0.8125rem; +} +.install-cmd-line code { + flex: 1; + font-family: var(--font-mono); + font-size: 0.8125rem; + color: var(--color-ink); + background: transparent; + padding: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.install-cmd-line .copy-btn { + flex-shrink: 0; +} +.install-cmd-note { + font-size: 0.75rem; + color: var(--color-ash); + line-height: 1.5; +} +.install-cmd-note code { + font-family: var(--font-mono); + font-size: 0.6875rem; + background: var(--color-mist); + padding: 2px 5px; + border-radius: 3px; + color: var(--color-ink); +} .install-together { max-width: 960px; margin: 0 0 var(--spacing-xl); @@ -5035,37 +5244,71 @@ code { display: inline-flex; align-items: center; justify-content: center; - width: 18px; - height: 18px; + width: 22px; + height: 22px; border-radius: 50%; - border: 1.5px solid currentColor; - font-size: 0.5rem; + border: 1.5px solid var(--color-accent); + color: var(--color-accent); + font-family: var(--font-mono); + font-size: 0.625rem; font-weight: 700; line-height: 1; - margin-right: 4px; - vertical-align: 1px; + flex-shrink: 0; } .install-updated-subscribe { - display: flex; - flex-direction: column; + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--spacing-sm); margin-bottom: var(--spacing-md); + align-items: stretch; } -.install-updated-subscribe iframe { +.install-updated-substack { display: block; - max-width: 100%; + width: 100%; + min-width: 0; + height: 130px; + border: 1px solid var(--color-mist); + border-radius: 8px; + background: var(--color-paper); } .install-updated-x { - display: inline-flex; - align-items: center; - gap: 6px; - font-size: 0.8125rem; - font-weight: 600; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: space-between; + gap: var(--spacing-xs); + height: 130px; + padding: var(--spacing-sm) var(--spacing-md); + border: 1px solid var(--color-mist); + border-radius: 8px; + background: var(--color-paper); color: var(--color-ink); text-decoration: none; - transition: color var(--duration-fast) var(--ease-out); + transition: border-color var(--duration-fast) var(--ease-out), background var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-out); } .install-updated-x:hover { + border-color: var(--color-ink); + background: var(--color-cream); +} +.install-updated-x-icon { + color: var(--color-ink); + flex-shrink: 0; +} +.install-updated-x-label { + font-size: 0.6875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--color-ash); + font-family: var(--font-mono); +} +.install-updated-x-handle { + font-size: 1rem; + font-weight: 600; + color: var(--color-ink); + letter-spacing: -0.01em; +} +.install-updated-x:hover .install-updated-x-handle { color: var(--color-accent); } .install-updated-refs { @@ -5119,11 +5362,17 @@ a.install-updated-ref:hover { color: var(--color-ink); } @media (max-width: 900px) { - .install-paths { + .install-row-primary { grid-template-columns: 1fr; - gap: var(--spacing-xl); + gap: var(--spacing-lg); max-width: calc(100vw - var(--spacing-md) * 2); } + .install-primary-alts { + padding-left: 0; + padding-top: var(--spacing-lg); + border-left: none; + border-top: 1px solid var(--color-mist); + } .install-path-primary { margin-bottom: var(--spacing-sm); padding: var(--spacing-md); @@ -5140,9 +5389,23 @@ a.install-updated-ref:hover { align-items: flex-start; gap: var(--spacing-xs); } - .install-updated-subscribe iframe { + .install-updated-subscribe { + grid-template-columns: 1fr; + } + .install-updated-substack { height: 130px; } + .install-updated-x { + height: auto; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: var(--spacing-sm); + padding: var(--spacing-sm) var(--spacing-md); + } + .install-updated-x-label { + display: none; + } .install-terminal-cmd code { font-size: 0.75rem; white-space: normal; @@ -5371,122 +5634,6 @@ a.install-updated-ref:hover { border-radius: 3px; color: var(--color-ink); } -.install-alt-download { - display: flex; - align-items: center; - gap: var(--spacing-sm); -} -.install-alt-divider { - height: 1px; - background: var(--color-mist); -} -.install-prefix-options { - display: flex; - align-items: flex-start; - gap: var(--spacing-sm); - margin-top: var(--spacing-md); -} -.install-prefix-option { - flex: 1; - display: flex; - align-items: flex-start; - gap: 8px; - padding: 10px 12px; - border: 1.5px solid var(--color-mist); - border-radius: 8px; - cursor: pointer; - transition: all var(--duration-fast) var(--ease-out); - line-height: 1; -} -.install-prefix-option:hover { - border-color: var(--color-charcoal); -} -.install-prefix-option:has(input:checked) { - border-color: var(--color-accent); -} -.install-prefix-option input { - appearance: none; - -webkit-appearance: none; - width: 16px; - height: 16px; - border: 1.5px solid var(--color-mist); - border-radius: 50%; - flex-shrink: 0; - margin: 0; - transition: all var(--duration-fast) var(--ease-out); - position: relative; -} -.install-prefix-option input:checked { - border-color: var(--color-accent); - background: var(--color-accent); -} -.install-prefix-option input:checked::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 6px; - height: 6px; - background: var(--color-paper); - border-radius: 50%; -} -.install-prefix-option input:focus-visible { - outline: 2px solid var(--color-accent); - outline-offset: 2px; -} -.install-prefix-option-content { - display: flex; - flex-direction: column; - gap: 3px; - padding-top: 1px; -} -.install-prefix-option-label { - font-size: 0.8125rem; - font-weight: 600; - color: var(--color-ink); - line-height: 1; -} -.install-prefix-option-examples { - display: flex; - flex-wrap: wrap; - gap: 4px; -} -.install-prefix-option-examples code { - font-family: var(--font-mono); - font-size: 0.6875rem; - background: var(--color-accent-dim); - color: var(--color-accent); - padding: 2px 6px; - border-radius: 3px; -} -.install-prefix-option-wrap { - flex: 1; - display: flex; - flex-direction: column; - gap: 4px; - align-self: flex-start; -} -.install-prefix-reason { - font-size: 0.6875rem; - color: var(--color-ash); - line-height: 1.4; - padding-left: 2px; -} -.install-prefix-option-label code { - font-family: var(--font-mono); - font-size: 0.6875rem; - background: var(--color-accent-dim); - color: var(--color-accent); - padding: 1px 5px; - border-radius: 3px; -} -.install-alt-method .install-prefix-options { - margin-top: var(--spacing-sm); -} -.install-alt-method .install-alt-download { - margin-top: var(--spacing-sm); -} .sr-only { position: absolute; width: 1px; @@ -5499,12 +5646,6 @@ a.install-updated-ref:hover { border: 0; } @media (max-width: 600px) { - .install-prefix-options { - flex-direction: column; - } - .install-alt-method { - padding: var(--spacing-sm) var(--spacing-md); - } .install-alt-method code { font-size: 0.75rem; } diff --git a/public/css/workflow.css b/public/css/workflow.css index 1016e3de7..8dfa984dd 100644 --- a/public/css/workflow.css +++ b/public/css/workflow.css @@ -1068,6 +1068,34 @@ border-bottom-color: var(--color-accent); } +/* ============================================ + CHANGELOG + FAQ ROW (side-by-side on desktop) + ============================================ */ + +.changelog-faq-row { + position: relative; +} + +@media (min-width: 1024px) { + .changelog-faq-row { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + column-gap: calc(var(--spacing-xl) * 1.5); + border-top: 1px solid var(--color-mist); + } + + .changelog-faq-row > .changelog-section, + .changelog-faq-row > .faq-section { + border-top: none; + } + + .changelog-faq-row > .faq-section { + border-left: 1px solid var(--color-mist); + padding-left: calc(var(--spacing-xl) * 0.75); + margin-left: calc(var(--spacing-xl) * -0.75); + } +} + /* ============================================ CHANGELOG SECTION ============================================ */ diff --git a/public/index.html b/public/index.html index 956996f5f..666d63681 100644 --- a/public/index.html +++ b/public/index.html @@ -289,11 +289,12 @@ Three ways to use it

The Chrome extension on any site, embedded in /critique during an AI design review, or standalone via npx impeccable live.

- + Impeccable Chrome extension panel listing detected anti-patterns
- New! - Get the Chrome extension → + Coming soon + Chrome extension + Get notified →
@@ -308,11 +309,10 @@

Get Started

-
- -
- 1 -

Install the skills Recommended

+ +
+
+

1Install the skills Recommended

21 commands that steer your AI toward better design, in real time. The full Impeccable experience.

@@ -343,135 +343,114 @@
Then run /impeccable teach to set up your project's design context.
- - -
- Other install methods -
-
- Claude Code plugin -
- $ - /plugin marketplace add pbakaus/impeccable - -
- Then open /plugin to install from Discover tab -
-
-
- Manual download - Contains all provider directories. Extract to your project root. -
- -
- - Avoids conflicts with built-in commands or other skill packs -
-
-
- -
- -
-
-
- -
- 2 -

Add the CLI Beta

-

Deterministic anti-pattern detection, cheatsheet and more. Use in CI and beyond.

+ +
+ Other install methods -
-
-
- - - -
-
-
-
- $ - npm i -g impeccable - -
- Or run directly: npx impeccable ... -
-
-
-
- -
- detect Scan files, dirs, or URLs - skills install Install skills into your project - skills update Update to latest version -
- - -
- - -
- 3 -

Stay updated

- - - -
- Quick reference - Command cheatsheet → -
+
+ Claude Code plugin +
$ - impeccable skills help + /plugin marketplace add pbakaus/impeccable + +
+ Then open /plugin in Claude Code +
+ +
+ Manual download all 11 providers +
- -
-
- Best together -

Skills give your AI the full design toolkit. The CLI adds automated quality gates for reviews and CI.

-
+ +
+ +
+ +

2Add the CLI Beta

+ +
+
+

Anti-pattern detection from the terminal. Scans HTML, CSS, JSX/TSX, Vue, and Svelte — perfect for CI, pre-commit hooks, and one-off audits.

+ +
+
+ $ + npm i -g impeccable + +
+ Or use npx impeccable directly without installing. +
+ + +
+
+ + +
+ +

3Browser extension Coming soon

+ +
+
+

One-click anti-pattern detection on any live page — yours, staging, production, or anyone else's. Same detection engine as /critique, in your browser.

+

Currently in Chrome Web Store review.

+ +
+
+ + +
+ +

4Stay updated

+ +
+
+

Roughly monthly notes on new commands, anti-patterns, and the design thinking behind Impeccable.

+ +
+
+ +
@@ -654,6 +633,7 @@
+
diff --git a/public/js/components/section-nav.js b/public/js/components/section-nav.js index 4e7fbb430..e34039387 100644 --- a/public/js/components/section-nav.js +++ b/public/js/components/section-nav.js @@ -42,9 +42,23 @@ export function initSectionNav() { } } + // If the current section shares its top row with siblings (e.g. side-by-side + // changelog + FAQ on desktop), treat all of them as active. + const activeSections = new Set(); + if (currentSection) { + const currentEl = document.getElementById(currentSection); + const currentTop = currentEl?.offsetTop ?? 0; + sectionIds.forEach(id => { + const el = document.getElementById(id); + if (el && Math.abs(el.offsetTop - currentTop) < 4) { + activeSections.add(id); + } + }); + } + // Update active state items.forEach(item => { - if (item.dataset.section === currentSection) { + if (activeSections.has(item.dataset.section)) { item.classList.add('is-active'); } else { item.classList.remove('is-active');