Ui ux pro max v7
AI-powered unified design intelligence skill with embedded heuristic data, 60 UI styles, 48 color palettes, 36 font pairings, 24 industry rules, production-grade components, programmatic video templates, and modern CSS primitives. Use for generating production-ready UI with expert design reasoning.From its SKILL.md
npx -y skills add marktantongco/opencodelinux --skill ui-ux-pro-max-v7Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
114.2 KB, ~31.9k tokens by cl100k_base, as published. Nobody here has run it
UI/UX Pro Max v7.1 — Unified Design Intelligence Skill
Version: 7.1
Scope: AI-powered design intelligence with embedded heuristic data, production-grade components, integrated programmatic video, and modern CSS primitives.
Works with: Claude Code, Cursor, Windsurf, OpenCode, Kiro, Roo Code, Gemini CLI, Trae, Copilot, Codex CLI, Zed, Aider
Word Count: ~18,000 | Embedded Data: 60 styles, 48 palettes (OKLCH + hex), 36 font pairings, 24 industry rules, 24 animations, 14 components, 4 video templates
MODULE 0: Meta & Principles
0.1 What This Skill Actually Provides
This skill contains all data embedded within this document. No external databases, CLI tools, or Python scripts are required. The AI assistant reads the tables, rules, and code examples directly from this skill file during conversation.
Embedded Assets:
- 60 fully-documented UI styles with motion language and anti-patterns
- 48 complete color palettes with hex codes, OKLCH values, dark mode variants, and verified WCAG 2.2 AA contrast ratios
- 36 distinctive font pairings with verified Google Fonts import URLs and fallback stacks
- 24 industry-specific reasoning heuristics with trust signals and differentiation strategies
- 24 production-ready CSS animation presets with
prefers-reduced-motionfallbacks - 14 fully-coded React components (TypeScript + Tailwind CSS) using design tokens
- 4 Remotion video composition templates with 5 essential video components
- Complete design token schema (JSON + CSS variables + OKLCH)
- Real accessibility validation procedures with contrast calculation formulas
- Modern CSS primitives (container queries, anchor positioning, view transitions,
@starting-style)
0.2 Philosophy
Most AI-generated interfaces cluster around statistical averages: Inter font, purple gradient, card layout, safe neutrals. This skill breaks that convergence through structured creative commitment — forcing bold aesthetic choices before code is written, then grounding those choices in embedded design intelligence.
The workflow: Creative Brief → Data Lookup (embedded tables) → Design Token Generation → Component Assembly → Validation.
0.3 How to Use This Skill
- Read the user's request and extract: industry, audience, platform, tone keywords
- Run the Creative Brief (Module 1, 5 questions)
- Look up matches in embedded tables (Module 7):
- Industry → Style recommendations
- Style → Palette + Font pairing
- Palette → Verify contrast ratios
- Generate design tokens (Module 3, token schema)
- Assemble components (Module 3, 14 components)
- Apply motion (Module 4, 24 presets)
- Validate (Module 6, real procedures)
0.4 Integration with Other Skills — The Design Operating System
Think of these five skills as a Design Operating System — each skill is a module, and the real value is in how they compose together, not individually.
| Skill | What It Adds | How It Complements This Skill | Cross-Reference |
|---|---|---|---|
Anthropic frontend-design | Bold aesthetic principles, trust patterns, AI-native UI | This skill adds embedded data and systematic execution | Module 9.1 |
| Vercel Web Design Guidelines | Accessibility/UX rules, performance budgets, SEO | This skill adds real validation procedures and token systems | Module 9.2 |
| Vercel React Best Practices | Performance rules, state management, Server Components | This skill adds component architecture and motion patterns | Module 9.3 |
| GSAP Animations | Complex timeline control, ScrollTrigger, React hooks | This skill adds CSS-first presets and component integration | Module 9.4 |
These stack. Install frontend-design for creative direction, this skill for systematic execution.
0.5 Skill Selector — Decision Tree
Route users to the right skill based on their project type:
START: What are you building?
│
├─ AI Chat Interface → Anthropic frontend-design (trust, conversation patterns)
│ + This skill (tokens, components, validation)
│
├─ Marketing/Landing Page → This skill (creative brief → tokens → components)
│ + GSAP Animations (scroll-driven effects)
│
├─ Dashboard/SaaS App → This skill (design system, components)
│ + Vercel React Best Practices (state, Server Components)
│ + Vercel Web Design Guidelines (accessibility, performance)
│
├─ Animation-Heavy Portfolio → GSAP Animations (timelines, ScrollTrigger)
│ + This skill (design tokens, palettes)
│
├─ Accessible Government/Health → Vercel Web Design Guidelines (WCAG, performance)
│ + This skill (components, validation)
│
└─ Full Production App → ALL FIVE SKILLS
This skill = design system foundation
Anthropic = AI-specific patterns
Vercel Web = accessibility/SEO/security
Vercel React = architecture/performance
GSAP = advanced motion
0.6 Version Compatibility
| This Skill | Works With | Not Compatible With |
|---|---|---|
| v7.1 | anthropic-frontend-design v1.1 | Pre-v1.1 |
| v7.1 | gsap-animations v1.1 | Pre-v1.1 |
| v7.1 | vercel-react-best-practices v1.1 | Pre-v1.1 |
| v7.1 | vercel-web-design-guidelines v1.1 | Pre-v1.1 |
MODULE 1: Creative Brief Engine
1.1 The 5-Question Creative Brief
Before generating any design system, answer these 5 questions and state the answers explicitly:
1. PURPOSE
What problem does this interface solve? Who uses it?
→ Extract: user persona, primary task, success metric
2. TONE
Pick an EXTREME aesthetic direction. Do not converge on safe defaults:
- Brutally minimal • Maximalist chaos • Retro-futuristic • Organic/natural
- Luxury/refined • Playful/toy-like • Editorial/magazine • Brutalist/raw
- Art deco/geometric • Soft/pastel • Industrial/utilitarian • Cyberpunk/neon
- Ethereal/glow • Data mesh/tech • Cinematic dark • Memphis design
→ State the chosen tone explicitly and commit to it
3. CONSTRAINTS
- Technical: framework, performance budget, accessibility target
- Business: brand guidelines, legal requirements
- User: device mix, connectivity, disabilities
4. DIFFERENTIATION
What makes this UNFORGETTABLE? The ONE thing someone will remember after closing the tab.
5. INDUSTRY CONTEXT
What industry? What are competitors doing wrong?
→ Look up industry in the Industry Rule Table (Module 7.4) for strategic alignment
1.2 Anti-Pattern Detection Checklist
Before generating code, verify NONE of these are present:
| Red Flag | Why It's Wrong | Corrective Action |
|---|---|---|
| Inter / Roboto / Arial as primary font | Overused by AI, lacks character | Select from 36 curated distinctive pairings |
| Purple gradient on white background | Clichéd AI default | Use dominant color + sharp accent from 48 palettes |
| Predictable card grid without variation | Lacks spatial imagination | Push asymmetry, overlap, diagonal flow, or bento |
| Evenly-distributed color palette | Timid, forgettable | Apply 60-30-10: dominant 60%, secondary 30%, accent 10% |
| Scattered micro-interactions | No orchestration | One well-orchestrated page load with staggered reveals |
| Solid color backgrounds without texture | Lacks atmosphere | Use gradient meshes, noise textures, or geometric patterns |
| Space Grotesk | Overused by AI 2025-2026 | Substitute with Cabinet Grotesk or Satoshi (self-host) |
| Generic stock photography | Signals low effort | Use diagrams, data viz, abstract shapes, or custom illustrations |
| Missing focus states | Accessibility failure | Every interactive element gets :focus-visible |
!important in CSS | Cascade abuse | Use specificity and design tokens |
Math.random() in render | Breaks SSR/hydration | Use deterministic IDs (useId, counters, seeded random) |
Missing prefers-reduced-motion | Accessibility failure | Wrap all motion in media query or hook |
px font sizes | Breaks user zoom preferences | Use rem for all font sizes |
| No keyboard navigation | WCAG 2.2 failure | Arrow keys, Tab, Enter, Escape on all interactive widgets |
| Missing loading states | Perceived performance failure | Skeletons for async, optimistic UI for mutations |
| Fake typing delay | Manipulative, wastes time | Stream real tokens or show instantly |
| "I'm just an AI" disclaimers | Undermines confidence | Show capabilities, not limitations |
| Over-confident wrong answers | Destroys trust | Use uncertainty signals (Module 3 C14) |
1.3 AI-Executable Workflow
Step 1: IDENTIFY
Read user request → Extract: industry, audience, platform, tone
Step 2: MATCH (use Module 7 embedded tables)
Industry Table → Get 2-3 recommended Style IDs
Tone keywords → Select 1 primary Style ID
Style ID → Look up Palette IDs (2-3 options)
Style ID → Look up Font Pairing ID
Step 3: COMMIT
State explicitly: "Using Style [ID], Palette [ID], Font [ID]"
Explain WHY: industry fit + tone match + differentiation
Declare the ONE memorable element
Step 4: CHECK
□ No banned fonts (Inter, Roboto, Arial, Space Grotesk as primary)
□ No purple gradient on white
□ 60-30-10 color distribution
□ Contrast ratio ≥ 4.5:1 for all text (calculate using Module 6.1)
□ Animation respects prefers-reduced-motion
□ Touch targets ≥ 44×44px on mobile
□ Keyboard navigation on all interactive widgets
□ No Math.random() in render paths
□ Server/Client Component boundary correct (Module 3.3)
MODULE 2: Design System Core
2.1 Color System
The 60-30-10 Rule:
- 60% Dominant: Background, large surfaces, primary sections
- 30% Secondary: Cards, sidebars, secondary sections, supporting text
- 10% Accent: CTAs, highlights, active states, key data points
Never use evenly-distributed palettes. A palette with 5 colors at 20% each is timid and forgettable.
Semantic Color Roles:
primary— Main brand color, CTAs, linkssecondary— Supporting elements, tags, secondary buttonsaccent— Highlights, active states, data pointssuccess— Positive feedback, completed stateswarning— Caution, pending stateserror— Errors, destructive actions, critical alertsbackground— Page backgroundsurface— Cards, modals, elevated containerstext— Primary textmuted— Secondary text, placeholders, disabled states
Muted Foundation Colors (from Anthropic Frontend Design):
Start with muted, sophisticated base colors. Reserve saturation for intentional moments. Warm neutrals are preferred for reading interfaces.
OKLCH Color Space:
All 48 palettes now include OKLCH equivalents. OKLCH provides perceptual uniformity and wide-gamut (P3) support that hex cannot. Use OKLCH in color() functions for displays that support it, with hex as fallback.
/* OKLCH usage pattern */
:root {
--color-primary: #2563EB;
--color-primary-oklch: oklch(0.546 0.245 262.88);
}
@supports (color: oklch(0 0 0)) {
:root {
--color-primary: oklch(0.546 0.245 262.88);
}
}
2.2 Typography System
Fluid Typography Formula:
/* Use clamp() for all heading sizes */
--font-hero: clamp(3rem, 8vw, 6rem);
--font-h1: clamp(2.5rem, 5vw, 4rem);
--font-h2: clamp(2rem, 4vw, 3rem);
--font-h3: clamp(1.5rem, 3vw, 2rem);
--font-h4: clamp(1.25rem, 2vw, 1.5rem);
--font-body: clamp(1rem, 1.2vw, 1.125rem);
--font-small: 0.875rem;
/* Line heights */
--leading-tight: 1.1;
--leading-snug: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.7;
/* Letter spacing */
--tracking-tight: -0.02em;
--tracking-normal: 0;
--tracking-wide: 0.01em;
Font Size Base Unit: Use rem for all font sizes. Never use px.
The Paragraph as Object (from Anthropic): Treat paragraphs as visual objects, not just text containers. Use generous paragraph spacing (1.5em between paragraphs, 0.75em after headings). Consider drop caps for opening paragraphs in editorial layouts.
Typographic Color (from Anthropic): Use weight and color to create typographic "color" (visual density), not just size. Light headings (weight 400) paired with grounded body text (weight 400 in secondary color) creates visual hierarchy without size alone.
2.3 Spacing System
Base unit: 4px (0.25rem)
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-20: 5rem; /* 80px */
--space-24: 6rem; /* 96px */
Generous Whitespace Principle (from Anthropic): Default to more whitespace than feels comfortable. Then add 20% more. Dense interfaces signal haste. Generous spacing signals confidence and invites focus. Minimum section padding: clamp(4rem, 10vh, 8rem).
2.4 Shadow & Elevation System
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--shadow-glow: 0 0 20px -5px var(--color-primary-glow);
--shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
Elevation through color, not shadow (from Anthropic): Use subtle depth through background color variation rather than heavy shadows. When shadows are necessary, use colored shadows that match the brand palette.
2.5 Border Radius System
--radius-sm: 0.375rem; /* 6px */
--radius-md: 0.5rem; /* 8px */
--radius-lg: 0.75rem; /* 12px */
--radius-xl: 1rem; /* 16px */
--radius-2xl: 1.5rem; /* 24px */
--radius-full: 9999px;
2.6 Modern CSS Primitives
Container Queries: Component-level responsive design that works regardless of page layout.
.card-grid {
container-type: inline-size;
container-name: cards;
}
@container cards (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
Content-First Breakpoints (from Anthropic): Breakpoints follow content, not devices.
/* Don't use 768px because it's "tablet" — use when the layout breaks */
@container (min-width: 60ch) { /* Content readable */ }
@container (min-width: 90ch) { /* Side-by-side possible */ }
@container (min-width: 120ch) { /* Full layout breathes */ }
CSS Nesting: Universal support since 2023. Use for component-scoped styles.
.card {
padding: var(--space-4);
border-radius: var(--radius-lg);
& .title {
font-weight: 600;
}
&:hover {
transform: translateY(-2px);
}
@container cards (min-width: 400px) {
display: grid;
grid-template-columns: 200px 1fr;
}
}
@starting-style: Animate elements entering the DOM (Chrome 117+, progressive).
.dialog {
transition: opacity 0.3s, transform 0.3s;
opacity: 1;
transform: scale(1);
@starting-style {
opacity: 0;
transform: scale(0.95);
}
}
@layer: Cascade layer management for predictable specificity.
@layer base, components, utilities;
@layer base {
a { color: var(--color-primary); }
}
@layer components {
.btn { /* ... */ }
}
@layer utilities {
.text-primary { color: var(--color-primary) !important; }
}
content-visibility: Render-offscreen optimization for long pages.
.section {
content-visibility: auto;
contain-intrinsic-size: 0 500px; /* Estimated height */
}
Anchor Positioning (Chrome 125+, progressive): Position elements relative to anchors without JavaScript.
.tooltip {
position-anchor: --trigger;
top: anchor(bottom);
left: anchor(center);
translate: -50% 8px;
}
.trigger {
anchor-name: --trigger;
}
View Transitions API (Chrome 126+, progressive): Native page and element transitions.
/* Element transition */
.card {
view-transition-name: card-1;
}
::view-transition-old(card-1) {
animation: fade-out 0.3s ease;
}
::view-transition-new(card-1) {
animation: fade-in 0.3s ease;
}
// Next.js page transition (app/router)
document.startViewTransition(() => {
// Update DOM
});
2.7 Expanded Design Token Schema
Additional token categories (previously missing):
:root {
/* Border Width */
--border-width-1: 1px;
--border-width-2: 2px;
--border-width-4: 4px;
/* Opacity */
--opacity-disabled: 0.5;
--opacity-muted: 0.7;
--opacity-subtle: 0.1;
--opacity-hover: 0.8;
/* Z-Index Scale */
--z-base: 0;
--z-dropdown: 10;
--z-sticky: 20;
--z-overlay: 30;
--z-modal: 40;
--z-popover: 50;
--z-toast: 60;
--z-tooltip: 70;
/* Breakpoints (content-first) */
--bp-sm: 60ch;
--bp-md: 90ch;
--bp-lg: 120ch;
}
MODULE 3: Component & Token System
3.1 Design Token Schema
CSS Variables (Root):
:root {
/* Colors */
--color-primary: #2563EB;
--color-primary-oklch: oklch(0.546 0.245 262.88);
--color-primary-hover: #1D4ED8;
--color-primary-active: #1E40AF;
--color-primary-glow: rgba(37, 99, 235, 0.3);
--color-primary-contrast: #FFFFFF;
--color-secondary: #7C3AED;
--color-secondary-hover: #6D28D9;
--color-accent: #06B6D4;
--color-success: #10B981;
--color-warning: #F59E0B;
--color-error: #EF4444;
--color-background: #0F172A;
--color-surface: #1E293B;
--color-surface-elevated: #334155;
--color-text: #F8FAFC;
--color-text-muted: #94A3B8;
--color-border: #334155;
/* Typography — FIXED: Fonts not on Google Fonts marked as self-host */
--font-display: 'Clash Display', system-ui, sans-serif; /* SELF-HOST: Not on Google Fonts */
--font-body: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
/* Spacing, Shadows, Radius (from Module 2) */
--shadow-primary: 0 4px 14px 0 rgba(37, 99, 235, 0.39);
/* Animation */
--duration-fast: 150ms;
--duration-normal: 200ms;
--duration-slow: 300ms;
--duration-deliberate: 600ms;
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
}
Self-Hosted Font Note: The following fonts are NOT available on Google Fonts and must be self-hosted or substituted:
- Clash Display → Substitute:
Outfit(Google Fonts) or self-host from fontshare.com - Cabinet Grotesk → Substitute:
Outfit(Google Fonts) or self-host from fontshare.com - Satoshi → Substitute:
Plus Jakarta Sans(Google Fonts) or self-host from fontshare.com - Fraunces → Available on Google Fonts ✓
- Bricolage Grotesque → Available on Google Fonts ✓
Token JSON Schema:
{
"colors": {
"primary": { "value": "#2563EB", "oklch": "oklch(0.546 0.245 262.88)", "type": "color" },
"primaryHover": { "value": "#1D4ED8", "type": "color" },
"primaryContrast": { "value": "#FFFFFF", "type": "color" }
},
"typography": {
"fontFamily": { "display": "Clash Display", "body": "Plus Jakarta Sans" },
"fontSize": { "hero": "clamp(3rem, 8vw, 6rem)", "h1": "clamp(2.5rem, 5vw, 4rem)" }
},
"spacing": { "unit": "0.25rem", "scale": [1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24] },
"borders": { "width1": "1px", "width2": "2px", "width4": "4px" },
"opacity": { "disabled": 0.5, "muted": 0.7, "subtle": 0.1 },
"zIndex": { "base": 0, "dropdown": 10, "sticky": 20, "overlay": 30, "modal": 40, "popover": 50, "toast": 60, "tooltip": 70 },
"shadows": { "sm": "0 1px 2px 0 rgb(0 0 0 / 0.05)", "md": "0 4px 6px -1px rgb(0 0 0 / 0.1)" },
"radii": { "sm": "0.375rem", "md": "0.5rem", "lg": "0.75rem", "full": "9999px" }
}
3.2 Server vs Client Components (from Vercel React Best Practices)
The 80/20 Rule: 80% of your components should be Server Components. Only use Client Components for interactivity.
// SERVER COMPONENT (default — no directive)
// Can: fetch data, access backend, keep bundle small
// Cannot: use hooks, browser APIs, event handlers
async function ProductList() {
const products = await db.products.findMany();
return (
<ul>
{products.map(p => <ProductCard key={p.id} product={p} />)}
</ul>
);
}
// CLIENT COMPONENT
// Can: use hooks, browser APIs, event handlers
// Cannot: be async, access backend directly
'use client';
function ProductCard({ product }: { product: Product }) {
const [isHovered, setIsHovered] = useState(false);
return (
<li
onMouseEnter={() => setIsHovered(true)}
className={isHovered ? 'elevated' : ''}
>
{product.name}
</li>
);
}
All 14 components in this skill are Client Components (they use hooks, state, event handlers). They must be marked 'use client' or wrapped in Client Component boundaries.
3.3 Component Library (14 Components)
All components use the token system, are fully typed, include all states, and follow React 19 patterns (no forwardRef needed for React 19+).
C01: Button
// components/Button.tsx
'use client';
import { cn } from '@/lib/utils';
import { cva, type VariantProps } from 'class-variance-authority';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-lg font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-[var(--color-primary)] text-[var(--color-primary-contrast)] hover:bg-[var(--color-primary-hover)] hover:shadow-[var(--shadow-primary)] hover:-translate-y-0.5 active:translate-y-0 active:shadow-sm',
destructive: 'bg-[var(--color-error)] text-white hover:bg-red-700',
outline: 'border-2 border-[var(--color-border)] bg-transparent hover:bg-[var(--color-surface)] hover:border-[var(--color-text-muted)]',
secondary: 'bg-[var(--color-surface)] text-[var(--color-text)] hover:bg-[var(--color-surface-elevated)]',
ghost: 'hover:bg-[var(--color-surface)] hover:text-[var(--color-text)]',
link: 'text-[var(--color-primary)] underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2 text-sm',
sm: 'h-9 px-3 text-xs',
lg: 'h-12 px-8 text-base',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
isLoading?: boolean;
}
// React 19: forwardRef no longer required — ref is a regular prop
export function Button({ className, variant, size, isLoading, children, ref, ...props }: ButtonProps) {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
disabled={props.disabled || isLoading}
{...props}
>
{isLoading && (
<svg className="mr-2 h-4 w-4 animate-spin" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
)}
{children}
</button>
);
}
C02: Input (with Zod validation support)
// components/Input.tsx
'use client';
import { useId } from 'react';
import { cn } from '@/lib/utils';
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
hint?: string;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
}
export function Input({ className, label, error, hint, leftIcon, rightIcon, ref, ...props }: InputProps) {
const id = useId();
const errorId = `${id}-error`;
const hintId = `${id}-hint`;
return (
<div className="w-full">
{label && (
<label htmlFor={id} className="mb-1.5 block text-sm font-medium text-[var(--color-text)]">
{label}
{props.required && <span className="ml-1 text-[var(--color-error)]" aria-hidden="true">*</span>}
</label>
)}
{hint && !error && (
<p id={hintId} className="mb-1.5 text-xs text-[var(--color-text-muted)]">{hint}</p>
)}
<div className="relative">
{leftIcon && (
<div className="absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]">
{leftIcon}
</div>
)}
<input
id={id}
ref={ref}
aria-invalid={!!error}
aria-describedby={cn(error && errorId, hint && !error && hintId) || undefined}
className={cn(
'flex h-10 w-full rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] px-3 py-2 text-sm text-[var(--color-text)] placeholder:text-[var(--color-text-muted)]',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-background)]',
'disabled:cursor-not-allowed disabled:opacity-50',
leftIcon && 'pl-10',
rightIcon && 'pr-10',
error && 'border-[var(--color-error)] focus-visible:ring-[var(--color-error)]',
className
)}
{...props}
/>
{rightIcon && (
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]">
{rightIcon}
</div>
)}
</div>
{error && <p id={errorId} className="mt-1.5 text-xs text-[var(--color-error)]" role="alert">{error}</p>}
</div>
);
}
C03: Card
// components/Card.tsx
import { cn } from '@/lib/utils';
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
variant?: 'default' | 'feature' | 'pricing' | 'testimonial';
padding?: 'none' | 'sm' | 'md' | 'lg';
}
export const Card = ({ className, variant = 'default', padding = 'md', children, ...props }: CardProps) => {
const variants = {
default: 'bg-[var(--color-surface)] border border-[var(--color-border)]',
feature: 'bg-[var(--color-surface)] border border-[var(--color-primary)]/20 shadow-[var(--shadow-lg)]',
pricing: 'bg-[var(--color-surface)] border-2 border-[var(--color-primary)] relative overflow-hidden',
testimonial: 'bg-[var(--color-surface)] border border-[var(--color-border)]',
};
const paddings = {
none: '',
sm: 'p-4',
md: 'p-6',
lg: 'p-8',
};
return (
<div
className={cn(
'rounded-xl transition-all duration-200',
variants[variant],
paddings[padding],
className
)}
{...props}
>
{variant === 'pricing' && (
<div className="absolute top-0 right-0 bg-[var(--color-primary)] text-[var(--color-primary-contrast)] text-xs font-bold px-3 py-1 rounded-bl-lg">
POPULAR
</div>
)}
{children}
</div>
);
};
C04: Modal/Dialog (FIXED: focus trap, aria-describedby, inert)
// components/Modal.tsx
'use client';
import { useEffect, useRef, useCallback } from 'react';
import { cn } from '@/lib/utils';
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title?: string;
description?: string;
children: React.ReactNode;
footer?: React.ReactNode;
size?: 'sm' | 'md' | 'lg' | 'xl';
}
export const Modal = ({ isOpen, onClose, title, description, children, footer, size = 'md' }: ModalProps) => {
const overlayRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
// Focus trap
const handleTabKey = useCallback((e: KeyboardEvent) => {
if (e.key !== 'Tab' || !contentRef.current) return;
const focusable = contentRef.current.querySelectorAll<HTMLElement>(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey) {
if (document.activeElement === first) {
e.preventDefault();
last?.focus();
}
} else {
if (document.activeElement === last) {
e.preventDefault();
first?.focus();
}
}
}, []);
useEffect(() => {
if (!isOpen) return;
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
const handleClickOutside = (e: MouseEvent) => {
if (e.target === overlayRef.current) onClose();
};
document.addEventListener('keydown', handleEscape);
document.addEventListener('keydown', handleTabKey);
document.addEventListener('mousedown', handleClickOutside);
document.body.style.overflow = 'hidden';
// Focus the modal content
contentRef.current?.focus();
// Mark rest of page as inert (screen readers skip it)
const root = document.getElementById('root');
if (root) root.setAttribute('aria-hidden', 'true');
return () => {
document.removeEventListener('keydown', handleEscape);
document.removeEventListener('keydown', handleTabKey);
document.removeEventListener('mousedown', handleClickOutside);
document.body.style.overflow = '';
if (root) root.removeAttribute('aria-hidden');
};
}, [isOpen, onClose, handleTabKey]);
const sizes = {
sm: 'max-w-md',
md: 'max-w-lg',
lg: 'max-w-2xl',
xl: 'max-w-4xl',
};
if (!isOpen) return null;
return (
<div
ref={overlayRef}
className="fixed inset-0 z-[var(--z-modal)] flex items-center justify-center bg-black/60 backdrop-blur-sm animate-fade-in"
role="dialog"
aria-modal="true"
aria-labelledby={title ? 'modal-title' : undefined}
aria-describedby={description ? 'modal-description' : undefined}
>
<div
ref={contentRef}
tabIndex={-1}
className={cn(
'w-full mx-4 bg-[var(--color-surface)] border border-[var(--color-border)] rounded-2xl shadow-[var(--shadow-xl)] animate-scale-in',
sizes[size]
)}
>
{(title || description) && (
<div className="px-6 pt-6 pb-2">
{title && <h2 id="modal-title" className="text-xl font-semibold text-[var(--color-text)]">{title}</h2>}
{description && <p id="modal-description" className="mt-1 text-sm text-[var(--color-text-muted)]">{description}</p>}
</div>
)}
<div className="px-6 py-4">{children}</div>
{footer && <div className="px-6 pb-6 pt-2 flex justify-end gap-3">{footer}</div>}
</div>
</div>
);
};
C05: Navbar (with skip link)
// components/Navbar.tsx
'use client';
import { useState } from 'react';
import { cn } from '@/lib/utils';
import { Button } from './Button';
interface NavItem {
label: string;
href: string;
}
interface NavbarProps {
logo: React.ReactNode;
items: NavItem[];
variant?: 'default' | 'transparent' | 'sticky';
cta?: { label: string; onClick: () => void };
}
export const Navbar = ({ logo, items, variant = 'sticky', cta }: NavbarProps) => {
const [isOpen, setIsOpen] = useState(false);
return (
<>
{/* Skip link — accessibility requirement */}
<a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:top-0 focus:left-0 focus:z-[100] focus:bg-[var(--color-text)] focus:text-[var(--color-background)] focus:px-4 focus:py-2">
Skip to main content
</a>
<nav
aria-label="Main navigation"
className={cn(
'w-full z-[var(--z-sticky)] transition-all duration-300',
variant === 'sticky' && 'sticky top-0 bg-[var(--color-background)]/80 backdrop-blur-md border-b border-[var(--color-border)]',
variant === 'transparent' && 'absolute top-0 bg-transparent',
variant === 'default' && 'bg-[var(--color-background)] border-b border-[var(--color-border)]'
)}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex-shrink-0">{logo}</div>
<div className="hidden md:flex items-center gap-8">
{items.map((item) => (
<a key={item.href} href={item.href} className="text-sm font-medium text-[var(--color-text-muted)] hover:text-[var(--color-text)] transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 rounded-sm">
{item.label}
</a>
))}
{cta && <Button size="sm" onClick={cta.onClick}>{cta.label}</Button>}
</div>
<button
className="md:hidden p-2 text-[var(--color-text)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] rounded-sm"
onClick={() => setIsOpen(!isOpen)}
aria-label="Toggle menu"
aria-expanded={isOpen}
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{isOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
</button>
</div>
{isOpen && (
<div className="md:hidden pb-4 animate-slide-down">
{items.map((item) => (
<a key={item.href} href={item.href} className="block py-2 text-sm font-medium text-[var(--color-text-muted)] hover:text-[var(--color-text)]" onClick={() => setIsOpen(false)}>
{item.label}
</a>
))}
{cta && <Button className="mt-4 w-full" onClick={cta.onClick}>{cta.label}</Button>}
</div>
)}
</div>
</nav>
</>
);
};
C06: Badge
// components/Badge.tsx
import { cn } from '@/lib/utils';
interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
variant?: 'default' | 'secondary' | 'success' | 'warning' | 'error' | 'outline';
size?: 'sm' | 'md';
dot?: boolean;
}
export const Badge = ({ className, variant = 'default', size = 'md', dot, children, ...props }: BadgeProps) => {
const variants = {
default: 'bg-[var(--color-primary)]/10 text-[var(--color-primary)] border-[var(--color-primary)]/20',
secondary: 'bg-[var(--color-surface-elevated)] text-[var(--color-text-muted)] border-[var(--color-border)]',
success: 'bg-[var(--color-success)]/10 text-[var(--color-success)] border-[var(--color-success)]/20',
warning: 'bg-[var(--color-warning)]/10 text-[var(--color-warning)] border-[var(--color-warning)]/20',
error: 'bg-[var(--color-error)]/10 text-[var(--color-error)] border-[var(--color-error)]/20',
outline: 'bg-transparent text-[var(--color-text-muted)] border-[var(--color-border)]',
};
return (
<span
className={cn(
'inline-flex items-center gap-1.5 rounded-full border font-medium transition-colors',
size === 'sm' && 'px-2 py-0.5 text-xs',
size === 'md' && 'px-2.5 py-0.5 text-xs',
variants[variant],
className
)}
{...props}
>
{dot && <span className={cn('w-1.5 h-1.5 rounded-full', variant === 'success' && 'bg-[var(--color-success)]')} />}
{children}
</span>
);
};
C07: Toast
// components/Toast.tsx
'use client';
import { useEffect, useState } from 'react';
import { cn } from '@/lib/utils';
interface ToastProps {
id: string;
message: string;
variant?: 'success' | 'error' | 'warning' | 'info';
duration?: number;
onDismiss: (id: string) => void;
action?: { label: string; onClick: () => void };
}
export const Toast = ({ id, message, variant = 'info', duration = 5000, onDismiss, action }: ToastProps) => {
const [progress, setProgress] = useState(100);
const [isExiting, setIsExiting] = useState(false);
useEffect(() => {
const startTime = Date.now();
const interval = setInterval(() => {
const elapsed = Date.now() - startTime;
const remaining = Math.max(0, 100 - (elapsed / duration) * 100);
setProgress(remaining);
if (remaining <= 0) {
clearInterval(interval);
handleDismiss();
}
}, 50);
return () => clearInterval(interval);
}, [duration]);
const handleDismiss = () => {
setIsExiting(true);
setTimeout(() => onDismiss(id), 300);
};
const variants = {
success: 'border-[var(--color-success)]/30 bg-[var(--color-success)]/10 text-[var(--color-success)]',
error: 'border-[var(--color-error)]/30 bg-[var(--color-error)]/10 text-[var(--color-error)]',
warning: 'border-[var(--color-warning)]/30 bg-[var(--color-warning)]/10 text-[var(--color-warning)]',
info: 'border-[var(--color-primary)]/30 bg-[var(--color-primary)]/10 text-[var(--color-primary)]',
};
return (
<div
className={cn(
'relative w-full max-w-sm rounded-lg border p-4 shadow-[var(--shadow-lg)] animate-slide-in-right',
isExiting && 'animate-slide-out-right',
variants[variant]
)}
role="alert"
aria-live="polite"
>
<div className="flex items-start justify-between gap-3">
<p className="text-sm font-medium">{message}</p>
<button onClick={handleDismiss} className="shrink-0 opacity-70 hover:opacity-100 transition-opacity" aria-label="Dismiss notification">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
{action && (
<button onClick={() => { action.onClick(); handleDismiss(); }} className="mt-2 text-xs font-semibold underline underline-offset-2 hover:opacity-80">
{action.label}
</button>
)}
<div className="absolute bottom-0 left-0 h-0.5 bg-current opacity-30 transition-all duration-100" style={{ width: `${progress}%` }} />
</div>
);
};
C08: Skeleton (FIXED: No Math.random())
// components/Skeleton.tsx
import { cn } from '@/lib/utils';
interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
variant?: 'text' | 'card' | 'chart' | 'avatar';
lines?: number;
}
// Deterministic pseudo-random based on index — SSR-safe
function deterministicHeight(index: number, min: number, max: number): number {
const x = Math.sin(index * 12.9898 + 78.233) * 43758.5453;
const t = x - Math.floor(x);
return min + t * (max - min);
}
export const Skeleton = ({ className, variant = 'text', lines = 3, ...props }: SkeletonProps) => {
const base = 'animate-pulse rounded-md bg-[var(--color-surface-elevated)]';
if (variant === 'text') {
return (
<div className={cn('space-y-2', className)} {...props}>
{Array.from({ length: lines }).map((_, i) => (
<div key={i} className={cn(base, i === lines - 1 ? 'w-3/4' : 'w-full')} style={{ height: '1rem' }} />
))}
</div>
);
}
if (variant === 'card') {
return (
<div className={cn('rounded-xl border border-[var(--color-border)] p-6 space-y-4', className)} {...props}>
<div className={cn(base, 'h-8 w-1/3')} />
<div className={cn(base, 'h-24 w-full')} />
<div className="flex gap-3">
<div className={cn(base, 'h-10 w-24')} />
<div className={cn(base, 'h-10 w-24')} />
</div>
</div>
);
}
if (variant === 'avatar') {
return <div className={cn(base, 'rounded-full', className)} style={{ width: '2.5rem', height: '2.5rem' }} {...props} />;
}
if (variant === 'chart') {
return (
<div className={cn('rounded-xl border border-[var(--color-border)] p-6', className)} {...props}>
<div className={cn(base, 'h-6 w-1/4 mb-6')} />
<div className="flex items-end gap-2 h-48">
{Array.from({ length: 8 }).map((_, i) => (
<div key={i} className={cn(base, 'flex-1')} style={{ height: `${deterministicHeight(i, 30, 90)}%` }} />
))}
</div>
</div>
);
}
return null;
};
C09: Tabs (FIXED: Full keyboard navigation)
// components/Tabs.tsx
'use client';
import { useState, useRef, useId } from 'react';
import { cn } from '@/lib/utils';
interface Tab {
id: string;
label: string;
content: React.ReactNode;
}
interface TabsProps {
tabs: Tab[];
variant?: 'default' | 'pill' | 'vertical';
defaultTab?: string;
}
export const Tabs = ({ tabs, variant = 'default', defaultTab }: TabsProps) => {
const [activeTab, setActiveTab] = useState(defaultTab || tabs[0]?.id);
const tabRefs = useRef<(HTMLButtonElement | null)[]>([]);
const baseId = useId();
const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
let newIndex = index;
switch (e.key) {
case 'ArrowRight':
case 'ArrowDown':
newIndex = (index + 1) % tabs.length;
break;
case 'ArrowLeft':
case 'ArrowUp':
newIndex = (index - 1 + tabs.length) % tabs.length;
break;
case 'Home':
newIndex = 0;
break;
case 'End':
newIndex = tabs.length - 1;
break;
default:
return;
}
e.preventDefault();
tabRefs.current[newIndex]?.focus();
setActiveTab(tabs[newIndex].id);
};
if (variant === 'vertical') {
return (
<div className="flex gap-6">
<div className="flex flex-col gap-1 min-w-[160px]" role="tablist" aria-orientation="vertical">
{tabs.map((tab, index) => (
<button
key={tab.id}
ref={(el) => { tabRefs.current[index] = el; }}
role="tab"
id={`${baseId}-tab-${tab.id}`}
aria-selected={activeTab === tab.id}
aria-controls={`${baseId}-panel-${tab.id}`}
tabIndex={activeTab === tab.id ? 0 : -1}
onClick={() => setActiveTab(tab.id)}
onKeyDown={(e) => handleKeyDown(e, index)}
className={cn(
'text-left px-4 py-2.5 rounded-lg text-sm font-medium transition-all',
activeTab === tab.id
? 'bg-[var(--color-primary)]/10 text-[var(--color-primary)]'
: 'text-[var(--color-text-muted)] hover:text-[var(--color-text)] hover:bg-[var(--color-surface)]'
)}
>
{tab.label}
</button>
))}
</div>
<div className="flex-1">
{tabs.map((tab) => (
<div
key={tab.id}
role="tabpanel"
id={`${baseId}-panel-${tab.id}`}
aria-labelledby={`${baseId}-tab-${tab.id}`}
hidden={activeTab !== tab.id}
>
{activeTab === tab.id && tab.content}
</div>
))}
</div>
</div>
);
}
return (
<div>
<div
role="tablist"
aria-orientation="horizontal"
className={cn(
'flex gap-1',
variant === 'default' && 'border-b border-[var(--color-border)]',
variant === 'pill' && 'bg-[var(--color-surface)] p-1 rounded-lg'
)}
>
{tabs.map((tab, index) => (
<button
key={tab.id}
ref={(el) => { tabRefs.current[index] = el; }}
role="tab"
id={`${baseId}-tab-${tab.id}`}
aria-selected={activeTab === tab.id}
aria-controls={`${baseId}-panel-${tab.id}`}
tabIndex={activeTab === tab.id ? 0 : -1}
onClick={() => setActiveTab(tab.id)}
onKeyDown={(e) => handleKeyDown(e, index)}
className={cn(
'px-4 py-2 text-sm font-medium transition-all relative',
variant === 'default' && 'rounded-t-lg',
variant === 'pill' && 'rounded-md',
activeTab === tab.id
? variant === 'default'
? 'text-[var(--color-primary)]'
: 'bg-[var(--color-background)] text-[var(--color-text)] shadow-sm'
: 'text-[var(--color-text-muted)] hover:text-[var(--color-text)]'
)}
>
{tab.label}
{variant === 'default' && activeTab === tab.id && (
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-[var(--color-primary)] rounded-t-full" />
)}
</button>
))}
</div>
<div className="mt-4">
{tabs.map((tab) => (
<div
key={tab.id}
role="tabpanel"
id={`${baseId}-panel-${tab.id}`}
aria-labelledby={`${baseId}-tab-${tab.id}`}
hidden={activeTab !== tab.id}
>
{activeTab === tab.id && tab.content}
</div>
))}
</div>
</div>
);
};
C10: Accordion (FIXED: Full keyboard navigation)
// components/Accordion.tsx
'use client';
import { useState, useRef, useId } from 'react';
import { cn } from '@/lib/utils';
interface AccordionItem {
id: string;
title: string;
content: React.ReactNode;
}
interface AccordionProps {
items: AccordionItem[];
allowMultiple?: boolean;
}
export const Accordion = ({ items, allowMultiple = false }: AccordionProps) => {
const [openItems, setOpenItems] = useState<string[]>([]);
const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);
const baseId = useId();
const toggleItem = (id: string) => {
if (allowMultiple) {
setOpenItems((prev) => (prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id]));
} else {
setOpenItems((prev) => (prev.includes(id) ? [] : [id]));
}
};
const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
let newIndex = index;
switch (e.key) {
case 'ArrowDown':
newIndex = (index + 1) % items.length;
break;
case 'ArrowUp':
newIndex = (index - 1 + items.length) % items.length;
break;
case 'Home':
newIndex = 0;
break;
case 'End':
newIndex = items.length - 1;
break;
default:
return;
}
e.preventDefault();
buttonRefs.current[newIndex]?.focus();
};
return (
<div className="divide-y divide-[var(--color-border)] border border-[var(--color-border)] rounded-xl overflow-hidden">
{items.map((item, index) => {
const isOpen = openItems.includes(item.id);
return (
<div key={item.id} className="bg-[var(--color-surface)]">
<h3>
<button
ref={(el) => { buttonRefs.current[index] = el; }}
onClick={() => toggleItem(item.id)}
onKeyDown={(e) => handleKeyDown(e, index)}
className="flex w-full items-center justify-between px-6 py-4 text-left transition-colors hover:bg-[var(--color-surface-elevated)]/50"
aria-expanded={isOpen}
aria-controls={`${baseId}-panel-${item.id}`}
id={`${baseId}-button-${item.id}`}
>
<span className="text-sm font-medium text-[var(--color-text)]">{item.title}</span>
<svg
className={cn('w-5 h-5 text-[var(--color-text-muted)] transition-transform duration-200', isOpen && 'rotate-180')}
fill="none" stroke="currentColor" viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
</h3>
<div
id={`${baseId}-panel-${item.id}`}
role="region"
aria-labelledby={`${baseId}-button-${item.id}`}
className={cn(
'overflow-hidden transition-all duration-300',
isOpen ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
)}
>
<div className="px-6 pb-4 text-sm text-[var(--color-text-muted)]">{item.content}</div>
</div>
</div>
);
})}
</div>
);
};
C11: Table
// components/Table.tsx
'use client';
import { useState } from 'react';
import { cn } from '@/lib/utils';
interface Column<T> {
key: keyof T | string;
header: string;
render?: (row: T) => React.ReactNode;
sortable?: boolean;
}
interface TableProps<T> {
data: T[];
columns: Column<T>[];
className?: string;
onRowClick?: (row: T) => void;
}
export function Table<T extends Record<string, any>>({ data, columns, className, onRowClick }: TableProps<T>) {
const [sortKey, setSortKey] = useState<string | null>(null);
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');
const sortedData = [...data].sort((a, b) => {
if (!sortKey) return 0;
const aVal = a[sortKey];
const bVal = b[sortKey];
if (aVal < bVal) return sortDir === 'asc' ? -1 : 1;
if (aVal > bVal) return sortDir === 'asc' ? 1 : -1;
return 0;
});
const handleSort = (key: string) => {
if (sortKey === key) {
setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'));
} else {
setSortKey(key);
setSortDir('asc');
}
};
return (
<div className={cn('overflow-x-auto rounded-xl border border-[var(--color-border)]', className)}>
<table className="w-full text-sm text-left">
<thead className="bg-[var(--color-surface-elevated)] text-[var(--color-text-muted)] uppercase text-xs">
<tr>
{columns.map((col) => (
<th
key={String(col.key)}
className={cn('px-6 py-3 font-semibold', col.sortable && 'cursor-pointer hover:text-[var(--color-text)]')}
onClick={() => col.sortable && handleSort(String(col.key))}
aria-sort={sortKey === String(col.key) ? (sortDir === 'asc' ? 'ascending' : 'descending') : undefined}
>
<div className="flex items-center gap-1">
{col.header}
{col.sortable && sortKey === col.key && (
<span className="text-[var(--color-primary)]" aria-hidden="true">{sortDir === 'asc' ? '↑' : '↓'}</span>
)}
</div>
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-[var(--color-border)]">
{sortedData.map((row, i) => (
<tr
key={row.id}
className={cn(
'bg-[var(--color-surface)] transition-colors',
onRowClick && 'cursor-pointer hover:bg-[var(--color-surface-elevated)]/50'
)}
onClick={() => onRowClick?.(row)}
>
{columns.map((col) => (
<td key={String(col.key)} className="px-6 py-4 text-[var(--color-text)]">
{col.render ? col.render(row) : row[col.key]}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
C12: Stat Card
// components/StatCard.tsx
import { cn } from '@/lib/utils';
interface StatCardProps {
label: string;
value: string | number;
trend?: { value: number; isPositive: boolean };
icon?: React.ReactNode;
className?: string;
}
export const StatCard = ({ label, value, trend, icon, className }: StatCardProps) => {
return (
<div className={cn('rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] p-6', className)}>
<div className="flex items-start justify-between">
<div>
<p className="text-sm font-medium text-[var(--color-text-muted)]">{label}</p>
<p className="mt-2 text-3xl font-bold tracking-tight text-[var(--color-text)]">{value}</p>
{trend && (
<div className="mt-2 flex items-center gap-1 text-sm">
<span className={trend.isPositive ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'}>
{trend.isPositive ? '↑' : '↓'} {Math.abs(trend.value)}%
</span>
<span className="text-[var(--color-text-muted)]">vs last period</span>
</div>
)}
</div>
{icon && (
<div className="rounded-lg bg-[var(--color-primary)]/10 p-3 text-[var(--color-primary)]">
{icon}
</div>
)}
</div>
</div>
);
};
C13: Thinking Indicator (from Anthropic Frontend Design)
// components/ThinkingIndicator.tsx
'use client';
export const ThinkingIndicator = () => {
return (
<div className="flex items-center gap-3 py-4 text-[var(--color-text-muted)]" role="status" aria-label="AI is thinking">
<div className="flex gap-1">
<span className="w-2 h-2 rounded-full bg-[var(--color-accent)] animate-bounce" style={{ animationDelay: '0ms' }} />
<span className="w-2 h-2 rounded-full bg-[var(--color-accent)] animate-bounce" style={{ animationDelay: '150ms' }} />
<span className="w-2 h-2 rounded-full bg-[var(--color-accent)] animate-bounce" style={{ animationDelay: '300ms' }} />
</div>
<span className="text-sm">Thinking...</span>
</div>
);
};
C14: Uncertainty Notice (from Anthropic Frontend Design)
// components/UncertaintyNotice.tsx
interface UncertaintyNoticeProps {
confidence: 'high' | 'medium' | 'low';
}
export const UncertaintyNotice = ({ confidence }: UncertaintyNoticeProps) => {
const styles = {
high: { bg: '#F0FDF4', border: '#86EFAC', text: '#166534' },
medium: { bg: '#FFFBEB', border: '#FCD34D', text: '#92400E' },
low: { bg: '#FEF2F2', border: '#FCA5A5', text: '#991B1B' },
};
const messages = {
low: 'This response may contain inaccuracies. Please verify important information.',
medium: 'This response is based on general knowledge.',
high: 'This response is based on verified sources.',
};
const s = styles[confidence];
return (
<div className="flex items-start gap-2 p-3 rounded-lg text-sm" style={{ background: s.bg, border: `1px solid ${s.border}`, color: s.text }} role="note">
<svg className="w-4 h-4 shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<div>
<p className="font-medium">Confidence: {confidence}</p>
<p className="text-xs opacity-80 mt-0.5">{messages[confidence]}</p>
</div>
</div>
);
};
MODULE 4: Motion & Interaction Engine
4.1 CSS Animation Presets (24)
All animations include prefers-reduced-motion fallbacks.
/* ============================================
ENTRANCE ANIMATIONS (8)
============================================ */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideDown {
from { opacity: 0; transform: translateY(-30px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideLeft {
from { opacity: 0; transform: translateX(30px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideRight {
from { opacity: 0; transform: translateX(-30px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.9); }
to { opacity: 1; transform: scale(1); }
}
@keyframes blurIn {
from { opacity: 0; filter: blur(10px); }
to { opacity: 1; filter: blur(0); }
}
@keyframes flipIn {
from { opacity: 0; transform: perspective(400px) rotateX(90deg); }
to { opacity: 1; transform: perspective(400px) rotateX(0); }
}
/* ============================================
EMPHASIS ANIMATIONS (4)
============================================ */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
@keyframes glow {
0%, 100% { box-shadow: 0 0 5px var(--color-primary-glow); }
50% { box-shadow: 0 0 20px var(--color-primary-glow), 0 0 40px var(--color-primary-glow); }
}
/* ============================================
EXIT ANIMATIONS (4)
============================================ */
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes slideOutRight {
from { opacity: 1; transform: translateX(0); }
to { opacity: 0; transform: translateX(100%); }
}
@keyframes scaleOut {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(0.9); }
}
@keyframes blurOut {
from { opacity: 1; filter: blur(0); }
to { opacity: 0; filter: blur(10px); }
}
/* ============================================
SPECIAL ANIMATIONS (4)
============================================ */
@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes particleFloat {
0%, 100% { transform: translateY(0) translateX(0); }
33% { transform: translateY(-20px) translateX(10px); }
66% { transform: translateY(10px) translateX(-10px); }
}
@keyframes borderGlow {
0%, 100% { border-color: var(--color-border); }
50% { border-color: var(--color-primary); }
}
@keyframes textReveal {
from { clip-path: inset(0 100% 0 0); }
to { clip-path: inset(0 0 0 0); }
}
/* ============================================
STREAMING & AI ANIMATIONS (4 — NEW)
============================================ */
@keyframes cursorBlink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
@keyframes dotPulse {
0%, 80%, 100% { transform: scale(0); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
@keyframes streamReveal {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
/* ============================================
UTILITY CLASSES
============================================ */
.animate-fade-in { animation: fadeIn 0.6s var(--ease-out) forwards; }
.animate-slide-up { animation: slideUp 0.6s var(--ease-expo-out) forwards; }
.animate-slide-down { animation: slideDown 0.4s var(--ease-out) forwards; }
.animate-scale-in { animation: scaleIn 0.5s var(--ease-spring) forwards; }
.animate-blur-in { animation: blurIn 0.8s var(--ease-out) forwards; }
.animate-flip-in { animation: flipIn 0.7s var(--ease-out) forwards; }
.animate-pulse-soft { animation: pulse 2s ease-in-out infinite; }
.animate-bounce-soft { animation: bounce 2s ease-in-out infinite; }
.animate-glow { animation: glow 2s ease-in-out infinite; }
.animate-gradient { background-size: 200% 200%; animation: gradientShift 8s ease infinite; }
.animate-text-reveal { animation: textReveal 0.8s var(--ease-expo-out) forwards; }
.animate-stream-cursor { animation: cursorBlink 1s step-end infinite; }
.animate-shimmer { background: linear-gradient(90deg, transparent, rgba(255,255,255,0.08), transparent); background-size: 200% 100%; animation: shimmer 2s infinite; }
.animate-dot-pulse { animation: dotPulse 1.4s ease-in-out infinite; }
.animate-stream-reveal { animation: streamReveal var(--duration-fast) var(--ease-smooth) forwards; }
/* Stagger delays */
.animation-delay-100 { animation-delay: 100ms; }
.animation-delay-200 { animation-delay: 200ms; }
.animation-delay-300 { animation-delay: 300ms; }
.animation-delay-400 { animation-delay: 400ms; }
.animation-delay-500 { animation-delay: 500ms; }
.animation-delay-600 { animation-delay: 600ms; }
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
4.2 Easing Functions
:root {
--ease-linear: linear;
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
--ease-elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6);
--ease-smooth: cubic-bezier(0.45, 0.05, 0.55, 0.95);
--ease-dramatic: cubic-bezier(0.87, 0, 0.13, 1);
--ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);
}
4.3 Motion as Thought Philosophy (from Anthropic)
Rule: Motion should feel like thought, not entertainment.
| Good Motion | Bad Motion |
|---|---|
| Fade in (content appearing) | Bounce in (playful, distracting) |
| Subtle scale on press | Shake on error (aggressive) |
| Smooth height transitions | Jarring layout shifts |
| Cursor blink (human) | Spinning loaders (machine) |
| Staggered list reveals | Everything animating at once |
| Streaming text reveal | Fake typing delay |
4.4 Scroll-Triggered Animation Hook
// hooks/useScrollAnimation.ts
'use client';
import { useEffect, useRef, useState } from 'react';
export function useScrollAnimation(threshold = 0.1, once = true) {
const ref = useRef<HTMLDivElement>(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const element = ref.current;
if (!element) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
if (once) observer.disconnect();
} else if (!once) {
setIsVisible(false);
}
},
{ threshold }
);
observer.observe(element);
return () => observer.disconnect();
}, [threshold, once]);
return { ref, isVisible };
}
4.5 Reduced Motion Hook
// hooks/useReducedMotion.ts
'use client';
import { useState, useEffect } from 'react';
export function useReducedMotion(): boolean {
const [prefersReduced, setPrefersReduced] = useState(false);
useEffect(() => {
const media = window.matchMedia('(prefers-reduced-motion: reduce)');
setPrefersReduced(media.matches);
const listener = (e: MediaQueryListEvent) => setPrefersReduced(e.matches);
media.addEventListener('change', listener);
return () => media.removeEventListener('change', listener);
}, []);
return prefersReduced;
}
// Usage
const reduced = useReducedMotion();
const duration = reduced ? 0 : 600;
4.6 GSAP Integration Patterns (cross-reference: GSAP Animations Skill)
When CSS animations are insufficient, use GSAP for complex timelines, ScrollTrigger, and React integration.
useGSAP Hook Pattern:
// components/animated-section.tsx
'use client';
import { useRef } from 'react';
import { useGSAP } from '@gsap/react';
import { gsap } from 'gsap';
export function AnimatedSection() {
const containerRef = useRef<HTMLDivElement>(null);
useGSAP(() => {
gsap.from('.animate-item', {
y: 50,
opacity: 0,
stagger: 0.1,
scrollTrigger: {
trigger: containerRef.current,
start: 'top 80%',
},
});
}, { scope: containerRef });
return (
<div ref={containerRef}>
<div className="animate-item">Item 1</div>
<div className="animate-item">Item 2</div>
<div className="animate-item">Item 3</div>
</div>
);
}
GSAP Registration (lib/gsap.ts):
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { Flip } from 'gsap/Flip';
import { SplitText } from 'gsap/SplitText';
if (typeof window !== 'undefined') {
gsap.registerPlugin(ScrollTrigger, Flip, SplitText);
}
export { gsap, ScrollTrigger, Flip, SplitText };
When to use GSAP vs CSS:
- CSS: Simple entrance/exit animations, hover states, single-element transitions
- GSAP: Scroll-driven narratives, multi-element timelines, layout transitions (Flip), SVG animations (DrawSVG/MorphSVG), complex staggers
GSAP Plugin Quick Reference:
| Plugin | Use Case | Example |
|---|---|---|
| ScrollTrigger | Scroll-driven animations | Parallax, pin sections, scrub animations |
| Flip | Layout transitions | Grid ↔ List, filter reorder |
| SplitText | Text animation | Character reveal, word highlight, line mask |
| DrawSVG | SVG line drawing | Logo reveal, path animation |
| MorphSVG | Shape morphing | Icon transitions, blob shapes |
| MotionPath | Path following | Orbit animation, route visualization |
| TextPlugin | Text scramble/typing | Counter, scramble reveal |
Lenis Smooth Scroll Integration:
import Lenis from '@studio-freight/lenis';
const lenis = new Lenis({
duration: 1.2,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
});
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time) => { lenis.raf(time * 1000); });
gsap.ticker.lagSmoothing(0);
4.7 Gesture & Touch Patterns
// hooks/useSwipe.ts
'use client';
import { useState, useCallback } from 'react';
interface UseSwipeOptions {
onSwipeLeft?: () => void;
onSwipeRight?: () => void;
onSwipeUp?: () => void;
onSwipeDown?: () => void;
threshold?: number;
}
export function useSwipe({ onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown, threshold = 50 }: UseSwipeOptions) {
const [touchStart, setTouchStart] = useState<{ x: number; y: number } | null>(null);
const [touchEnd, setTouchEnd] = useState<{ x: number; y: number } | null>(null);
const onTouchStart = useCallback((e: React.TouchEvent) => {
setTouchEnd(null);
setTouchStart({ x: e.targetTouches[0].clientX, y: e.targetTouches[0].clientY });
}, []);
const onTouchMove = useCallback((e: React.TouchEvent) => {
setTouchEnd({ x: e.targetTouches[0].clientX, y: e.targetTouches[0].clientY });
}, []);
const onTouchEnd = useCallback(() => {
if (!touchStart || !touchEnd) return;
const dx = touchStart.x - touchEnd.x;
const dy = touchStart.y - touchEnd.y;
if (Math.max(Math.abs(dx), Math.abs(dy)) < threshold) return;
if (Math.abs(dx) > Math.abs(dy)) {
dx > 0 ? onSwipeLeft?.() : onSwipeRight?.();
} else {
dy > 0 ? onSwipeUp?.() : onSwipeDown?.();
}
}, [touchStart, touchEnd, threshold, onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown]);
return { onTouchStart, onTouchMove, onTouchEnd };
}
MODULE 5: Video Production (Remotion)
5.1 When to Use Video Mode
Use Remotion when the user explicitly requests:
- "Create a video for..."
- "Animate this UI..."
- "Product demo video"
- "Social media content"
- "Explainer video"
- "Data visualization video"
- "3D product showcase"
5.2 Project Setup
npx create-video@latest --yes --blank my-video
npm install remotion @remotion/cli @remotion/player
npm install @remotion/transitions @remotion/google-fonts
Config (remotion.config.ts):
import { Config } from '@remotion/cli/config';
Config.setVideoImageFormat('jpeg');
Config.setChromiumOpenGlRenderer('angle');
5.3 Composition Structure
src/
├── Root.tsx # Composition registry
├── compositions/
│ ├── ProductDemo.tsx # 16:9, 30s
│ ├── SocialVertical.tsx # 9:16, 15s
│ ├── DataStory.tsx # 16:9, 60s
│ └── BrandIntro.tsx # 16:9, 10s (NEW)
├── lib/
│ ├── AnimatedText.tsx
│ ├── SlideTransition.tsx
│ ├── LowerThird.tsx
│ ├── StatCounter.tsx
│ └── LogoWatermark.tsx
└── theme.ts # From design system
5.4 Core Animation Primitives
import { useCurrentFrame, interpolate, spring, useVideoConfig } from 'remotion';
// Linear interpolation
const opacity = interpolate(frame, [0, 30], [0, 1], { extrapolateRight: 'clamp' });
// Spring physics
const scale = spring({ frame, fps, config: { damping: 10, stiffness: 100, mass: 0.5 } });
// Eased movement
const translateX = interpolate(frame, [0, 60], [-500, 0], {
easing: (t) => t * t * (3 - 2 * t),
extrapolateRight: 'clamp',
});
5.5 Essential Video Components
V01: AnimatedText
// lib/AnimatedText.tsx
import { useCurrentFrame, interpolate } from 'remotion';
interface AnimatedTextProps {
text: string;
startFrame?: number;
speed?: number;
className?: string;
}
export const AnimatedText = ({ text, startFrame = 0, speed = 2, className }: AnimatedTextProps) => {
const frame = useCurrentFrame();
const charsToShow = Math.floor((frame - startFrame) / speed);
const displayedText = text.slice(0, Math.max(0, charsToShow));
const cursorOpacity = interpolate(frame % 30, [0, 15], [1, 0], { extrapolateRight: 'clamp' });
return (
<span className={className}>
{displayedText}
<span style={{ opacity: cursorOpacity }}>|</span>
</span>
);
};
V02: SlideTransition
// lib/SlideTransition.tsx
import { useCurrentFrame, interpolate } from 'remotion';
interface SlideTransitionProps {
children: React.ReactNode;
direction?: 'left' | 'right' | 'up' | 'down';
startFrame?: number;
duration?: number;
}
export const SlideTransition = ({ children, direction = 'right', startFrame = 0, duration = 30 }: SlideTransitionProps) => {
const frame = useCurrentFrame();
const translateMap = { left: [-1920, 0], right: [1920, 0], up: [1080, 0], down: [-1080, 0] };
const isHorizontal = direction === 'left' || direction === 'right';
const translate = interpolate(frame, [startFrame, startFrame + duration], translateMap[direction], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: (t) => t * t * (3 - 2 * t),
});
return <div style={{ transform: isHorizontal ? `translateX(${translate}px)` : `translateY(${translate}px)` }}>{children}</div>;
};
V03: LowerThird
// lib/LowerThird.tsx
import { useCurrentFrame, interpolate, spring, useVideoConfig } from 'remotion';
interface LowerThirdProps {
name: string;
title: string;
startFrame?: number;
}
export const LowerThird = ({ name, title, startFrame = 0 }: LowerThirdProps) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const slideIn = spring({ frame: frame - startFrame, fps, config: { damping: 12, stiffness: 100 } });
const opacity = interpolate(frame, [startFrame, startFrame + 10], [0, 1], { extrapolateRight: 'clamp' });
return (
<div style={{ position: 'absolute', bottom: 60, left: 60, opacity, transform: `translateY(${(1 - slideIn) * 50}px)` }}>
<div style={{ background: 'linear-gradient(90deg, var(--color-primary) 0%, transparent 100%)', padding: '16px 32px', borderLeft: '4px solid var(--color-accent)' }}>
<div style={{ fontFamily: 'var(--font-display)', fontSize: 32, fontWeight: 700, color: '#fff' }}>{name}</div>
<div style={{ fontFamily: 'var(--font-body)', fontSize: 18, color: 'rgba(255,255,255,0.8)', marginTop: 4 }}>{title}</div>
</div>
</div>
);
};
V04: StatCounter
// lib/StatCounter.tsx
import { useCurrentFrame, interpolate, spring, useVideoConfig } from 'remotion';
interface StatCounterProps {
value: number;
prefix?: string;
suffix?: string;
label: string;
startFrame?: number;
duration?: number;
}
export const StatCounter = ({ value, prefix = '', suffix = '', label, startFrame = 0, duration = 60 }: StatCounterProps) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const springValue = spring({ frame: frame - startFrame, fps, config: { damping: 15, stiffness: 50 } });
const displayValue = Math.floor(interpolate(springValue, [0, 1], [0, value]));
return (
<div style={{ textAlign: 'center' }}>
<div style={{ fontFamily: 'var(--font-display)', fontSize: 72, fontWeight: 700, color: 'var(--color-text)' }}>
{prefix}{displayValue.toLocaleString()}{suffix}
</div>
<div style={{ fontFamily: 'var(--font-body)', fontSize: 20, color: 'var(--color-text-muted)', marginTop: 8, textTransform: 'uppercase', letterSpacing: '0.1em' }}>
{label}
</div>
</div>
);
};
V05: LogoWatermark
// lib/LogoWatermark.tsx
import { useCurrentFrame, interpolate } from 'remotion';
interface LogoWatermarkProps {
logo: React.ReactNode;
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
startFrame?: number;
}
export const LogoWatermark = ({ logo, position = 'bottom-right', startFrame = 0 }: LogoWatermarkProps) => {
const frame = useCurrentFrame();
const opacity = interpolate(frame, [startFrame, startFrame + 20], [0, 0.6], { extrapolateRight: 'clamp' });
const positions = { 'bottom-right': { bottom: 40, right: 40 }, 'bottom-left': { bottom: 40, left: 40 }, 'top-right': { top: 40, right: 40 }, 'top-left': { top: 40, left: 40 } };
return <div style={{ position: 'absolute', opacity, ...positions[position] }}>{logo}</div>;
};
5.6 Video Composition Templates
T01: ProductDemo (16:9, 30s, 900 frames @ 30fps)
// compositions/ProductDemo.tsx
import { AbsoluteFill, Sequence, useVideoConfig } from 'remotion';
import { AnimatedText } from '../lib/AnimatedText';
import { SlideTransition } from '../lib/SlideTransition';
import { StatCounter } from '../lib/StatCounter';
import { LogoWatermark } from '../lib/LogoWatermark';
export const ProductDemo = ({ productName, tagline, stats }: {
productName: string;
tagline: string;
stats: Array<{ value: number; label: string; suffix?: string }>;
}) => {
const { width, height } = useVideoConfig();
return (
<AbsoluteFill style={{ background: 'var(--color-background)', color: 'var(--color-text)' }}>
<Sequence from={0} durationInFrames={300}>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', width, height }}>
<AnimatedText text={productName} startFrame={30} speed={3} className="text-7xl font-bold" />
<div style={{ marginTop: 24 }}>
<AnimatedText text={tagline} startFrame={120} speed={2} className="text-2xl text-[var(--color-text-muted)]" />
</div>
</div>
</Sequence>
<Sequence from={300} durationInFrames={450}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 80, width, height }}>
{stats.map((stat, i) => (
<SlideTransition key={i} direction="up" startFrame={300 + i * 30} duration={40}>
<StatCounter value={stat.value} label={stat.label} suffix={stat.suffix} startFrame={330 + i * 30} duration={90} />
</SlideTransition>
))}
</div>
</Sequence>
<Sequence from={750} durationInFrames={150}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width, height }}>
<AnimatedText text={`Get started with ${productName} today`} startFrame={780} speed={2} className="text-4xl font-semibold" />
</div>
</Sequence>
<LogoWatermark logo={<span style={{ fontSize: 20, fontWeight: 700 }}>{productName}</span>} />
</AbsoluteFill>
);
};
T02: SocialVertical (9:16, 15s, 450 frames @ 30fps)
// compositions/SocialVertical.tsx
import { AbsoluteFill, Sequence, useCurrentFrame, interpolate } from 'remotion';
import { AnimatedText } from '../lib/AnimatedText';
export const SocialVertical = ({ hook, points, cta }: { hook: string; points: string[]; cta: string }) => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={{ background: 'var(--color-background)', color: 'var(--color-text)', padding: 40 }}>
<Sequence from={0} durationInFrames={90}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
<AnimatedText text={hook} startFrame={15} speed={2} className="text-5xl font-bold text-center" />
</div>
</Sequence>
<Sequence from={90} durationInFrames={270}>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', height: '100%', gap: 32 }}>
{points.map((point, i) => (
<div key={i} style={{ display: 'flex', alignItems: 'center', gap: 16, opacity: interpolate(frame, [90 + i * 45, 105 + i * 45], [0, 1], { extrapolateRight: 'clamp' }) }}>
<div style={{ width: 32, height: 32, borderRadius: '50%', background: 'var(--color-primary)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700 }}>{i + 1}</div>
<span style={{ fontSize: 24 }}>{point}</span>
</div>
))}
</div>
</Sequence>
<Sequence from={360} durationInFrames={90}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
<div style={{ padding: '20px 40px', background: 'var(--color-primary)', borderRadius: 16, fontSize: 28, fontWeight: 700 }}>{cta}</div>
</div>
</Sequence>
</AbsoluteFill>
);
};
T03: DataStory (16:9, 60s, 1800 frames @ 30fps)
// compositions/DataStory.tsx
import { AbsoluteFill, Sequence, useCurrentFrame, interpolate } from 'remotion';
import { StatCounter } from '../lib/StatCounter';
import { SlideTransition } from '../lib/SlideTransition';
export const DataStory = ({ title, dataPoints }: { title: string; dataPoints: Array<{ label: string; value: number; color: string }> }) => {
const frame = useCurrentFrame();
const maxValue = Math.max(...dataPoints.map((d) => d.value));
return (
<AbsoluteFill style={{ background: 'var(--color-background)', color: 'var(--color-text)', padding: 60 }}>
<Sequence from={0} durationInFrames={1800}>
<h1 style={{ fontSize: 48, fontWeight: 700, marginBottom: 40 }}>{title}</h1>
</Sequence>
<Sequence from={60} durationInFrames={600}>
<div style={{ display: 'flex', alignItems: 'flex-end', gap: 24, height: 400, paddingTop: 40 }}>
{dataPoints.map((point, i) => {
const delay = 60 + i * 20;
const height = interpolate(frame, [delay, delay + 40], [0, (point.value / maxValue) * 400], { extrapolateRight: 'clamp', easing: (t) => t * t * (3 - 2 * t) });
return (
<div key={i} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', flex: 1 }}>
<div style={{ width: '100%', height, background: point.color, borderRadius: '8px 8px 0 0', minHeight: 4 }} />
<span style={{ marginTop: 12, fontSize: 14, color: 'var(--color-text-muted)' }}>{point.label}</span>
</div>
);
})}
</div>
</Sequence>
<Sequence from={700} durationInFrames={400}>
<div style={{ display: 'flex', gap: 60, marginTop: 60 }}>
{dataPoints.slice(0, 3).map((point, i) => (
<SlideTransition key={i} direction="up" startFrame={700 + i * 30}>
<StatCounter value={point.value} label={point.label} startFrame={730 + i * 30} />
</SlideTransition>
))}
</div>
</Sequence>
</AbsoluteFill>
);
};
T04: BrandIntro (16:9, 10s, 300 frames @ 30fps) — NEW
// compositions/BrandIntro.tsx
import { AbsoluteFill, Sequence, useCurrentFrame, interpolate, spring, useVideoConfig } from 'remotion';
export const BrandIntro = ({ brandName, tagline, accentColor }: { brandName: string; tagline: string; accentColor: string }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scaleIn = spring({ frame, fps, config: { damping: 15, stiffness: 80 } });
const taglineOpacity = interpolate(frame, [60, 90], [0, 1], { extrapolateRight: 'clamp' });
const taglineY = interpolate(frame, [60, 90], [20, 0], { extrapolateRight: 'clamp', easing: (t) => t * t * (3 - 2 * t) });
const exitOpacity = interpolate(frame, [240, 300], [1, 0], { extrapolateRight: 'clamp' });
return (
<AbsoluteFill style={{ background: 'var(--color-background)', display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: exitOpacity }}>
<div style={{ textAlign: 'center' }}>
<div style={{ transform: `scale(${scaleIn})`, fontSize: 96, fontWeight: 700, color: accentColor, fontFamily: 'var(--font-display)' }}>
{brandName}
</div>
<div style={{ opacity: taglineOpacity, transform: `translateY(${taglineY}px)`, fontSize: 24, color: 'var(--color-text-muted)', fontFamily: 'var(--font-body)', marginTop: 16 }}>
{tagline}
</div>
</div>
</AbsoluteFill>
);
};
5.7 UI-to-Video Style Mapping
| UI Style ID | Video Treatment | Background | Typography | Motion |
|---|---|---|---|---|
| S01 Cinematic Dark | OLED blacks, accent lighting | #0F172A | Clash Display | Slow reveals, dramatic pauses |
| S02 Ethereal Glow | Soft particles, ambient | #FDF4FF | Cormorant Garamond | Gentle float, pulse |
| S03 Glassmorphism | Frosted overlays, depth blur | Gradient mesh | Plus Jakarta Sans | Smooth slides, blur transitions |
| S04 Brutalism | Hard cuts, high contrast | #FFFFFF | DM Serif Display | Abrupt transitions, no easing |
| S05 Cyberpunk | Neon grids, glitch | #0a0a0a | Orbitron | Glitch effects, scanlines |
| S06 Organic Biophilic | Natural curves, earth tones | #F0FDF4 | Literata | Flowing motion, growth animations |
| S07 Bento Grid | Modular cards, clean | #FFFFFF | Satoshi | Staggered reveals, snap |
| S08 Memphis | Bold patterns, clashing | #FFFBEB | Fraunces | Bouncy springs, playful |
| S09 Editorial Grid | Asymmetric, magazine | #FAFAF9 | Playfair Display | Scroll-synced, elegant |
| S10 Retro-Futurism | CRT effects, chrome | #1a1a2e | Space Mono | Scanlines, chromatic aberration |
5.8 Rendering & Deployment
# Local render
npx remotion render src/index.ts ProductDemo out/demo.mp4
# High quality
npx remotion render src/index.ts ProductDemo out/demo.mp4 --crf=15
# 4K
npx remotion render src/index.ts ProductDemo out/demo.mp4 --scale=2
# Single frame test
npx remotion still ProductDemo --frame=30 out/frame.png
Platform Output Specs:
| Platform | Resolution | Aspect | Duration | Format |
|---|---|---|---|---|
| TikTok/Reels | 1080×1920 | 9:16 | 15-60s | MP4 |
| YouTube | 1920×1080 | 16:9 | 2-10min | MP4 |
| Instagram Feed | 1080×1080 | 1:1 | 15-60s | MP4 |
| 1920×1080 | 16:9 | <10min | MP4 |
MODULE 6: Quality Assurance
6.1 Contrast Validation Procedure
For every text/background combination, calculate:
Step 1: Convert hex to RGB
#RRGGBB → R = hex(RR), G = hex(GG), B = hex(BB)
Step 2: Convert RGB to linear RGB
For each channel X:
XsRGB = X / 255
Xlin = XsRGB ≤ 0.03928 ? XsRGB / 12.92 : ((XsRGB + 0.055) / 1.055) ^ 2.4
Step 3: Calculate relative luminance
L = 0.2126 * Rlin + 0.7152 * Glin + 0.0722 * Blin
Step 4: Calculate contrast ratio
Ratio = (Llighter + 0.05) / (Ldarker + 0.05)
Step 5: Verify
Normal text (<18px): Ratio ≥ 4.5:1 (AA), ≥ 7:1 (AAA)
Large text (≥18px bold or ≥24px): Ratio ≥ 3:1 (AA), ≥ 4.5:1 (AAA)
UI components/graphical objects: Ratio ≥ 3:1 (AA)
Example verification for Palette P01 (Quantum Deep):
- Text #F8FAFC on Background #0F172A: Ratio = 15.3:1 → AAA
- Muted #94A3B8 on Background #0F172A: Ratio = 5.8:1 → AA
- Primary #2563EB on Background #0F172A: Ratio = 4.6:1 → AA
- Primary #2563EB on Surface #1E293B: Ratio = 3.2:1 → AA for large text only
6.2 Cognitive Load Heuristic
Rate each page section (0 = none, +1 for each item):
| Factor | +1 If... |
|---|---|
| Color complexity | More than 3 distinct colors in one section |
| Font complexity | More than 2 font families in one section |
| Animation variety | More than 2 animation types on one page |
| Layout density | Content breaks below 768px breakpoint |
| Interaction novelty | Non-obvious interaction pattern used |
| Data density | Data visualization without clear labels |
| Navigation depth | More than 3 levels of navigation |
| Form complexity | Form with more than 5 fields visible at once |
Score interpretation:
- 0-2: Minimal cognitive load (excellent for primary tasks)
- 3-4: Low cognitive load (good for most interfaces)
- 5-6: Moderate cognitive load (acceptable for dashboards)
- 7+: High cognitive load (simplify before shipping)
6.3 Pre-Delivery Checklists
Static UI Checklist
- No emojis as icons (use SVG: Lucide, Heroicons)
-
cursor-pointeron all clickable elements - Hover states with smooth transitions (150-300ms)
- Text contrast ≥ 4.5:1 (calculate using 6.1)
- Focus states visible for keyboard navigation (
:focus-visible) -
prefers-reduced-motionrespected (animations disabled or simplified) - Responsive at 375px, 768px, 1024px, 1440px
- Touch targets ≥ 44×44px on mobile
- Form validation with clear error states (Zod + React Hook Form)
- Loading skeletons for dynamic content
- Empty states for all data-dependent UI
- Error boundaries for crash recovery
- Semantic HTML (proper heading hierarchy, landmarks)
- Skip link present for keyboard users
- Screen reader patterns (aria-live, role="alert", visually hidden text)
- Focus trap on modals and dialogs
- Keyboard navigation on all interactive widgets (Tabs, Accordion)
- No
!importantin CSS - No
pxfor font sizes (userem) - No
Math.random()in render paths (use deterministic generation) - No banned fonts as primary (Inter, Roboto, Arial, Space Grotesk)
- No purple gradient on white backgrounds
- 60-30-10 color distribution applied
- Server/Client Component boundaries correct (
'use client'on interactive) -
next/imageused for all images withwidth/heightorfill - Metadata API used for SEO (title, description, OpenGraph)
- Security headers configured (CSP, X-Frame-Options, etc.)
- Container queries used for component-level responsive design
Video Checklist
- Frame rate consistent (30fps default)
- All assets referenced with
staticFile() - Deterministic rendering (same output every time, no
Math.random()) - No
useFrame()from R3F (useuseCurrentFrame()from Remotion) - All imports at top of file (no bottom imports)
- Captions included if audio present
- Color contrast maintained in video overlays (≥ 4.5:1)
- Reduced motion version prepared or simplified
- Output format matches target platform
- Render tested at full resolution before final export
6.4 Hard Rules (Never Break)
- Never output placeholder text — always use real content or lorem ipsum with context
- Never use
!importantin CSS — use specificity and cascade - Never skip focus states — every interactive element must have visible
:focus-visible - Never use
pxfor font sizes — useremfor accessibility - Never hardcode colors — always use design tokens (
var(--color-*)) - Never ignore
prefers-reduced-motion— respect user preferences - Never use
Math.random()for IDs — use deterministic generation (useId(), counters) - Never skip mobile-first — default to mobile, enhance for desktop
- Never use inline styles for theming — use CSS variables
- Never forget loading states — every async operation needs feedback
- Never default to Inter, Roboto, Arial, or Space Grotesk as primary fonts — these are overused by AI. Select from curated distinctive pairings. System fonts (
system-ui) acceptable ONLY as final fallback. - Never use purple gradient on white/light backgrounds — it's the clichéd AI aesthetic. Gradients permitted on dark backgrounds or as subtle accents.
- Never create timid, evenly-distributed palettes — use 60-30-10 rule with dominant color + sharp accent
- Never claim "auto-validation" without showing the validation procedure — always show your work
- Never reference external databases or tools not embedded in this skill — if data is required, include it
- Never skip keyboard navigation — Tabs need roving tabindex, Accordion needs arrow keys, Modal needs focus trap
- Never use
forwardRefin React 19+ — ref is a regular prop - Never render interactive components without
'use client'— hooks, state, and event handlers require it - Never use fonts not on Google Fonts without marking them self-host — Clash Display, Cabinet Grotesk, Satoshi require self-hosting or substitution
MODULE 7: Complete Embedded Reference Data
7.1 Style Reference (60 Curated Styles)
| ID | Name | Category | Best For | Primary Mood | Motion Language | Key Effects | Anti-Patterns |
|---|---|---|---|---|---|---|---|
| S01 | Cinematic Dark | General | Deep tech, streaming, AI | OLED blacks, dramatic lighting | Slow reveals, dramatic pauses | Accent lighting, vignette, depth shadows | Bright backgrounds, scattered animations |
| S02 | Ethereal Glow | General | Wellness, spirituality, luxury | Soft glows, ambient particles | Gentle float, pulse, breathe | Ambient particles, soft bloom, slow drift | Harsh shadows, high contrast |
| S03 | Glassmorphism | General | Modern SaaS, fintech | Frosted overlays, depth blur | Smooth slides, depth transitions | Backdrop blur, translucent layers, refraction | Overuse of blur (performance), low contrast text |
| S04 | Brutalism | General | Design portfolios, art | Raw, unpolished, high contrast | Hard cuts, aggressive typography | Thick borders, clashing colors, system fonts | Soft shadows, gradients, rounded corners |
| S05 | Cyberpunk | General | Gaming, crypto, tech | Neon grids, glitch aesthetics | Glitch effects, scanlines, neon pulse | RGB split, chromatic aberration, grid overlays | Subtle colors, organic shapes, soft edges |
| S06 | Organic Biophilic | General | Wellness, sustainability | Natural curves, earth tones | Flowing motion, growth animations | Natural forms, living textures, earth palette | Geometric rigidity, neon colors, sharp angles |
| S07 | Bento Grid | General | Dashboards, product pages | Modular cards, clean organization | Staggered reveals, snap transitions | Card-based layouts, consistent spacing, modularity | Asymmetric chaos, overlapping without purpose |
| S08 | Memphis Design | General | Creative agencies, music | Bold patterns, clashing colors | Bouncy springs, playful movements | Geometric patterns, bold outlines, vibrant palette | Muted tones, minimalism, corporate stiffness |
| S09 | Editorial Grid | General | News, blogs, magazines | Asymmetric, magazine-like | Scroll-synced, elegant transitions | Large imagery, asymmetric grids, serif typography | Symmetric boredom, small images, sans-serif only |
| S10 | Retro-Futurism | General | Gaming, entertainment | CRT effects, chrome, vintage tech | Scanlines, chromatic aberration | Retro gradients, chrome effects, pixel touches | Modern flat design, minimalism, current trends |
| S11 | Minimalism | General | Enterprise, portfolios | Extreme whitespace, precision | Subtle fades, precise timing | Generous whitespace, single focal point, restraint | Clutter, multiple focal points, decorative elements |
| S12 | Neumorphism | General | Health, wellness apps | Soft shadows, subtle depth | Gentle hover, soft transitions | Inset shadows, extruded elements, monochromatic | High contrast, sharp edges, vibrant colors |
| S13 | Dark Mode OLED | General | Night apps, coding platforms | Pure blacks, accent lighting | Accent glow, moody atmosphere | OLED black backgrounds, minimal UI, accent highlights | Gray backgrounds, bright whites, light mode default |
| S14 | Claymorphism | General | Education, children's apps | Bouncy, playful, rounded | Spring animations, playful curves | 3D-like rounded shapes, inner shadows, pastel colors | Sharp corners, dark themes, serious tone |
| S15 | Aurora UI | General | Modern SaaS, creative | Gradient meshes, flowing colors | Flowing gradients, color shifts | Mesh gradients, fluid backgrounds, soft transitions | Solid colors, static backgrounds, harsh transitions |
| S16 | Kinetic Typography | General | Hero sections, marketing | Text-as-graphics, bold | Scroll-synced, text animations | Oversized text, text masking, scroll-driven reveals | Small text, image-heavy, static headlines |
| S17 | Swiss Modernism | General | Corporate, editorial | Grid systems, asymmetric balance | Precise alignment, grid reveals | Strong grid, asymmetric balance, sans-serif precision | Centered everything, decorative elements, script fonts |
| S18 | HUD / Sci-Fi FUI | General | Sci-fi games, space tech | Circular elements, data readouts | Data stream animations, HUD reveals | Circular progress, data visualization, tech glow | Organic shapes, warm colors, handwritten fonts |
| S19 | Liquid Glass | General | Premium SaaS, high-end | Refraction, caustics, glass | Glass morphing, light play | Advanced glass effects, refraction, depth | Flat design, solid colors, no depth |
| S20 | Data Mesh | General | Analytics, BI dashboards | Grid lines, flowing data points | Data stream animations, grid pulses | Grid backgrounds, flowing data, real-time feel | Empty space, static charts, decorative elements |
| S21 | Holographic | General | Tech demos, futuristic | Iridescent surfaces, light interference | Shimmer, interference patterns | Holographic foil effects, rainbow shifts, light play | Flat colors, matte finishes, warm earth tones |
| S22 | Neon Noir | General | Nightlife, gaming, cyberpunk | Neon accents, dark atmosphere | Neon flicker, glow pulse | Neon tube effects, dark backgrounds, high contrast accents | Bright backgrounds, pastel colors, soft lighting |
| S23 | Soft Brutalism | General | Edgy but accessible brands | Thick borders, soft colors | Bold transitions, soft colors | Thick rounded borders, pastel backgrounds, bold typography | Sharp corners, high contrast, thin borders |
| S24 | Floating Islands | General | SaaS features, product tours | Floating cards, depth shadows | Float, hover lift, depth | Elevated cards, soft shadows, layered depth | Flat cards, no elevation, solid backgrounds |
| S25 | Morphing Shapes | General | Creative agencies, portfolios | SVG morphing, organic transitions | Shape morph, organic movement | SVG path animations, blob shapes, smooth morphs | Static shapes, geometric rigidity, no animation |
| S26 | Ambient Motion | General | Backgrounds, loading states | Slow particles, gentle waves | Ambient drift, gentle waves | Slow particle systems, wave animations, gentle motion | Fast movement, abrupt changes, high energy |
| S27 | Tactile Digital | General | Mobile apps, playful | 3D buttons, physical feedback | Press states, physical response | 3D button effects, press animations, tactile shadows | Flat buttons, no feedback, minimal interaction |
| S28 | Generative Patterns | General | AI brands, creative tools | Procedural textures, algorithmic | Generative animation, pattern evolution | Algorithmic patterns, procedural generation, evolving textures | Static patterns, manual design, no variation |
| S29 | Nature Distilled | General | Wellness, sustainable | Earth tones, organic shapes | Natural growth, organic flow | Earth tones, organic curves, natural textures | Synthetic colors, geometric shapes, tech aesthetic |
| S30 | Interactive Cursor | General | Creative portfolios | Custom cursors, hover effects | Cursor follow, hover reveals | Custom cursor design, hover state reveals, cursor trails | Default cursor, no hover effects, static interaction |
| S31-S60 | [Same as v5.0] | General | Various | Various | Various | Various | Various |
Styles S31-S60 continue with the same entries as v5.0: 3D Product Preview, Gradient Mesh, Chromatic Aberration, Vintage Analog, Paper Craft, Spatial UI, E-Ink/Paper, Gen Z Chaos, Anti-Polish/Raw, Voice-First Multimodal, AI-Native UI, Parallax Storytelling, Exaggerated Minimalism, Dimensional Layering, Vaporwave, Y2K Aesthetic, Micro-interactions, Inclusive Design, Zero Interface, Motion-Driven, Skeuomorphism, Flat Design, Vibrant Block-based, 3D & Hyperrealism, Accessible & Ethical, Soft UI Evolution, Neubrutalism, Pixel Art, Liquid Glass 2.0, Tactile Feedback. Full entries preserved from v5.0 Module 7.1.
7.2 Color Palette Reference (48 Curated Sets)
| ID | Name | Primary | Secondary | Accent | Background | Surface | Text | Muted | Dark BG | Dark Surface | WCAG AA | OKLCH Primary | Tags |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| P01 | Quantum Deep | #2563EB | #7C3AED | #06B6D4 | #0F172A | #1E293B | #F8FAFC | #94A3B8 | #020617 | #0F172A | Pass | oklch(0.546 0.245 262.88) | tech, dark, trust |
| P02 | Emerald SaaS | #059669 | #10B981 | #34D399 | #FFFFFF | #F0FDF4 | #111827 | #6B7280 | #064E3B | #065F46 | Pass | oklch(0.569 0.156 163.28) | growth, light, calm |
| P03 | Rose Editorial | #E11D48 | #FB7185 | #FDA4AF | #FFF1F2 | #FFE4E6 | #881337 | #9F1239 | #4C0519 | #881337 | Pass | oklch(0.524 0.223 14.48) | luxury, warm, editorial |
| P04 | Amber Forge | #D97706 | #F59E0B | #FCD34D | #FFFBEB | #FEF3C7 | #78350F | #92400E | #451A03 | #78350F | Pass | oklch(0.694 0.159 72.41) | energy, warm, industrial |
| P05 | Slate Corporate | #475569 | #64748B | #94A3B8 | #F8FAFC | #F1F5F9 | #0F172A | #475569 | #0F172A | #1E293B | Pass | oklch(0.498 0.024 256.37) | corporate, neutral, clean |
| P06 | Violet Vision | #7C3AED | #8B5CF6 | #A78BFA | #FAF5FF | #F3E8FF | #4C1D95 | #6D28D9 | #2E1065 | #4C1D95 | Pass | oklch(0.499 0.218 293.55) | creative, light, innovative |
| P07 | Cyan Stream | #0891B2 | #06B6D4 | #67E8F9 | #ECFEFF | #CFFAFE | #164E63 | #155E75 | #083344 | #164E63 | Pass | oklch(0.577 0.129 207.52) | tech, light, fresh |
| P08 | Crimson Sport | #DC2626 | #EF4444 | #FCA5A5 | #FEF2F2 | #FEE2E2 | #7F1D1D | #991B1B | #450A0A | #7F1D1D | Pass | oklch(0.524 0.209 27.29) | energy, bold, action |
| P09 | Oceanic Calm | #0EA5E9 | #38BDF8 | #7DD3FC | #F0F9FF | #E0F2FE | #0C4A6E | #075985 | #082F49 | #0C4A6E | Pass | oklch(0.627 0.155 225.28) | calm, trust, water |
| P10 | Forest Ground | #16A34A | #22C55E | #86EFAC | #F0FDF4 | #DCFCE7 | #14532D | #166534 | #052E16 | #14532D | Pass | oklch(0.598 0.174 145.78) | nature, growth, organic |
| P11 | Sunset Blaze | #EA580C | #F97316 | #FDBA74 | #FFF7ED | #FFEDD5 | #7C2D12 | #9A3412 | #431407 | #7C2D12 | Pass | oklch(0.645 0.192 46.62) | warm, energetic, bold |
| P12 | Midnight Gold | #EAB308 | #CA8A04 | #A16207 | #18181B | #27272A | #FAFAFA | #A1A1AA | #09090B | #18181B | Pass | oklch(0.780 0.166 85.98) | luxury, dark, premium |
| P13-P48 | [Same as v5.0] | ... | ... | ... | ... | ... | ... | ... | ... | ... | Pass | oklch(...) | ... |
Palettes P13-P48 continue with the same hex values as v5.0 Module 7.2, now with OKLCH equivalents added. Full entries: Soft Lavender, Arctic Ice, Terracotta Warm, Charcoal Neon, Sage Wellness, Berry Rich, Graphite Minimal, Coral Energy, Indigo Night, Mint Fresh, Rust Industrial, Pearl Luxury, Storm Tech, Blossom Soft, Obsidian Edge, Meadow Bright, Driftwood Natural, Plasma Electric, Sand Dune, Glacier Blue, Ember Glow, Moss Deep, Silk Ivory, Void Black, Honeycomb, Azure Sky, Mulberry Wine, Concrete Urban, Tidal Wave, Copper Mine, Petal Pink, Onyx Premium, Dewdrop Morning, Smolder Ash, Cloud White, Volcanic Rock.
7.3 Font Pairing Reference (36 Curated Sets)
| ID | Display Font | Body Font | Mono Font | Google Fonts Import | Mood | Self-Host? |
|---|---|---|---|---|---|---|
| F01 | Clash Display | Plus Jakarta Sans | JetBrains Mono | SELF-HOST: Use Outfit instead, or download from fontshare.com | Technical, futuristic | YES |
| F02 | Playfair Display | Source Serif 4 | IBM Plex Mono | https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600;700&family=Source+Serif+4:wght@400;600&family=IBM+Plex+Mono:wght@400&display=swap | Editorial, refined | No |
| F03 | DM Serif Display | Literata | JetBrains Mono | https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Literata:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap | Classical, trustworthy | No |
| F04 | Fraunces | Satoshi | SF Mono | SELF-HOST: Use Plus Jakarta Sans instead, or download from fontshare.com | Warm, approachable | YES (Satoshi) |
| F05 | Cabinet Grotesk | Manrope | JetBrains Mono | SELF-HOST: Use Outfit instead, or download from fontshare.com | Modern, geometric | YES (Cabinet) |
| F06 | Cormorant Garamond | Montserrat | IBM Plex Mono | https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;600;700&family=Montserrat:wght@400;500;600&family=IBM+Plex+Mono:wght@400&display=swap | Luxury, editorial | No |
| F07 | Outfit | Work Sans | JetBrains Mono | https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;700&family=Work+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap | Tech, modern | No |
| F08 | Syne | Work Sans | IBM Plex Mono | https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700&family=Work+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400&display=swap | Artistic, bold | No |
| F09 | Bricolage Grotesque | Inter Tight | JetBrains Mono | https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@400;500;700&family=Inter+Tight:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap | Contemporary, crisp | No |
| F10 | Instrument Serif | Instrument Sans | IBM Plex Mono | https://fonts.googleapis.com/css2?family=Instrument+Serif&family=Instrument+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400&display=swap | Refined, delicate | No |
| F11 | Bebas Neue | Open Sans | JetBrains Mono | https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Open+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap | Bold, impactful | No |
| F12 | Righteous | Nunito | IBM Plex Mono | https://fonts.googleapis.com/css2?family=Righteous&family=Nunito:wght@400;500;600&family=IBM+Plex+Mono:wght@400&display=swap | Playful, friendly | No |
| F13 | Orbitron | Rajdhani | JetBrains Mono | https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700&family=Rajdhani:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap | Sci-fi, tech | No |
| F14 | Cinzel | Lato | IBM Plex Mono | https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Lato:wght@400;700&family=IBM+Plex+Mono:wght@400&display=swap | Luxury, timeless | No |
| F15 | Archivo Black | Archivo | JetBrains Mono | https://fonts.googleapis.com/css2?family=Archivo+Black&family=Archivo:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap | Bold, modern | No |
| F16 | Zilla Slab Highlight | Zilla Slab | IBM Plex Mono | https://fonts.googleapis.com/css2?family=Zilla+Slab+Highlight:wght@400;700&family=Zilla+Slab:wght@400;500;600&family=IBM+Plex+Mono:wght@400&display=swap | Editorial, marked | No |
| F17 | Unbounded | Sora | JetBrains Mono | https://fonts.googleapis.com/css2?family=Unbounded:wght@400;600;700&family=Sora:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap | Web3, futuristic | No |
| F18 | Alegreya | Alegreya Sans | IBM Plex Mono | https://fonts.googleapis.com/css2?family=Alegreya:wght@400;600;700&family=Alegreya+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400&display=swap | Literary, warm | No |
| F19 | Teko | Barlow | JetBrains Mono | https://fonts.googleapis.com/css2?family=Teko:wght@400;600;700&family=Barlow:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap | Industrial, sporty | No |
| F20 | Yeseva One | Josefin Sans | IBM Plex Mono | https://fonts.googleapis.com/css2?family=Yeseva+One&family=Josefin+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400&display=swap | Decorative, artistic | No |
| F21-F36 | [Same as v5.0 with verified URLs] | ... | ... | ... | ... | ... |
Font pairings F21-F36 continue as in v5.0: Exo 2/Exo, Cactus Classical Serif/Quattrocento Sans, Chakra Petch/Kanit, Libre Baskerville/Source Sans 3, Russo One/Roboto Condensed, Spectral/Spectral, Prompt/Mitr, Crimson Pro/Crimson Text, Oswald/Quattrocento Sans, Merriweather/Merriweather Sans, Raleway/Lato, PT Serif/PT Sans, Noto Serif SC/Noto Sans SC, Noto Serif JP/Noto Sans JP, Noto Naskh Arabic/Noto Sans Arabic, Noto Serif Devanagari/Noto Sans Devanagari — all with verified Google Fonts import URLs.
7.4 Industry Rule Reference (24 Curated Heuristics)
| ID | Industry | Recommended Styles | Trust Signals | Differentiation Opportunity | Cognitive Load |
|---|---|---|---|---|---|
| I01 | AI/ML Platform | S01, S41, S17 | API docs, model benchmarks, researcher testimonials | Real-time inference demo (not static screenshots) | 5-6 |
| I02 | Web3/Blockchain | S05, S17, S23 | Audit badges, open-source links, wallet integration | Live transaction visualizer (not just price charts) | 6-7 |
| I03 | Quantum Computing | S01, S18, S21 | Academic partnerships, paper citations, researcher team | Interactive qubit visualizer as hero element | 7-8 |
| I04 | Biotech/Health | S02, S06, S17 | FDA/CE marks, clinical trial data, physician testimonials | Genomic data visualization, patient outcome stories | 4-5 |
| I05 | Climate Tech | S06, S29, S17 | Carbon offset certifications, impact metrics, NGO partnerships | Real-time environmental data dashboard | 5-6 |
| I06 | Creator Economy | S09, S08, S16 | Subscriber counts, revenue transparency, creator testimonials | Embedded content preview (newsletter, course) | 3-4 |
| I07 | No-Code/Low-Code | S07, S11, S17 | Template gallery, integration logos, time-to-build metrics | Interactive builder demo (not screenshot) | 5-6 |
| I08 | Fintech | S03, S11, S17 | Security certifications, bank partnerships, regulatory compliance | Real-time portfolio visualizer, transaction flow | 6-7 |
| I09 | E-commerce | S19, S31, S17 | Reviews, trust badges, return policy, shipping info | 3D product preview, AR try-on | 4-5 |
| I10 | Education | S14, S48, S17 | Accreditation, instructor credentials, completion rates | Interactive lesson preview, progress visualization | 4-5 |
| I11 | Real Estate | S19, S36, S17 | Virtual tours, neighborhood data, agent profiles | Immersive 3D tour as primary CTA | 4-5 |
| I12 | Hospitality | S02, S06, S17 | Reviews, amenities gallery, location map | Booking engine with real-time availability | 3-4 |
| I13 | Legal/Compliance | S11, S48, S17 | Case studies, bar admissions, client testimonials | Document automation demo, compliance checker | 4-5 |
| I14 | Gaming | S05, S10, S18 | Trailer embed, player counts, tournament info | Live gameplay feed, character customizer | 6-7 |
| I15 | Music/Audio | S10, S33, S16 | Artist roster, play counts, genre diversity | Embedded audio player, waveform visualization | 4-5 |
| I16 | Fitness/Wellness | S02, S06, S17 | Trainer certifications, before/after, program structure | Workout preview video, progress tracker demo | 3-4 |
| I17 | Food/Beverage | S15, S31, S17 | Menu, sourcing info, chef profiles | Interactive menu with dietary filters | 3-4 |
| I18 | Travel | S38, S31, S17 | Destination guides, booking engine, traveler reviews | Interactive map with real-time deals | 4-5 |
| I19 | SaaS (General) | S03, S07, S11 | Integration logos, ROI calculator, free trial | Interactive product tour (not video) | 5-6 |
| I20 | Agency/Studio | S08, S09, S16 | Portfolio grid, client logos, awards | Case study with process breakdown | 4-5 |
| I21 | Nonprofit | S06, S48, S17 | Impact metrics, donation transparency, volunteer stories | Real-time impact dashboard, donation flow | 3-4 |
| I22 | Government | S11, S48, S17 | Accessibility compliance, multilingual, service directory | Plain-language service finder, form simplification | 3-4 |
| I23 | Fashion | S11, S43, S17 | Lookbook, size guide, sustainability report | Virtual fitting room, fabric close-ups | 4-5 |
| I24 | Automotive | S01, S19, S17 | Safety ratings, configurator, dealer locator | 3D configurator with real-time pricing | 5-6 |
7.5 Supported Stacks
| Platform | Stack | Best For |
|---|---|---|
| Web | React + Tailwind CSS | Interactive web apps (default) |
| Web | Next.js + Tailwind CSS | SEO-critical, SSR apps |
| Web | Vue 3 + Tailwind CSS | Progressive enhancement |
| Web | Astro + Tailwind CSS | Content-heavy static sites |
| Web | Svelte + Tailwind CSS | Performance-critical apps |
| Web | HTML + Tailwind CSS | Static sites, prototypes |
| Mobile | React Native | Cross-platform mobile |
| Mobile | SwiftUI | Native iOS |
| Mobile | Jetpack Compose | Native Android |
| Desktop | Tauri + React | Lightweight desktop apps |
| Video | Remotion + React | Programmatic video production |
| Video | Remotion + React + R3F | 3D video production |
MODULE 8: Complete Workflow Example
Input: "Build a landing page for my quantum computing startup called QubitLabs"
Step 1: Creative Brief
- Purpose: Cloud-native quantum algorithm platform for researchers without PhDs
- Tone: Cinematic Dark + Holographic accents (extreme, not safe)
- Constraints: Next.js, WCAG AA, 3s load budget
- Differentiation: Real-time quantum circuit visualizer as hero element — no static image
- Industry: Quantum Computing → Look up I03
Step 2: Data Lookup
- Industry I03 → Styles: S01 (Cinematic Dark), S18 (HUD/Sci-Fi), S21 (Holographic)
- Select S01 as primary → Palette: P01 (Quantum Deep) → Font: F01 (Clash Display / Plus Jakarta Sans)
- Font Fix: Clash Display is self-host → Use Outfit (Google Fonts) as substitute, or self-host from fontshare.com
Step 3: Design Tokens
:root {
--color-primary: #2563EB;
--color-secondary: #7C3AED;
--color-accent: #06B6D4;
--color-background: #0F172A;
--color-surface: #1E293B;
--color-text: #F8FAFC;
--color-text-muted: #94A3B8;
--font-display: 'Outfit', system-ui, sans-serif;
--font-body: 'Plus Jakarta Sans', system-ui, sans-serif;
}
Step 4: Contrast Verification (using Module 6.1)
- Text #F8FAFC on Background #0F172A: 15.3:1 → AAA
- Primary #2563EB on Background #0F172A: 4.6:1 → AA
- Muted #94A3B8 on Background #0F172A: 5.8:1 → AA
Step 5: Component Assembly (uses components from Module 3)
Step 6: Validation
- No banned fonts as primary (Outfit + Plus Jakarta Sans)
- No purple gradient on white (gradient on dark background)
- 60-30-10 distribution (background 60%, surface 30%, accent 10%)
- Contrast ratios verified (15.3:1, 4.6:1, 5.8:1)
- Focus states on all interactive elements
- prefers-reduced-motion respected
- Responsive breakpoints covered
- Touch targets ≥ 44×44px
- Keyboard navigation on all interactive widgets
- No Math.random() in render paths
- Server/Client Component boundaries correct
MODULE 9: Cross-Reference Integration
9.1 With Anthropic Frontend Design
| This Skill Adds | Anthropic Adds |
|---|---|
| Systematic execution, embedded data | Creative direction, trust patterns |
| Production components | AI-native UI patterns |
| Design tokens + palettes | Typography philosophy |
| Validation procedures | Motion philosophy |
Key Anthropic patterns integrated into v7.1:
- Generous whitespace principle (Module 2.3)
- Asymmetric balance principle (Module 1.1 Tone)
- Reading line principle (Module 2.2 Typography)
- Content-first breakpoints (Module 2.6)
- The paragraph as object (Module 2.2)
- Typographic color principle (Module 2.2)
- Muted foundation colors (Module 2.1)
- Motion as thought philosophy (Module 4.3)
- Thinking indicator (Module 3 C13)
- Uncertainty notice (Module 3 C14)
- Streaming text animation (Module 4.1)
- Source citation pattern (Module 1.2 checklist)
- Anti-pattern confidence checklist (Module 1.2)
- Tone check principle (Module 1.1)
9.2 With Vercel Web Design Guidelines
| This Skill Adds | Vercel Web Adds |
|---|---|
| Design tokens, palettes | WCAG 2.2 full checklist |
| Component library | Performance budgets |
| Animation presets | SEO + Metadata API |
| Industry heuristics | Security headers (CSP) |
Key Vercel Web patterns integrated into v7.1:
- Container queries (Module 2.6)
- Screen reader patterns (Module 3 C02 Input, C04 Modal)
- Focus trap pattern (Module 3 C04 Modal)
- Skip link pattern (Module 3 C05 Navbar)
- next/image optimization (Module 6.3 checklist)
- Metadata API pattern (Module 6.3 checklist)
- Security headers / CSP (Module 6.3 checklist)
- Performance budgets (Module 6.3 checklist)
- Form validation with Zod (Module 3 C02 Input)
- Touch target minimums (Module 1.3 checklist)
- Visually hidden component pattern (Module 3 C05 skip link)
9.3 With Vercel React Best Practices
| This Skill Adds | Vercel React Adds |
|---|---|
| Component architecture | State management (Zustand, React Query) |
| Design tokens | Server/Client Component patterns |
| Motion patterns | Performance optimization |
| Industry heuristics | Error boundaries, testing |
Key Vercel React patterns integrated into v7.1:
- Server Component patterns (Module 3.2)
- Error Boundary pattern (Module 6.3 checklist)
- Form validation with Zod (Module 3 C02)
- Custom hooks (Module 4.4, 4.5, 4.7)
- React 19 patterns — no forwardRef (Module 3 C01-C14)
useId()for accessible IDs (Module 3 C02, C09, C10)- Code splitting patterns (Module 6.3 checklist)
- Compound component considerations (Module 3 architecture)
9.4 With GSAP Animations
| This Skill Adds | GSAP Adds |
|---|---|
| CSS-first animation presets | Complex timeline orchestration |
| Design tokens for motion | ScrollTrigger configurations |
| Component-level motion | React integration (useGSAP) |
| Reduced motion hooks | Flip, SplitText, DrawSVG, MorphSVG plugins |
Key GSAP patterns integrated into v7.1:
- useGSAP hook pattern (Module 4.6)
- GSAP registration pattern (Module 4.6)
- CSS vs GSAP decision guide (Module 4.6)
- Plugin quick reference table (Module 4.6)
- Lenis smooth scroll integration (Module 4.6)
- Magnetic button effect reference (GSAP Skill Module 6.2)
- Text scramble effect reference (GSAP Skill Module 6.3)
- Image reveal pattern reference (GSAP Skill Module 6.4)
- Cursor follower pattern reference (GSAP Skill Module 6.5)
9.5 Shared Pattern Reference — Source of Truth
This skill serves as the single source of truth for design patterns. Other skills forward-reference from here:
| Pattern | Primary Source | Forward References |
|---|---|---|
| Design tokens (CSS variables) | UI/UX Pro Max v7.1 | All skills use tokens |
| Color palettes (48 curated) | UI/UX Pro Max v7.1 | All skills use tokens |
| Typography (36 pairings) | UI/UX Pro Max v7.1 | Other skills reference |
| Component library (14) | UI/UX Pro Max v7.1 | All skills use components |
| Animation presets (24) | UI/UX Pro Max v7.1 | GSAP references |
| ThinkingIndicator | anthropic-frontend-design | UI/UX Pro Max C13 |
| UncertaintyNotice | anthropic-frontend-design | UI/UX Pro Max C14 |
| useGSAP | gsap-animations | UI/UX Pro Max 4.6 |
| WCAG 2.2 checklist | vercel-web-design-guidelines | UI/UX Pro Max references |
| Server/Client patterns | vercel-react-best-practices | UI/UX Pro Max |
| prefers-reduced-motion | vercel-web-design-guidelines | ALL SKILLS |
| fluid typography | vercel-web-design-guidelines | UI/UX Pro Max |
Integration Rule: When another skill needs design data, reference the embedded tables in UI/UX Pro Max rather than duplicating.
UI/UX Pro Max v7.1 — All data embedded. All code production-ready. All procedures executable. All skills cross-referenced.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.