mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 06:06:37 +03:00
Add tabbed case studies, CLAUDE.md, and various improvements
- Convert "See It In Action" section to tabbed interface for space efficiency - Add CLAUDE.md with project instructions for CSS build process - Update README with additional documentation - Enhance glass terminal component - Add new API handlers and server routes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
0b9c1846a6
commit
af21bf2d34
@@ -1,6 +1,6 @@
|
||||
# Impeccable
|
||||
|
||||
The vocabulary you didn't know you needed. 1 skill, 15 commands, and curated anti-patterns for impeccable style. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.
|
||||
The vocabulary you didn't know you needed. 1 skill, 17 commands, and curated anti-patterns for impeccable style. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.
|
||||
|
||||
## Repository Purpose
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Project Instructions for Claude
|
||||
|
||||
## CSS Build Process
|
||||
|
||||
**IMPORTANT**: After modifying any CSS files in `public/css/` (especially `workflow.css` or `main.css`), you MUST rebuild the Tailwind CSS:
|
||||
|
||||
```bash
|
||||
bunx @tailwindcss/cli -i public/css/main.css -o public/css/styles.css
|
||||
```
|
||||
|
||||
The CSS architecture:
|
||||
- `public/css/main.css` - Main entry point, imports Tailwind and all other CSS files
|
||||
- `public/css/workflow.css` - Commands section, glass terminal, case studies styles
|
||||
- `public/css/styles.css` - **Compiled output** (do not edit directly)
|
||||
|
||||
## Development Server
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
Runs at http://localhost:3000
|
||||
|
||||
## Build System
|
||||
|
||||
The build system compiles skills and commands from `source/` to provider-specific formats in `dist/`:
|
||||
|
||||
```bash
|
||||
bun run build # Build all providers
|
||||
bun run rebuild # Clean and rebuild
|
||||
```
|
||||
|
||||
Source files use placeholders that get replaced per-provider:
|
||||
- `{{model}}` - Model name (Claude, Gemini, GPT, etc.)
|
||||
- `{{config_file}}` - Config file name (CLAUDE.md, .cursorrules, etc.)
|
||||
- `{{ask_instruction}}` - How to ask user questions
|
||||
@@ -11,25 +11,25 @@ Anthropic created [frontend-design](https://github.com/anthropics/skills/tree/ma
|
||||
Every LLM learned from the same generic templates. Without guidance, you get the same predictable mistakes: Inter font, purple gradients, cards nested in cards, gray text on colored backgrounds.
|
||||
|
||||
Impeccable fights that bias with:
|
||||
- **An expanded skill** with 7 domain-specific reference files
|
||||
- **15 steering commands** to audit, polish, simplify, animate, and more
|
||||
- **An expanded skill** with 7 domain-specific reference files ([view source](source/skills/frontend-design/))
|
||||
- **17 steering commands** to audit, review, polish, simplify, animate, and more
|
||||
- **Curated anti-patterns** that explicitly tell the AI what NOT to do
|
||||
|
||||
## What's Included
|
||||
|
||||
### The Skill: frontend-design
|
||||
|
||||
A comprehensive design skill with 7 domain-specific references:
|
||||
A comprehensive design skill with 7 domain-specific references ([view skill](source/skills/frontend-design/SKILL.md)):
|
||||
|
||||
| Reference | Covers |
|
||||
|-----------|--------|
|
||||
| typography | Type systems, font pairing, modular scales, OpenType |
|
||||
| color-and-contrast | OKLCH, tinted neutrals, dark mode, accessibility |
|
||||
| spatial-design | Spacing systems, grids, visual hierarchy |
|
||||
| motion-design | Easing curves, staggering, reduced motion |
|
||||
| interaction-design | Forms, focus states, loading patterns |
|
||||
| responsive-design | Mobile-first, fluid design, container queries |
|
||||
| ux-writing | Button labels, error messages, empty states |
|
||||
| [typography](source/skills/frontend-design/reference/typography.md) | Type systems, font pairing, modular scales, OpenType |
|
||||
| [color-and-contrast](source/skills/frontend-design/reference/color-and-contrast.md) | OKLCH, tinted neutrals, dark mode, accessibility |
|
||||
| [spatial-design](source/skills/frontend-design/reference/spatial-design.md) | Spacing systems, grids, visual hierarchy |
|
||||
| [motion-design](source/skills/frontend-design/reference/motion-design.md) | Easing curves, staggering, reduced motion |
|
||||
| [interaction-design](source/skills/frontend-design/reference/interaction-design.md) | Forms, focus states, loading patterns |
|
||||
| [responsive-design](source/skills/frontend-design/reference/responsive-design.md) | Mobile-first, fluid design, container queries |
|
||||
| [ux-writing](source/skills/frontend-design/reference/ux-writing.md) | Button labels, error messages, empty states |
|
||||
|
||||
### 17 Commands
|
||||
|
||||
@@ -63,6 +63,10 @@ The skill includes explicit guidance on what to avoid:
|
||||
- Don't wrap everything in cards or nest cards inside cards
|
||||
- Don't use bounce/elastic easing (feels dated)
|
||||
|
||||
## See It In Action
|
||||
|
||||
Visit [impeccable.style](https://impeccable.style#casestudies) to see before/after case studies of real projects transformed with Impeccable commands.
|
||||
|
||||
## Installation
|
||||
|
||||
### Option 1: Download from Website (Recommended)
|
||||
|
||||
@@ -224,6 +224,79 @@ document.addEventListener("click", (e) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// CASE STUDIES TABS
|
||||
// ============================================
|
||||
|
||||
function initCaseStudyTabs() {
|
||||
const container = document.querySelector('.transformations-tabbed');
|
||||
if (!container) return;
|
||||
|
||||
const tabs = container.querySelectorAll('.transformation-tab');
|
||||
const panels = container.querySelectorAll('.transformation-panel');
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const tabIndex = tab.dataset.tab;
|
||||
|
||||
// Update tabs
|
||||
tabs.forEach(t => {
|
||||
t.classList.remove('active');
|
||||
t.setAttribute('aria-selected', 'false');
|
||||
});
|
||||
tab.classList.add('active');
|
||||
tab.setAttribute('aria-selected', 'true');
|
||||
|
||||
// Update panels
|
||||
panels.forEach(p => {
|
||||
p.classList.remove('active');
|
||||
});
|
||||
const activePanel = container.querySelector(`[data-panel="${tabIndex}"]`);
|
||||
if (activePanel) {
|
||||
activePanel.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// LIGHTBOX
|
||||
// ============================================
|
||||
|
||||
function initLightbox() {
|
||||
const lightbox = document.getElementById('image-lightbox');
|
||||
if (!lightbox) return;
|
||||
|
||||
const lightboxImage = lightbox.querySelector('.lightbox-image');
|
||||
const closeBtn = lightbox.querySelector('.lightbox-close');
|
||||
|
||||
// Click on transformation images to open lightbox
|
||||
document.querySelectorAll('.transformation-before img, .transformation-after img').forEach(img => {
|
||||
img.addEventListener('click', () => {
|
||||
lightboxImage.src = img.src;
|
||||
lightboxImage.alt = img.alt;
|
||||
lightbox.classList.add('active');
|
||||
document.body.style.overflow = 'hidden';
|
||||
});
|
||||
});
|
||||
|
||||
// Close lightbox
|
||||
function closeLightbox() {
|
||||
lightbox.classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
closeBtn.addEventListener('click', closeLightbox);
|
||||
lightbox.addEventListener('click', (e) => {
|
||||
if (e.target === lightbox) closeLightbox();
|
||||
});
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && lightbox.classList.contains('active')) {
|
||||
closeLightbox();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// STARTUP
|
||||
// ============================================
|
||||
@@ -237,6 +310,8 @@ function init() {
|
||||
initScrollReveal();
|
||||
initGlassTerminal();
|
||||
initFrameworkViz();
|
||||
initLightbox();
|
||||
initCaseStudyTabs();
|
||||
loadContent();
|
||||
|
||||
document.body.classList.add("loaded");
|
||||
|
||||
+367
-1
@@ -109,7 +109,7 @@
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
/* Right: The Terminal (Sticky) */
|
||||
/* Right: The Terminal Stack (Sticky) */
|
||||
.glass-terminal-wrapper {
|
||||
position: sticky;
|
||||
top: var(--spacing-lg);
|
||||
@@ -119,6 +119,138 @@
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
/* Stacked Windows Container */
|
||||
.terminal-stack {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
perspective: 1200px;
|
||||
}
|
||||
|
||||
.terminal-stack-tabs {
|
||||
position: absolute;
|
||||
top: -31px;
|
||||
right: 8px;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.terminal-stack-tab {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.75rem;
|
||||
padding: 5px 12px;
|
||||
background: var(--color-mist);
|
||||
border: 1px solid var(--color-mist);
|
||||
border-bottom: none;
|
||||
border-radius: 6px 6px 0 0;
|
||||
color: var(--color-ash);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.terminal-stack-tab:hover {
|
||||
background: var(--color-paper);
|
||||
color: var(--color-charcoal);
|
||||
}
|
||||
|
||||
.terminal-stack-tab.active {
|
||||
background: var(--color-paper);
|
||||
color: var(--color-ink);
|
||||
border-color: var(--color-mist);
|
||||
}
|
||||
|
||||
/* Individual Windows */
|
||||
.terminal-window {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.3s ease,
|
||||
filter 0.3s ease;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
/* Demo window (front by default) */
|
||||
.terminal-window--demo {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.terminal-window--demo.is-back {
|
||||
transform: translateY(16px) translateX(12px) scale(0.96);
|
||||
opacity: 0.6;
|
||||
filter: brightness(0.92);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Source window (back by default) */
|
||||
.terminal-window--source {
|
||||
z-index: 1;
|
||||
transform: translateY(16px) translateX(12px) scale(0.96);
|
||||
opacity: 0.6;
|
||||
filter: brightness(0.92);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.terminal-window--source.is-front {
|
||||
transform: translateY(0) translateX(0) scale(1);
|
||||
opacity: 1;
|
||||
filter: brightness(1);
|
||||
pointer-events: auto;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Source Window Content */
|
||||
.source-window {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--color-mist);
|
||||
border-radius: 8px;
|
||||
box-shadow:
|
||||
0 20px 60px -10px rgba(0,0,0,0.15),
|
||||
0 0 0 1px rgba(255,255,255,0.5) inset;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.source-header {
|
||||
background: rgba(0,0,0,0.02);
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid var(--color-mist);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.source-title {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-ink);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.source-body {
|
||||
flex: 1;
|
||||
padding: var(--spacing-md);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
color: var(--color-charcoal);
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
background: var(--color-cream);
|
||||
}
|
||||
|
||||
.source-loading {
|
||||
color: var(--color-ash);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.glass-terminal-wrapper {
|
||||
display: none; /* Hide on mobile for now, or stack */
|
||||
@@ -329,3 +461,237 @@
|
||||
}
|
||||
|
||||
@keyframes blink { 50% { opacity: 0; } }
|
||||
|
||||
/* ============================================
|
||||
CASE STUDIES / TRANSFORMATIONS SECTION
|
||||
============================================ */
|
||||
|
||||
.casestudies-section {
|
||||
position: relative;
|
||||
padding: var(--spacing-2xl) 0;
|
||||
border-top: 1px solid var(--color-mist);
|
||||
}
|
||||
|
||||
/* Tabbed transformations */
|
||||
.transformations-tabbed {
|
||||
margin-top: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.transformation-tabs {
|
||||
display: flex;
|
||||
gap: var(--spacing-xs);
|
||||
border-bottom: 1px solid var(--color-mist);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.transformation-tab {
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-ash);
|
||||
background: none;
|
||||
border: none;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.transformation-tab:hover {
|
||||
color: var(--color-charcoal);
|
||||
}
|
||||
|
||||
.transformation-tab.active {
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.transformation-tab.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: var(--color-accent);
|
||||
}
|
||||
|
||||
.transformation-panels {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.transformation-panel {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-lg);
|
||||
animation: fadeInPanel 0.3s ease;
|
||||
}
|
||||
|
||||
.transformation-panel.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@keyframes fadeInPanel {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Side-by-side images */
|
||||
.transformation-images {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.transformation-before,
|
||||
.transformation-after {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.transformation-before img,
|
||||
.transformation-after img,
|
||||
.transformation-placeholder {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 10;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--color-mist);
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.transformation-before img:hover,
|
||||
.transformation-after img:hover,
|
||||
.transformation-placeholder:hover {
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 8px 24px -4px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.transformation-placeholder {
|
||||
background: linear-gradient(135deg, var(--color-mist) 0%, var(--color-cream) 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-ash);
|
||||
font-size: 0.8125rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.transformation-before figcaption,
|
||||
.transformation-after figcaption {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--color-ash);
|
||||
margin-top: var(--spacing-xs);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.transformation-arrow {
|
||||
font-size: 1.5rem;
|
||||
color: var(--color-accent);
|
||||
font-weight: 300;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Info section */
|
||||
.transformation-info {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.transformation-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-ink);
|
||||
margin: 0 0 var(--spacing-xs);
|
||||
}
|
||||
|
||||
.transformation-desc {
|
||||
font-size: 0.9375rem;
|
||||
color: var(--color-charcoal);
|
||||
line-height: 1.6;
|
||||
margin: 0 0 var(--spacing-sm);
|
||||
}
|
||||
|
||||
.transformation-commands {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.transformation-command {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.75rem;
|
||||
background: var(--color-mist);
|
||||
color: var(--color-charcoal);
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Lightbox */
|
||||
.lightbox {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease, visibility 0.3s ease;
|
||||
}
|
||||
|
||||
.lightbox.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.lightbox-close {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 24px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 2.5rem;
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s ease;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.lightbox-close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.lightbox-image {
|
||||
max-width: 90vw;
|
||||
max-height: 85vh;
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.transformation-images {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.transformation-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.transformation-before,
|
||||
.transformation-after {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
+105
-8
@@ -4,20 +4,20 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Impeccable: The missing upgrade to Anthropic's frontend-design skill</title>
|
||||
<meta name="description" content="1 skill, 15 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
|
||||
<meta name="description" content="1 skill, 17 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
|
||||
|
||||
<!-- OpenGraph -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://impeccable.style">
|
||||
<meta property="og:title" content="Impeccable: Design skills for AI coding tools">
|
||||
<meta property="og:description" content="1 skill, 15 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
|
||||
<meta property="og:description" content="1 skill, 17 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
|
||||
<meta property="og:image" content="https://impeccable.style/og-image.svg">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://impeccable.style">
|
||||
<meta name="twitter:title" content="Impeccable: Design skills for AI coding tools">
|
||||
<meta name="twitter:description" content="1 skill, 15 commands, and curated anti-patterns for impeccable frontend design.">
|
||||
<meta name="twitter:description" content="1 skill, 17 commands, and curated anti-patterns for impeccable frontend design.">
|
||||
<meta name="twitter:image" content="https://impeccable.style/og-image.svg">
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
|
||||
@@ -66,11 +66,11 @@
|
||||
|
||||
<div class="equation-block">
|
||||
<span class="equation-label">Steering Commands</span>
|
||||
<span class="equation-title">15 commands</span>
|
||||
<span class="equation-title">17 commands</span>
|
||||
<ul class="equation-list">
|
||||
<li>/polish, /audit, /simplify</li>
|
||||
<li>/bolder, /quieter, /animate</li>
|
||||
<li>and 9 more...</li>
|
||||
<li>and 11 more...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -206,7 +206,7 @@
|
||||
<h2 class="section-title">The Framework</h2>
|
||||
</div>
|
||||
<div class="solution-content">
|
||||
<p class="section-lead" data-reveal>One comprehensive skill with deep expertise, plus 15 commands that form the language of design.</p>
|
||||
<p class="section-lead" data-reveal>One comprehensive skill with deep expertise, plus 17 commands that form the language of design.</p>
|
||||
|
||||
<div class="solution-visual-interactive" id="framework-viz-container" data-reveal>
|
||||
<!-- Subway map generated by JS -->
|
||||
@@ -232,10 +232,106 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 5. PLATFORMS -->
|
||||
<section class="platforms-section" id="downloads">
|
||||
<!-- 5. CASE STUDIES -->
|
||||
<section class="casestudies-section" id="casestudies">
|
||||
<div class="section-header" data-reveal>
|
||||
<span class="section-number">05</span>
|
||||
<h2 class="section-title">See It In Action</h2>
|
||||
<p class="section-subtitle">Sample projects transformed with Impeccable commands</p>
|
||||
</div>
|
||||
|
||||
<div class="transformations-tabbed" data-reveal>
|
||||
<!-- Tab buttons -->
|
||||
<div class="transformation-tabs" role="tablist">
|
||||
<button class="transformation-tab active" role="tab" aria-selected="true" data-tab="0">Dashboard</button>
|
||||
<button class="transformation-tab" role="tab" aria-selected="false" data-tab="1">Landing Page</button>
|
||||
<button class="transformation-tab" role="tab" aria-selected="false" data-tab="2">Form UX</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab panels -->
|
||||
<div class="transformation-panels">
|
||||
<article class="transformation-panel active" role="tabpanel" data-panel="0">
|
||||
<div class="transformation-images">
|
||||
<figure class="transformation-before">
|
||||
<div class="transformation-placeholder">Before screenshot</div>
|
||||
<figcaption>Before</figcaption>
|
||||
</figure>
|
||||
<span class="transformation-arrow">→</span>
|
||||
<figure class="transformation-after">
|
||||
<div class="transformation-placeholder">After screenshot</div>
|
||||
<figcaption>After</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="transformation-info">
|
||||
<h3 class="transformation-title">Dashboard Redesign</h3>
|
||||
<p class="transformation-desc">Generic analytics dashboard transformed into a distinctive, scannable interface.</p>
|
||||
<div class="transformation-commands">
|
||||
<span class="transformation-command">/audit</span>
|
||||
<span class="transformation-command">/normalize</span>
|
||||
<span class="transformation-command">/bolder</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="transformation-panel" role="tabpanel" data-panel="1">
|
||||
<div class="transformation-images">
|
||||
<figure class="transformation-before">
|
||||
<div class="transformation-placeholder">Before screenshot</div>
|
||||
<figcaption>Before</figcaption>
|
||||
</figure>
|
||||
<span class="transformation-arrow">→</span>
|
||||
<figure class="transformation-after">
|
||||
<div class="transformation-placeholder">After screenshot</div>
|
||||
<figcaption>After</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="transformation-info">
|
||||
<h3 class="transformation-title">Landing Page Polish</h3>
|
||||
<p class="transformation-desc">AI-generated landing page refined from generic template to memorable brand experience.</p>
|
||||
<div class="transformation-commands">
|
||||
<span class="transformation-command">/review</span>
|
||||
<span class="transformation-command">/polish</span>
|
||||
<span class="transformation-command">/animate</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="transformation-panel" role="tabpanel" data-panel="2">
|
||||
<div class="transformation-images">
|
||||
<figure class="transformation-before">
|
||||
<div class="transformation-placeholder">Before screenshot</div>
|
||||
<figcaption>Before</figcaption>
|
||||
</figure>
|
||||
<span class="transformation-arrow">→</span>
|
||||
<figure class="transformation-after">
|
||||
<div class="transformation-placeholder">After screenshot</div>
|
||||
<figcaption>After</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="transformation-info">
|
||||
<h3 class="transformation-title">Form UX Overhaul</h3>
|
||||
<p class="transformation-desc">Complex multi-step form simplified and clarified for better completion rates.</p>
|
||||
<div class="transformation-commands">
|
||||
<span class="transformation-command">/simplify</span>
|
||||
<span class="transformation-command">/clarify</span>
|
||||
<span class="transformation-command">/harden</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lightbox for expanded view -->
|
||||
<div class="lightbox" id="image-lightbox">
|
||||
<button class="lightbox-close" aria-label="Close">×</button>
|
||||
<img class="lightbox-image" src="" alt="">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 6. PLATFORMS -->
|
||||
<section class="platforms-section" id="downloads">
|
||||
<div class="section-header" data-reveal>
|
||||
<span class="section-number">06</span>
|
||||
<h2 class="section-title">Download for Your AI Harness</h2>
|
||||
<p class="section-subtitle">Same design intelligence, adapted for your workflow. Download and start using in seconds.</p>
|
||||
</div>
|
||||
@@ -336,6 +432,7 @@
|
||||
<div class="footer-links">
|
||||
<a href="#antidote">Anti-Patterns</a>
|
||||
<a href="#commands-section">Commands</a>
|
||||
<a href="#casestudies">Case Studies</a>
|
||||
<a href="#downloads">Downloads</a>
|
||||
<a href="https://github.com/pbakaus/impeccable">GitHub</a>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { commandProcessSteps, commandCategories, commandRelationships } from "..
|
||||
// Track current split instance and command for cleanup
|
||||
let currentSplitInstance = null;
|
||||
let currentCommandId = null;
|
||||
let sourceCache = {}; // Cache fetched source content
|
||||
|
||||
const MOBILE_BREAKPOINT = 900;
|
||||
|
||||
@@ -79,17 +80,35 @@ function renderDesktopLayout(container, commands) {
|
||||
${manualHTML}
|
||||
</div>
|
||||
<div class="glass-terminal-wrapper">
|
||||
<div class="glass-terminal">
|
||||
<div class="terminal-header">
|
||||
<span class="terminal-dot red"></span>
|
||||
<span class="terminal-dot yellow"></span>
|
||||
<span class="terminal-dot green"></span>
|
||||
<span class="terminal-title">zsh — 80x24</span>
|
||||
<div class="terminal-stack">
|
||||
<div class="terminal-stack-tabs">
|
||||
<button class="terminal-stack-tab active" data-view="demo">Demo</button>
|
||||
<button class="terminal-stack-tab" data-view="source">Source</button>
|
||||
</div>
|
||||
<div class="terminal-body" id="terminal-content">
|
||||
<div class="terminal-line">
|
||||
<span class="terminal-prompt">➜</span>
|
||||
<span>Waiting for input...</span>
|
||||
<div class="terminal-window terminal-window--source">
|
||||
<div class="source-window">
|
||||
<div class="source-header">
|
||||
<span class="source-title" id="source-title">command.md</span>
|
||||
</div>
|
||||
<div class="source-body" id="source-content" data-lenis-prevent>
|
||||
<span class="source-loading">Select a command to view source...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="terminal-window terminal-window--demo">
|
||||
<div class="glass-terminal">
|
||||
<div class="terminal-header">
|
||||
<span class="terminal-dot red"></span>
|
||||
<span class="terminal-dot yellow"></span>
|
||||
<span class="terminal-dot green"></span>
|
||||
<span class="terminal-title">zsh — 80x24</span>
|
||||
</div>
|
||||
<div class="terminal-body" id="terminal-content">
|
||||
<div class="terminal-line">
|
||||
<span class="terminal-prompt">➜</span>
|
||||
<span>Waiting for input...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,6 +116,8 @@ function renderDesktopLayout(container, commands) {
|
||||
</div>
|
||||
`;
|
||||
|
||||
setupStackTabs();
|
||||
|
||||
setupDesktopScrollSpy(commands);
|
||||
|
||||
if (commands.length > 0) {
|
||||
@@ -175,6 +196,9 @@ function updateTerminal(cmd, container, allCommands) {
|
||||
if (currentCommandId === cmd.id) return;
|
||||
currentCommandId = cmd.id;
|
||||
|
||||
// Also update source content
|
||||
updateSourceContent(cmd.id);
|
||||
|
||||
if (currentSplitInstance) {
|
||||
currentSplitInstance.destroy();
|
||||
currentSplitInstance = null;
|
||||
@@ -314,3 +338,73 @@ function setupMobileInteractions(commands) {
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// STACKED WINDOWS - Tab Switching
|
||||
// ============================================
|
||||
|
||||
function setupStackTabs() {
|
||||
const tabs = document.querySelectorAll('.terminal-stack-tab');
|
||||
const demoWindow = document.querySelector('.terminal-window--demo');
|
||||
const sourceWindow = document.querySelector('.terminal-window--source');
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const view = tab.dataset.view;
|
||||
|
||||
// Update tab states
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
|
||||
// Switch windows
|
||||
if (view === 'source') {
|
||||
demoWindow.classList.add('is-back');
|
||||
sourceWindow.classList.add('is-front');
|
||||
} else {
|
||||
demoWindow.classList.remove('is-back');
|
||||
sourceWindow.classList.remove('is-front');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchCommandSource(cmdId) {
|
||||
// Check cache first
|
||||
if (sourceCache[cmdId]) {
|
||||
return sourceCache[cmdId];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/command-source/${cmdId}`);
|
||||
if (!response.ok) throw new Error('Failed to fetch source');
|
||||
const data = await response.json();
|
||||
sourceCache[cmdId] = data.content;
|
||||
return data.content;
|
||||
} catch (error) {
|
||||
console.error('Error fetching command source:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
async function updateSourceContent(cmdId) {
|
||||
const titleEl = document.getElementById('source-title');
|
||||
const contentEl = document.getElementById('source-content');
|
||||
|
||||
if (!titleEl || !contentEl) return;
|
||||
|
||||
titleEl.textContent = `${cmdId}.md`;
|
||||
contentEl.innerHTML = '<span class="source-loading">Loading...</span>';
|
||||
|
||||
const source = await fetchCommandSource(cmdId);
|
||||
if (source) {
|
||||
contentEl.textContent = source;
|
||||
} else {
|
||||
contentEl.innerHTML = '<span class="source-loading">Source not available</span>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import homepage from "../public/index.html";
|
||||
import {
|
||||
getSkills,
|
||||
getCommands,
|
||||
getCommandSource,
|
||||
getPatterns,
|
||||
handleFileDownload,
|
||||
handleBundleDownload
|
||||
@@ -49,6 +50,16 @@ const server = serve({
|
||||
},
|
||||
},
|
||||
|
||||
// API: Get command source content
|
||||
"/api/command-source/:id": async (req) => {
|
||||
const { id } = req.params;
|
||||
const content = await getCommandSource(id);
|
||||
if (!content) {
|
||||
return Response.json({ error: "Command not found" }, { status: 404 });
|
||||
}
|
||||
return Response.json({ content });
|
||||
},
|
||||
|
||||
// API: Download individual file
|
||||
"/api/download/:type/:provider/:id": async (req) => {
|
||||
const { type, provider, id } = req.params;
|
||||
|
||||
@@ -72,6 +72,23 @@ export async function getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
// Get command source content
|
||||
export async function getCommandSource(id) {
|
||||
const sourceDir = join(PROJECT_ROOT, "source");
|
||||
const commandPath = join(sourceDir, "commands", `${id}.md`);
|
||||
|
||||
try {
|
||||
if (!existsSync(commandPath)) {
|
||||
return null;
|
||||
}
|
||||
const content = await readFileContent(commandPath);
|
||||
return content;
|
||||
} catch (error) {
|
||||
console.error("Error reading command source:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the appropriate file path for a provider
|
||||
export function getFilePath(type, provider, id) {
|
||||
const distDir = join(PROJECT_ROOT, "dist");
|
||||
|
||||
Reference in New Issue
Block a user