Restructure narrative: Foundation, Language, Antidote, Visual Mode

Major site restructure reflecting Impeccable's expanded value prop:
- 01 The Foundation: 7 design dimensions, 212 guidelines grid
- 02 The Language: merged periodic table + Commands in Action
- 03 The Antidote: moved from position 1, anti-patterns + gallery
- 04 Visual Mode: live iframe embed of detection overlay demo
- Composite demo page combining purple gradients + side-tab cards
- Updated hero copy (212 guidelines, 22 commands)
- 7-item sticky nav, backward-compat anchor aliases

Detection overlay improvements:
- Staggered entrance animation (80ms per element)
- Banner slides in from top
- Skip body/html from overlay targeting (fixes scrollbar bug)
- Banner overflow: hidden + maxWidth: 100vw
- CLAUDE.md: document that browser script is generated, never edit directly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-04-02 19:56:29 -07:00
co-authored by Claude Opus 4.6
parent 4b356a6b5b
commit 03a4433dcc
11 changed files with 646 additions and 5141 deletions
@@ -1210,11 +1210,20 @@ if (IS_BROWSER) {
// Inject hover styles via CSS (more reliable than JS event listeners)
const styleEl = document.createElement('style');
styleEl.textContent = `
@keyframes impeccable-reveal {
from { opacity: 0; outline-color: transparent; }
to { opacity: 1; outline-color: ${OUTLINE_COLOR}; }
}
.impeccable-overlay:not(.impeccable-banner) {
pointer-events: none;
outline: 2px solid ${OUTLINE_COLOR};
border-radius: 4px;
transition: outline-color 0.3s ease;
animation: impeccable-reveal 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
animation-play-state: paused;
}
.impeccable-overlay.impeccable-visible {
animation-play-state: running;
}
.impeccable-overlay.impeccable-hover {
outline-color: rgba(0,0,0,0.85);
@@ -1287,6 +1296,7 @@ if (IS_BROWSER) {
// Uses a huge rootMargin so all *rendered* elements count as intersecting,
// while display:none / closed <details> / hidden modals etc. do not.
// This is event-driven -- no polling needed.
let overlayIndex = 0;
const visibilityObserver = new IntersectionObserver((entries) => {
for (const entry of entries) {
const overlay = entry.target._impeccableOverlay;
@@ -1294,6 +1304,11 @@ if (IS_BROWSER) {
if (entry.isIntersecting) {
overlay.style.display = '';
positionOverlay(overlay);
if (!overlay._revealed) {
overlay._revealed = true;
overlay.style.animationDelay = `${(overlay._staggerIndex || 0) * 80}ms`;
requestAnimationFrame(() => overlay.classList.add('impeccable-visible'));
}
} else {
overlay.style.display = 'none';
}
@@ -1360,6 +1375,7 @@ if (IS_BROWSER) {
// Start hidden; the IntersectionObserver will show it once the target is rendered
outline.style.display = 'none';
outline._staggerIndex = overlayIndex++;
el._impeccableOverlay = outline;
visibilityObserver.observe(el);
@@ -1380,8 +1396,13 @@ if (IS_BROWSER) {
background: LABEL_BG, color: 'white',
fontFamily: 'system-ui, sans-serif', fontSize: '13px',
display: 'flex', alignItems: 'center', pointerEvents: 'auto',
height: '36px',
height: '36px', overflow: 'hidden', maxWidth: '100vw',
transform: 'translateY(-100%)',
transition: 'transform 0.4s cubic-bezier(0.16, 1, 0.3, 1)',
});
requestAnimationFrame(() => requestAnimationFrame(() => {
banner.style.transform = 'translateY(0)';
}));
// Scrollable findings area
const scrollArea = document.createElement('div');
@@ -1475,6 +1496,8 @@ if (IS_BROWSER) {
// Skip browser extension elements (Claude, etc.)
const elId = el.id || '';
if (elId.startsWith('claude-') || elId.startsWith('cic-')) continue;
// Skip html/body -- page-level findings go in the banner, not a full-page overlay
if (el === document.body || el === document.documentElement) continue;
const findings = [
...checkElementBordersDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+2
View File
@@ -66,6 +66,8 @@ The detection script is at `source/skills/critique/scripts/detect-antipatterns.m
Build the browser script: `node scripts/build-browser-detector.js`
**IMPORTANT**: The browser script is **generated** from `detect-antipatterns.mjs` -- never edit the browser `.js` files directly. All changes must go in the `.mjs` source. The build strips Node-specific sections and wraps it in an IIFE. Running `bun run build` also regenerates it, which will **overwrite** any direct edits to the browser script.
## Versioning
When bumping the version, update **all** of these locations to keep them in sync:
@@ -0,0 +1,232 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Visual Mode Demo</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', sans-serif;
min-height: 100vh;
background: linear-gradient(135deg, #1a0a2e 0%, #2d1b4e 50%, #1a0a2e 100%);
color: white;
padding: 60px 24px 24px;
overflow-x: hidden;
}
.container {
max-width: 720px;
margin: 0 auto;
background: linear-gradient(145deg, #2a1a4a 0%, #1a0a2e 100%);
border-radius: 24px;
padding: 32px;
position: relative;
overflow: hidden;
}
/* orbs removed to reduce detector noise */
.content { position: relative; z-index: 1; }
nav {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32px;
}
.logo {
font-size: 18px;
font-weight: 800;
background: linear-gradient(135deg, #a855f7, #ec4899);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.nav-links {
display: flex;
gap: 20px;
align-items: center;
}
.nav-links a {
color: rgba(255,255,255,0.6);
text-decoration: none;
font-size: 13px;
}
.nav-cta {
background: linear-gradient(135deg, #8b5cf6, #a855f7) !important;
color: white !important;
padding: 8px 16px;
border-radius: 999px;
font-weight: 600;
font-size: 12px;
}
.hero {
text-align: center;
padding: 0 20px;
margin-bottom: 32px;
}
.badge {
display: inline-flex;
align-items: center;
gap: 6px;
background: linear-gradient(135deg, rgba(139,92,246,0.3), rgba(168,85,247,0.2));
border: 1px solid rgba(168,85,247,0.4);
padding: 6px 14px;
border-radius: 999px;
font-size: 11px;
margin-bottom: 16px;
color: #c4b5fd;
}
.badge::before { content: '\2728'; }
h1 {
font-size: 36px;
font-weight: 800;
line-height: 1.1;
margin-bottom: 12px;
background: linear-gradient(135deg, #fff 0%, #c4b5fd 50%, #a855f7 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
font-size: 14px;
color: rgba(255,255,255,0.5);
max-width: 400px;
margin: 0 auto 24px;
line-height: 1.6;
}
/* Side-tab border cards */
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 24px;
}
.card {
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;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
}
.card h3 {
font-size: 14px;
font-weight: 700;
margin-bottom: 6px;
}
.card p {
font-size: 11px;
color: rgba(255,255,255,0.5);
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;
justify-content: center;
gap: 40px;
padding-top: 20px;
border-top: 1px solid rgba(139,92,246,0.3);
}
.stat-value {
font-size: 24px;
font-weight: 800;
background: linear-gradient(135deg, #a855f7, #ec4899);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-label {
font-size: 11px;
color: rgba(255,255,255,0.4);
margin-top: 2px;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
<nav>
<div class="logo">Synthwave.ai</div>
<div class="nav-links">
<a href="#">Features</a>
<a href="#">Pricing</a>
<a href="#" class="nav-cta">Get Started</a>
</div>
</nav>
<div class="hero">
<span class="badge">New: AI-Powered Everything</span>
<h1>Build the Future<br>With AI Magic</h1>
<p class="subtitle">Transform your workflow with our revolutionary AI platform. Purple gradients included.</p>
</div>
<div class="cards">
<div class="card">
<div class="card-icon">&#9889;</div>
<h3>Fast Performance</h3>
<p>Blazing fast inference with our optimized pipeline.</p>
</div>
<div class="card">
<div class="card-icon">&#128274;</div>
<h3>Secure & Safe</h3>
<p>Enterprise-grade security with encrypted data.</p>
</div>
<div class="card">
<div class="card-icon">&#128200;</div>
<h3>Analytics</h3>
<p>Powerful insights to help you make better decisions.</p>
</div>
</div>
<div class="stats">
<div class="stat">
<div class="stat-value">10K+</div>
<div class="stat-label">Active Users</div>
</div>
<div class="stat">
<div class="stat-value">99.9%</div>
<div class="stat-label">Uptime Guaranteed</div>
</div>
<div class="stat">
<div class="stat-value">50M+</div>
<div class="stat-label">Data Processed</div>
</div>
</div>
</div>
</div>
<script src="/js/detect-antipatterns-browser.js"></script>
</body>
</html>
+2
View File
@@ -7,6 +7,7 @@ import { initFrameworkViz } from "./js/components/framework-viz.js";
import { initScrollReveal } from "./js/utils/reveal.js";
import { initAnchorScroll, initHashTracking } from "./js/utils/scroll.js";
import { initSectionNav } from "./js/components/section-nav.js";
import { initFoundationGrid } from "./js/components/foundation-grid.js";
// ============================================
// STATE
@@ -226,6 +227,7 @@ function init() {
initScrollReveal();
initGlassTerminal();
initFrameworkViz();
initFoundationGrid();
initSectionNav();
loadContent();
+227 -1
View File
@@ -973,7 +973,233 @@ code {
}
/* ============================================
SOLUTION SECTION
FOUNDATION SECTION
============================================ */
.foundation-section {
padding: var(--spacing-2xl) 0;
border-top: 1px solid var(--color-mist);
}
.foundation-content {
display: grid;
gap: var(--spacing-lg);
}
.foundation-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--spacing-sm);
}
@media (max-width: 900px) {
.foundation-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 480px) {
.foundation-grid {
grid-template-columns: 1fr;
}
}
.foundation-card {
padding: var(--spacing-md);
background: white;
border-radius: 8px;
}
.foundation-card-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: var(--spacing-xs);
}
.foundation-card-label {
font-family: var(--font-mono);
font-size: 0.625rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-ink);
}
.foundation-card-count {
font-family: var(--font-mono);
font-size: 0.625rem;
color: var(--color-accent);
}
.foundation-card-detail {
font-size: 0.8125rem;
line-height: 1.5;
color: var(--color-ash);
margin: 0;
}
.foundation-cta {
text-align: center;
}
.foundation-cta-text {
font-size: 0.9375rem;
color: var(--color-charcoal);
margin: 0;
}
.foundation-cta-text code {
font-family: var(--font-mono);
font-size: 0.875rem;
color: var(--color-ink);
}
.foundation-slash {
color: var(--color-accent);
}
/* ============================================
LANGUAGE SECTION (wraps periodic table + commands)
============================================ */
.language-section {
padding: var(--spacing-2xl) 0;
border-top: 1px solid var(--color-mist);
}
.language-content {
display: grid;
gap: var(--spacing-xl);
}
.commands-subsection {
display: grid;
gap: var(--spacing-lg);
}
.commands-header-title {
font-family: var(--font-display);
font-size: clamp(1.5rem, 3vw, 2rem);
font-weight: 400;
margin: 0 0 var(--spacing-xs);
color: var(--color-ink);
}
.commands-header-subtitle {
font-size: 1rem;
color: var(--color-charcoal);
margin: 0;
}
/* ============================================
VISUAL MODE SECTION
============================================ */
.visual-mode-section {
padding: var(--spacing-2xl) 0;
border-top: 1px solid var(--color-mist);
}
.visual-mode-content {
display: grid;
gap: var(--spacing-lg);
}
.visual-mode-demo {
display: grid;
grid-template-columns: 3fr 2fr;
gap: var(--spacing-xl);
align-items: start;
}
@media (max-width: 900px) {
.visual-mode-demo {
grid-template-columns: 1fr;
}
}
/* Browser chrome frame for the iframe */
.visual-mode-preview {
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--color-mist);
box-shadow: 0 8px 30px -6px rgba(0,0,0,0.12);
}
.visual-mode-preview-header {
display: flex;
align-items: center;
gap: 6px;
padding: 10px 14px;
background: var(--color-cream);
border-bottom: 1px solid var(--color-mist);
}
.visual-mode-preview-dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
.visual-mode-preview-dot.red { background: #ff5f56; }
.visual-mode-preview-dot.yellow { background: #ffbd2e; }
.visual-mode-preview-dot.green { background: #27c93f; }
.visual-mode-preview-title {
margin-left: auto;
font-family: var(--font-mono);
font-size: 0.75rem;
color: var(--color-ash);
}
.visual-mode-iframe {
display: block;
width: 100%;
height: 480px;
border: none;
background: white;
}
/* Details sidebar */
.visual-mode-details {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}
.visual-mode-feature {
padding: var(--spacing-md);
background: white;
border-radius: 8px;
}
.visual-mode-feature-label {
display: block;
font-family: var(--font-mono);
font-size: 0.625rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-accent);
margin-bottom: var(--spacing-xs);
}
.visual-mode-feature p {
font-size: 0.8125rem;
line-height: 1.55;
color: var(--color-charcoal);
margin: 0;
}
.visual-mode-feature code {
font-family: var(--font-mono);
font-size: 0.8125rem;
color: var(--color-ink);
}
/* ============================================
SOLUTION SECTION (legacy styles for periodic table)
============================================ */
.solution-section {
+1 -5080
View File
File diff suppressed because one or more lines are too long
+103 -57
View File
@@ -49,12 +49,13 @@
<!-- Sticky Section Nav -->
<nav class="section-nav" id="section-nav" aria-label="Page sections">
<a href="#antidote" class="section-nav-item" data-section="antidote"><span class="section-nav-num">01</span><span class="section-nav-label">Anti-Patterns</span></a>
<a href="#solution" class="section-nav-item" data-section="solution"><span class="section-nav-num">02</span><span class="section-nav-label">Framework</span></a>
<a href="#commands-section" class="section-nav-item" data-section="commands-section"><span class="section-nav-num">03</span><span class="section-nav-label">Commands</span></a>
<a href="#downloads" class="section-nav-item" data-section="downloads"><span class="section-nav-num">04</span><span class="section-nav-label">Install</span></a>
<a href="#changelog" class="section-nav-item" data-section="changelog"><span class="section-nav-num">05</span><span class="section-nav-label">Changelog</span></a>
<a href="#faq" class="section-nav-item" data-section="faq"><span class="section-nav-num">06</span><span class="section-nav-label">FAQ</span></a>
<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>
<a href="#language" class="section-nav-item" data-section="language"><span class="section-nav-num">02</span><span class="section-nav-label">Language</span></a>
<a href="#antidote" class="section-nav-item" data-section="antidote"><span class="section-nav-num">03</span><span class="section-nav-label">Antidote</span></a>
<a href="#visual-mode" class="section-nav-item" data-section="visual-mode"><span class="section-nav-num">04</span><span class="section-nav-label">Visual</span></a>
<a href="#downloads" class="section-nav-item" data-section="downloads"><span class="section-nav-num">05</span><span class="section-nav-label">Install</span></a>
<a href="#changelog" class="section-nav-item" data-section="changelog"><span class="section-nav-num">06</span><span class="section-nav-label">New</span></a>
<a href="#faq" class="section-nav-item" data-section="faq"><span class="section-nav-num">07</span><span class="section-nav-label">FAQ</span></a>
</nav>
<!-- 1. HERO - Combined with Problem Section -->
@@ -69,15 +70,15 @@
<h1 class="hero-title-combined">Impeccable</h1>
<p class="hero-tagline-combined">Design fluency for AI harnesses</p>
<p class="hero-hook-text hero-hook-text--full">Great design prompts require design vocabulary. Most people don't have it. You can't ask for "more vertical rhythm" if you've never used those words. Impeccable gives you commands that put designer language in your hands.</p>
<p class="hero-hook-text hero-hook-text--short">Great design prompts require design vocabulary. Impeccable gives you commands that put designer language in your hands.</p>
<p class="hero-hook-text hero-hook-text--full">Great design prompts require design vocabulary. Most people don't have it. Impeccable teaches your AI 212 design guidelines and gives you 22 commands to steer the result.</p>
<p class="hero-hook-text hero-hook-text--short">Impeccable teaches your AI real design and gives you 22 commands to steer the result.</p>
<div class="hero-included-box">
<span class="hero-included-title">What's included</span>
<div class="hero-included-items">
<span>Enhanced <em>impeccable</em> skill + anti-patterns</span>
<span class="hero-included-sep">·</span>
<span>20 design skills: /polish, /audit, /typeset, /overdrive...</span>
<span>22 design commands: /polish, /audit, /typeset, /overdrive...</span>
</div>
</div>
@@ -157,10 +158,57 @@
<main class="site-content" id="main-content">
<!-- 2. THE ANTIDOTE - Patterns & Anti-Patterns -->
<section class="antidote-section" id="antidote">
<!-- 01. THE FOUNDATION - Design knowledge -->
<section class="foundation-section" id="foundation">
<div class="section-header" data-reveal>
<span class="section-number">01</span>
<h2 class="section-title">The Foundation</h2>
</div>
<div class="foundation-content">
<p class="section-lead" data-reveal>Before commands, before detection -- Impeccable teaches your AI real design. 212 guidelines across 7 dimensions, loaded as deep reference knowledge every time your AI writes code.</p>
<div class="foundation-grid" data-reveal>
<!-- 7 dimension cards, rendered by JS -->
</div>
<div class="foundation-cta" data-reveal>
<p class="foundation-cta-text">Run <code><span class="foundation-slash">/</span>impeccable teach</code> once to set your project's design context. Every command benefits.</p>
</div>
</div>
</section>
<!-- 02. THE LANGUAGE - Commands vocabulary + demos -->
<section class="language-section" id="language">
<div id="solution" style="height:0;overflow:hidden;visibility:hidden"></div>
<div class="section-header" data-reveal>
<span class="section-number">02</span>
<h2 class="section-title">The Language</h2>
</div>
<div class="language-content">
<p class="section-lead" data-reveal>22 commands form a shared vocabulary between you and your AI. Each one encodes a specific design discipline -- so you can steer with precision.</p>
<div class="solution-visual-interactive" id="framework-viz-container" data-reveal>
<!-- Periodic table generated by JS -->
</div>
<div id="commands-section" style="height:0;overflow:hidden;visibility:hidden"></div>
<div class="commands-subsection">
<div class="commands-header" data-reveal>
<h3 class="commands-header-title">Commands in Action</h3>
<p class="commands-header-subtitle">Interactive demos for every command <a href="/cheatsheet" class="cheatsheet-link">View cheatsheet &rarr;</a></p>
</div>
<div class="commands-gallery">
<!-- Rendered by JS -->
</div>
</div>
</div>
</section>
<!-- 03. THE ANTIDOTE - Anti-patterns -->
<section class="antidote-section" id="antidote">
<div class="section-header" data-reveal>
<span class="section-number">03</span>
<h2 class="section-title">The Antidote</h2>
</div>
<div class="antidote-content">
@@ -168,12 +216,10 @@
<!-- Patterns + Gallery side by side -->
<div class="antidote-row" data-reveal>
<!-- Pattern reference — tabbed categories -->
<div class="patterns-tabbed" id="patterns-categories">
<!-- Rendered by JS -->
</div>
<!-- Gallery of Shame — 3D card stack -->
<div class="gallery-stack-container">
<div class="gallery-stack-header">
<h3 class="gallery-stack-title">Gallery of Shame</h3>
@@ -210,55 +256,55 @@
</div>
</div>
<!-- Detection callout (full width below) -->
<div class="detection-callout" data-reveal>
<div class="detection-callout-inner">
<span class="detection-badge">New!</span>
<p class="detection-desc">24 patterns are caught automatically by the CLI and browser overlay -- no LLM needed. The rest are caught by <code>/critique</code> through design analysis.</p>
<code class="detection-cmd">npx impeccable detect src/</code>
</div>
</div>
<div class="antidote-footer" data-reveal>
<a href="https://github.com/pbakaus/impeccable/issues/new?labels=pattern&title=Pattern%20suggestion%3A%20" class="antidote-suggest-link">Suggest a pattern &rarr;</a>
</div>
</div>
</section>
<!-- 3. THE SOLUTION - Two-part system -->
<section class="solution-section" id="solution">
<div class="section-header" data-reveal>
<span class="section-number">02</span>
<h2 class="section-title">The Framework</h2>
</div>
<div class="solution-content">
<p class="section-lead" data-reveal>One skill with deep design expertise. 20 commands that form the vocabulary of design. And a CLI that catches what humans miss.</p>
<div class="solution-visual-interactive" id="framework-viz-container" data-reveal>
<!-- Subway map generated by JS -->
</div>
</div>
</section>
<!-- 4. COMMANDS - Glass Terminal -->
<section class="commands-section" id="commands-section">
<div class="section-header" data-reveal>
<span class="section-number">03</span>
<h2 class="section-title">Commands in Action</h2>
<p class="section-subtitle">Interactive demos for every command <a href="/cheatsheet" class="cheatsheet-link">View cheatsheet &rarr;</a></p>
</div>
<div class="commands-gallery">
<!-- Rendered by JS -->
</div>
</section>
<!-- 5. GET STARTED -->
<section class="platforms-section" id="downloads">
<!-- 04. VISUAL MODE - Browser overlay -->
<section class="visual-mode-section" id="visual-mode">
<div class="section-header" data-reveal>
<span class="section-number">04</span>
<h2 class="section-title">Visual Mode</h2>
</div>
<div class="visual-mode-content">
<p class="section-lead" data-reveal>See design issues highlighted directly in your browser. When you run <code>/critique</code>, Impeccable injects a diagnostic overlay onto your live page -- no screenshots, no guesswork.</p>
<div class="visual-mode-demo" data-reveal>
<div class="visual-mode-preview">
<div class="visual-mode-preview-header">
<span class="visual-mode-preview-dot red"></span>
<span class="visual-mode-preview-dot yellow"></span>
<span class="visual-mode-preview-dot green"></span>
<span class="visual-mode-preview-title">Live detection overlay</span>
</div>
<iframe src="/antipattern-examples/visual-mode-demo.html" class="visual-mode-iframe" loading="lazy" title="Anti-pattern detection overlay demo"></iframe>
</div>
<div class="visual-mode-details">
<div class="visual-mode-feature">
<span class="visual-mode-feature-label">24 deterministic checks</span>
<p>No LLM needed. Pattern matching catches purple gradients, overused fonts, nested cards, low contrast, and more.</p>
</div>
<div class="visual-mode-feature">
<span class="visual-mode-feature-label">Any workflow</span>
<p>Embedded in <code>/critique</code>, or run standalone via the CLI.</p>
</div>
<div class="detection-callout">
<div class="detection-callout-inner">
<span class="detection-badge">New!</span>
<code class="detection-cmd">npx impeccable detect src/</code>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 05. GET STARTED -->
<section class="platforms-section" id="downloads">
<div class="section-header" data-reveal>
<span class="section-number">05</span>
<h2 class="section-title">Get Started</h2>
</div>
@@ -429,7 +475,7 @@
<!-- 5. CHANGELOG -->
<section class="changelog-section" id="changelog">
<div class="section-header" data-reveal>
<span class="section-number">05</span>
<span class="section-number">06</span>
<h2 class="section-title">What's New</h2>
</div>
@@ -547,7 +593,7 @@
<!-- 6. FAQ -->
<section class="faq-section" id="faq">
<div class="section-header" data-reveal>
<span class="section-number">06</span>
<span class="section-number">07</span>
<h2 class="section-title">Frequently Asked Questions</h2>
</div>
@@ -635,7 +681,7 @@
<div class="footer-links">
<a href="#antidote">Anti-Patterns</a>
<a href="/gallery">Gallery of Shame</a>
<a href="#commands-section">Commands</a>
<a href="#language">Commands</a>
<a href="/cheatsheet">Cheatsheet</a>
<a href="/privacy">Privacy</a>
<a href="https://github.com/pbakaus/impeccable">GitHub</a>
+19
View File
@@ -0,0 +1,19 @@
import { skillFocusAreas, dimensionGuidelineCounts } from '../data.js';
export function initFoundationGrid() {
const container = document.querySelector('.foundation-grid');
if (!container) return;
const dimensions = skillFocusAreas['impeccable'];
if (!dimensions) return;
container.innerHTML = dimensions.map(dim => `
<div class="foundation-card">
<div class="foundation-card-header">
<span class="foundation-card-label">${dim.area}</span>
<span class="foundation-card-count">${dimensionGuidelineCounts[dim.area] || ''}</span>
</div>
<p class="foundation-card-detail">${dim.detail}</p>
</div>
`).join('');
}
+11
View File
@@ -30,6 +30,17 @@ export const skillFocusAreas = {
]
};
// Guideline counts per dimension (verified from reference files)
export const dimensionGuidelineCounts = {
'Typography': 33,
'Color & Contrast': 29,
'Spatial Design': 27,
'Motion': 32,
'Interaction': 36,
'Responsive': 23,
'UX Writing': 32
};
// Reference domains within the impeccable skill
export const skillReferenceDomains = [
'typography',
+1 -1
View File
@@ -91,7 +91,7 @@ const server = serve({
const assetFile = file(filePath);
if (await assetFile.exists()) {
return new Response(assetFile, {
headers: { "Content-Type": "text/html", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY" }
headers: { "Content-Type": "text/html", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "SAMEORIGIN" }
});
}
return new Response("Not Found", { status: 404 });
@@ -1208,11 +1208,20 @@ if (IS_BROWSER) {
// Inject hover styles via CSS (more reliable than JS event listeners)
const styleEl = document.createElement('style');
styleEl.textContent = `
@keyframes impeccable-reveal {
from { opacity: 0; outline-color: transparent; }
to { opacity: 1; outline-color: ${OUTLINE_COLOR}; }
}
.impeccable-overlay:not(.impeccable-banner) {
pointer-events: none;
outline: 2px solid ${OUTLINE_COLOR};
border-radius: 4px;
transition: outline-color 0.3s ease;
animation: impeccable-reveal 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
animation-play-state: paused;
}
.impeccable-overlay.impeccable-visible {
animation-play-state: running;
}
.impeccable-overlay.impeccable-hover {
outline-color: rgba(0,0,0,0.85);
@@ -1285,6 +1294,7 @@ if (IS_BROWSER) {
// Uses a huge rootMargin so all *rendered* elements count as intersecting,
// while display:none / closed <details> / hidden modals etc. do not.
// This is event-driven -- no polling needed.
let overlayIndex = 0;
const visibilityObserver = new IntersectionObserver((entries) => {
for (const entry of entries) {
const overlay = entry.target._impeccableOverlay;
@@ -1292,6 +1302,11 @@ if (IS_BROWSER) {
if (entry.isIntersecting) {
overlay.style.display = '';
positionOverlay(overlay);
if (!overlay._revealed) {
overlay._revealed = true;
overlay.style.animationDelay = `${(overlay._staggerIndex || 0) * 80}ms`;
requestAnimationFrame(() => overlay.classList.add('impeccable-visible'));
}
} else {
overlay.style.display = 'none';
}
@@ -1358,6 +1373,7 @@ if (IS_BROWSER) {
// Start hidden; the IntersectionObserver will show it once the target is rendered
outline.style.display = 'none';
outline._staggerIndex = overlayIndex++;
el._impeccableOverlay = outline;
visibilityObserver.observe(el);
@@ -1378,8 +1394,13 @@ if (IS_BROWSER) {
background: LABEL_BG, color: 'white',
fontFamily: 'system-ui, sans-serif', fontSize: '13px',
display: 'flex', alignItems: 'center', pointerEvents: 'auto',
height: '36px',
height: '36px', overflow: 'hidden', maxWidth: '100vw',
transform: 'translateY(-100%)',
transition: 'transform 0.4s cubic-bezier(0.16, 1, 0.3, 1)',
});
requestAnimationFrame(() => requestAnimationFrame(() => {
banner.style.transform = 'translateY(0)';
}));
// Scrollable findings area
const scrollArea = document.createElement('div');
@@ -1473,6 +1494,8 @@ if (IS_BROWSER) {
// Skip browser extension elements (Claude, etc.)
const elId = el.id || '';
if (elId.startsWith('claude-') || elId.startsWith('cic-')) continue;
// Skip html/body -- page-level findings go in the banner, not a full-page overlay
if (el === document.body || el === document.documentElement) continue;
const findings = [
...checkElementBordersDOM(el).map(f => ({ type: f.id, detail: f.snippet })),