prototype-system
Prototype System Skill
The unified reference for building prototypes that look and behave like they already belong in the AskElephant app.
When to Use
- Building new prototypes (
/proto) - Iterating on existing prototypes (
/iterate) - Reviewing prototype quality (
/proto-audit) - Deciding where a feature should live in the app (placement analysis)
- Checking design token compliance
- Understanding available production components
Supporting Documents
This skill references two companion documents — load them when building:
- Component Registry:
.cursor/skills/prototype-system/component-registry.md— Catalog of all production components available for prototype reuse - Interactive Patterns:
.cursor/skills/prototype-system/interactive-patterns.md— Patterns for making prototypes fully functional
1. Component-First Approach
The #1 reason prototypes look wrong is they don't use production components.
Before Creating ANY Component
Always scan the live codebase first. The component registry (component-registry.md) documents import patterns and usage notes, but the codebase is the source of truth — it ships faster than any doc.
- Scan the live codebase at the start of every prototype build:
# What primitives exist right now? ls elephant-ai/apps/web/src/components/primitives/ # What UI components exist right now? ls elephant-ai/apps/web/src/components/ui/ # What domain component directories exist? ls -d elephant-ai/apps/web/src/components/*/ # What AI elements exist? ls elephant-ai/apps/web/src/components/ai-elements/ # What prototype infrastructure exists? ls elephant-ai/apps/web/src/components/prototypes/_shared/ # Search for a specific component by name rg -l "export.*ComponentName" elephant-ai/apps/web/src/components/ --type tsx - If found — Import and use directly, or create a thin wrapper in the prototype's
components/directory - If similar — Read the actual file to understand its props and patterns before building
- If not found — Build from production primitives, matching production patterns exactly
component-registry.mdis a patterns reference (import paths, key props, usage notes). Use it after the live scan to understand how to use what you found — not to discover what exists.
Import Priority Order
// 1. Primitives buffer layer (preferred — themed, wrapped)
import { Button } from '@/components/primitives/button';
import { Card, CardHeader, CardContent } from '@/components/primitives/card';
// 2. UI layer (fallback when no primitive wrapper exists)
import { Calendar } from '@/components/ui/calendar';
import { Chart } from '@/components/ui/chart';
// 3. Domain components (for feature-specific reuse)
import { EngagementStateBadge } from '@/components/engagements/engagement-state-badge';
import { CompanyLogo } from '@/components/company/company-logo';
// 4. AI elements (for AI feature prototypes)
import { ChainOfThought } from '@/components/ai-elements/chain-of-thought';
import { Suggestion } from '@/components/ai-elements/suggestion';
// ❌ NEVER — prototype-specific base components
import { Button } from './ui/button';
import { Card } from '../shared/card';
When You Must Create New Components
Place them in the prototype's components/ directory. Build from production primitives:
// ✅ New component built from production primitives
import { Card, CardHeader, CardContent } from '@/components/primitives/card';
import { Badge } from '@/components/primitives/badge';
import { Progress } from '@/components/ui/progress';
export function HealthScoreGauge({ score, label }: HealthScoreGaugeProps) {
return (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">{label}</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<Progress value={score} className="h-2" />
<Badge variant="outline" className="mt-2">
{score}% healthy
</Badge>
</CardContent>
</Card>
);
}
2. App Shell Integration
Every prototype renders inside the real app navigation. No exceptions.
The App Shell Provides
- Top-level navigation — Horizontal nav bar with app sections (Search, My meetings, Customers, Chats, Workflows)
- Global chat access — Pull-out drawer via Cmd/Ctrl+K or nav chat button
- Content header — Page title and actions area
- User/workspace context — Profile menu, workspace switcher
How to Use
For views/ and demo/ stories (any full-page view), wrap in the AppShell decorator:
import { withAppShell } from '@/components/prototypes/_shared';
const meta: Meta = {
title: 'Prototypes/CustomerHealth/v1/Views/Dashboard',
parameters: { layout: 'fullscreen' },
decorators: [
withAppShell({
workspaceId: 'demo-workspace',
route: '/workspaces/demo-workspace/customers/health',
}),
],
};
The withAppShell decorator is implemented at elephant-ai/apps/web/src/components/prototypes/_shared/AppShell.tsx. It wraps stories in the real TopNav, GlobalCommand, and GlobalChat context. Use the barrel import from @/components/prototypes/_shared.
Feature flags — pass them as story parameters (handled by the existing PostHog mock):
parameters: {
featureFlags: { 'customers-page': true, 'new-home-page': true },
}
When NOT to Use the App Shell
components/stories (isolated component testing) — Uselayout: 'centered'orlayout: 'padded'- Testing a component in multiple contexts — Create both isolated and in-shell stories
3. Prototype Structure
Canonical Directory Layout
elephant-ai/apps/web/src/components/prototypes/[InitiativeName]/
├── index.ts # Re-exports latest version
├── v1/
│ ├── components/ # Feature-specific components
│ │ ├── [Component].tsx
│ │ ├── [Component].stories.tsx
│ │ └── ...
│ ├── views/ # Full page/panel views (AppShell required)
│ │ ├── [FeaturePage].tsx
│ │ ├── [FeaturePage].stories.tsx
│ │ └── ...
│ ├── flows/ # Interactive journey stories
│ │ ├── Discovery.tsx
│ │ ├── Onboarding.tsx
│ │ ├── HappyPath.tsx
│ │ ├── ErrorRecovery.tsx
│ │ ├── DayTwo.tsx
│ │ └── Flows.stories.tsx
│ ├── demo/ # Fully functional prototype
│ │ ├── Demo.tsx
│ │ ├── Demo.stories.tsx
│ │ ├── Walkthrough.tsx
│ │ └── Walkthrough.stories.tsx
│ ├── types.ts
│ └── mock-data.ts
What Goes Where
| Directory | Purpose | AppShell? | Layout |
|---|---|---|---|
components/ |
Isolated building blocks | No | centered or padded |
views/ |
Full page as it appears in app | Yes | fullscreen |
flows/ |
Multi-step user journeys | Yes | fullscreen |
demo/ |
Stakeholder-ready click-through | Yes | fullscreen |
Storybook Titles
Prototypes / [InitiativeName] / v1 / Components / [Name]
Prototypes / [InitiativeName] / v1 / Views / [Name]
Prototypes / [InitiativeName] / v1 / Flows
Prototypes / [InitiativeName] / v1 / Demo
Prototypes / [InitiativeName] / v1 / Walkthrough
4. Placement Analysis
Before building, determine where the feature lives. This information configures the app shell and drives the discovery flow.
Questions to Answer
| Question | Determines |
|---|---|
| What type of feature? | Container type (page, panel, modal, section) |
| Where do similar features live? | Domain folder, layout pattern |
| How do users discover it? | Nav item, command palette, deep link, button |
| What's adjacent? | What users do before/after |
| What role/permissions needed? | Feature flag, role check |
Feature Type → Container Decision
| Feature Type | Container | Navigation | Example |
|---|---|---|---|
| Primary workflow | Full page | Top nav item | Customers dashboard |
| Detail view | Side panel / Sheet | Click from list | Company detail panel |
| Quick action | Modal / Dialog | Button trigger | Send notetaker dialog |
| Configuration | Settings page | Settings > Section | Integration config |
| Inline enhancement | Embedded section | Already visible | AI suggestions on meeting page |
| Global utility | Drawer / Overlay | Keyboard shortcut | Global chat (Cmd+K) |
Record Placement Decision
Save to pm-workspace-docs/initiatives/active/[name]/placement-research.md:
## Placement: [Feature Name]
**Type:** Full page
**Route:** `/workspaces/:workspaceId/customer-health`
**Nav Entry:** Top nav > Customers (sub-item or replaces)
**Discovery:** Top nav click + command palette search
**Adjacent Features:** Company detail, engagement list
**Feature Flags:** `customer-health-dashboard`
**Rationale:** [2-3 sentences]
5. Design Token Compliance
Colors — Theme V2 Semantic Tokens
Always use semantic tokens. Never use raw Tailwind color names for semantic purposes.
| Purpose | Use | NOT |
|---|---|---|
| Success/positive | text-success-foreground, bg-success-50 |
text-emerald-500, bg-emerald-50 |
| Error/destructive | text-destruction-foreground, bg-destruction-50 |
text-red-500, bg-rose-50 |
| Warning | text-warning-foreground, bg-warning-50 |
text-amber-500, bg-amber-50 |
| Primary actions | bg-primary, text-primary-foreground |
bg-blue-500 |
| Muted text | text-muted-foreground |
text-gray-500, text-slate-500 |
| Borders | border-border |
border-gray-200 |
| Card surfaces | bg-card |
bg-white |
| Page background | bg-background |
bg-gray-50, bg-slate-50 |
When the .theme-v2 CSS class is needed: Add it to the root wrapper. Check apps/web/src/index.css for the latest token definitions.
When the audit source says emerald/amber/rose: The storybook-components-audit was written before V2 tokens. Map these to semantic tokens.
Spacing — 4px Grid
| Token | Value | Tailwind | Usage |
|---|---|---|---|
| xs | 4px | p-1, gap-1 |
Icon padding, tight gaps |
| sm | 8px | p-2, gap-2 |
Inline gaps |
| md | 12px | p-3, gap-3 |
Component internal padding |
| lg | 16px | p-4, gap-4 |
Card padding, section gaps |
| xl | 24px | p-6, gap-6 |
Page sections |
| 2xl | 32px | p-8, gap-8 |
Page margins |
Card pattern: p-6 outer, CardContent p-6 pt-0, CardHeader pb-2
No arbitrary values. p-[13px] is always wrong. Use the grid.
Typography
| Level | Classes | Usage |
|---|---|---|
| Page title | text-2xl font-semibold tracking-tight |
One per page |
| Section header | text-lg font-semibold |
Section dividers |
| Card title | text-sm font-medium |
Card headers |
| Body | text-sm |
Primary content |
| Secondary | text-xs text-muted-foreground |
Timestamps, metadata |
| Label | text-sm font-medium |
Form labels |
Icons
Lucide React only. Never inline SVGs.
import { Search, MessageSquare, Upload, Settings, ChevronRight } from 'lucide-react';
// Inline with text: h-4 w-4
<Search className="h-4 w-4" />
// Standalone / buttons: h-5 w-5
<Settings className="h-5 w-5" />
6. Flow Requirements
Full Mode — Required Flows
| Flow | What It Shows | Starts From | Key Question Answered |
|---|---|---|---|
| Discovery | How users learn this exists | Dashboard, notification, nav | "How would I even know about this?" |
| Onboarding | First-time setup/activation | Feature entry point | "What do I do the first time?" |
| HappyPath | Core usage scenario | Feature main view | "How does it work when things go right?" |
| ErrorRecovery | Failure handling | Mid-flow error | "What happens when things go wrong?" |
| DayTwo | Returning user value | App home → feature | "Why would I come back to this?" |
Lofi Mode — Minimum Required
| Flow | What It Shows |
|---|---|
| HappyPath | Core usage scenario |
Flow Implementation
Each flow is a state machine that walks the user through a multi-step journey:
function DiscoveryFlow() {
const { state, advance } = useFlowState('browsing');
switch (state) {
case 'browsing':
return <AppDashboard onNoticeFeature={() => advance('noticed')} />;
case 'noticed':
return <FeatureTeaser onExplore={() => advance('exploring')} />;
case 'exploring':
return <FeatureLanding onActivate={() => advance('activated')} />;
case 'activated':
return <ActivationSuccess />;
}
}
7. Quality Checklist
Run this before considering a prototype "done":
Structure
- Follows canonical directory layout (components/ views/ flows/ demo/)
- Storybook titles follow convention
-
index.tsre-exports latest version -
mock-data.tsuses realistic AskElephant data (no lorem ipsum)
Component Reuse
- No prototype-specific
ui/directory - All base components from
@/components/primitives/or@/components/ui/ - Icons from Lucide React (no inline SVGs)
- Report lists reused vs. created components
App Shell
- All
views/stories wrap in AppShell decorator - All
demo/stories wrap in AppShell decorator - Correct nav item highlighted
- Global chat accessible (not in-page chat)
Design Tokens
- Semantic color tokens only (no raw Tailwind colors for semantic use)
- Spacing on 4px grid (no arbitrary values)
- Typography matches production scale
-
.theme-v2applied where needed
Interactivity
- Every button triggers a state change or action
- Forms validate and submit to mock handlers
- Navigation within prototype works
- Loading states resolve after realistic delay
- Error states show recovery actions
Flows
- Discovery flow: how users find the feature
- Onboarding flow: first-time experience
- HappyPath flow: core usage
- ErrorRecovery flow: failure handling
- DayTwo flow: returning user value
- (Lofi: HappyPath only is acceptable)
AI States (for AI features)
- Loading (short)
- Loading (long — 3+ seconds)
- Success
- Error
- Low Confidence
- Empty
Demo
- Fully functional click-through
- Every interaction works
- Walkthrough with step-by-step narration
- Suitable for sharing with stakeholders on Chromatic
8. Anti-Patterns — What Makes Prototypes Look Wrong
| Anti-Pattern | Why It's Wrong | Do This Instead |
|---|---|---|
Prototype-specific ui/ components |
Diverges from production look | Import from @/components/primitives/ |
| Standalone page without app shell | Doesn't show real app context | Use AppShell decorator |
| In-page chat interface | App uses global chat drawer | Show global chat opening via Cmd+K |
| Sidebar navigation | App switched to top-level nav | Use TopNav via AppShell |
Raw Tailwind colors (emerald-500) |
Breaks in theme switching | Use semantic tokens (success-*) |
| Non-functional buttons | Feels like a mockup, not a prototype | Use onClick handlers with state changes |
| Lorem ipsum data | Doesn't feel real | Use realistic AskElephant data |
| Starting at the feature | Skips discovery context | Start at the user's natural entry point |
| Inline SVG icons | Inconsistent with production | Use Lucide React |
bg-white / bg-gray-50 |
Breaks dark mode | Use bg-card / bg-background |
| Missing loading states | Feels static, unrealistic | Add simulated loading with delays |
| Single creative direction | Not enough exploration | Show 2+ options (full mode) |
p-[13px] arbitrary spacing |
Off the design grid | Use 4px grid values |
9. Global Chat Pattern
AskElephant's global chat is a pull-out drawer, NOT an in-page component.
How It Works in Production
- User presses Cmd/Ctrl+K or clicks the Chat nav button
- Chat panel slides in from the right side
- Chat is contextual — it knows what page/entity the user is on
- User can keep chat open while navigating
- Chat thread persists across navigation
How to Prototype Chat Features
- Show the global chat drawer opening (use Sheet component from right side)
- Pre-populate with contextual suggestions based on the current page
- Use the
useMockChat()hook for simulated AI responses - Show
ContextIndicatorwith the current entity - NEVER create a chat input embedded in the page content area
Exception: Specialized Chat Surfaces
Some features use chat as their primary interface (e.g., the Chats page itself). In these cases:
- The chat IS the page content
- It still lives within the app shell
- It's accessed via the "Chats" top nav item
- This is different from creating ad-hoc chat panels inside other features
More from tylersahagun/pm-workspace
digest-website
Generate a shareable website from digests with newspaper-style design and past issues archive. Use when running /publish-digest or when user wants a public digest page.
10slack-block-kit
Format Slack messages using Block Kit for rich, interactive layouts. Apply when sending any Slack message that should look polished.
9agents-generator
Generate product-focused AGENTS.md documentation for AI agents. Use when creating documentation that explains the WHY behind code.
6skylar-start-here
Get the AskElephant app and Storybook running locally for a designer. Use when the designer says "start the app", "run locally", "see my changes", "open storybook", "first time setup", "how do I see the app", or "set up my environment".
6portfolio-status
Analyze status of ALL initiatives with artifact gap matrix, health scoring, and prioritized action queue. Use when running /status-all command or answering "where are we across everything?" questions.
6activity-reporter
Generate time-bounded activity reports (end-of-day, end-of-week, digest) that aggregate work across GitHub, Linear, and PM workspace. Use when running /eod, /eow, /digest, or /eod --sam commands.
6