From c2d1a31630300d92815200a451745f32740d6d5b Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 22 Jan 2026 15:32:15 -0800 Subject: [PATCH 1/3] Initial Spark Skill --- .../spark/skills/spark-app-template/SKILL.md | 490 +++++++++ .../references/color-palettes.md | 519 ++++++++++ .../references/component-patterns.md | 977 ++++++++++++++++++ .../references/design-system.md | 591 +++++++++++ .../references/performance-checklist.md | 628 +++++++++++ .../references/prd-template.md | 302 ++++++ .../references/radix-migration-guide.md | 279 +++++ .../references/typography-pairings.md | 475 +++++++++ .../stacks/complex-application.md | 896 ++++++++++++++++ .../stacks/content-showcase.md | 452 ++++++++ .../stacks/data-dashboard.md | 445 ++++++++ .../stacks/default-webapp.md | 427 ++++++++ 12 files changed, 6481 insertions(+) create mode 100644 plugins/spark/skills/spark-app-template/SKILL.md create mode 100644 plugins/spark/skills/spark-app-template/references/color-palettes.md create mode 100644 plugins/spark/skills/spark-app-template/references/component-patterns.md create mode 100644 plugins/spark/skills/spark-app-template/references/design-system.md create mode 100644 plugins/spark/skills/spark-app-template/references/performance-checklist.md create mode 100644 plugins/spark/skills/spark-app-template/references/prd-template.md create mode 100644 plugins/spark/skills/spark-app-template/references/radix-migration-guide.md create mode 100644 plugins/spark/skills/spark-app-template/references/typography-pairings.md create mode 100644 plugins/spark/skills/spark-app-template/stacks/complex-application.md create mode 100644 plugins/spark/skills/spark-app-template/stacks/content-showcase.md create mode 100644 plugins/spark/skills/spark-app-template/stacks/data-dashboard.md create mode 100644 plugins/spark/skills/spark-app-template/stacks/default-webapp.md diff --git a/plugins/spark/skills/spark-app-template/SKILL.md b/plugins/spark/skills/spark-app-template/SKILL.md new file mode 100644 index 0000000..697ebf5 --- /dev/null +++ b/plugins/spark/skills/spark-app-template/SKILL.md @@ -0,0 +1,490 @@ +--- +name: spark-app-template +description: Comprehensive guidance for building web apps with opinionated defaults for tech stack, design system, and code standards. Use when user wants to create a new web application, dashboard, or interactive interface. Provides tech choices, styling guidance, project structure, and design philosophy to get users up and running quickly with a fully functional, beautiful web app. +--- + +# Spark App Template + +## Purpose + +Spark App Template provides battle-tested defaults and comprehensive guidance for building web applications in 2026. When a user asks to build a web app, this skill provides the complete technical foundation and design direction to create beautiful, functional applications quickly. + +## When to Use This Skill + +Activate Spark App Template when the user: +- Wants to build a new web application from scratch +- Asks "what stack should I use?" +- Needs guidance on design, styling, or tech choices +- Wants to start a dashboard, interactive tool, data app, or content site +- Requests help choosing between frameworks, libraries, or approaches + +## Quick Start Workflow + +**CRITICAL**: Follow this exact order to avoid configuration errors: + +1. **Create project**: `pnpm create vite@latest my-app --template react-ts` +2. **Install base dependencies**: `pnpm install` +3. **Configure TypeScript path aliases** (tsconfig.json AND tsconfig.app.json - see Step 1 below) +4. **Install Tailwind and tooling**: `pnpm add -D tailwindcss @tailwindcss/vite @biomejs/biome` +5. **Configure vite.config.ts**: Add Tailwind plugin and path aliases +6. **Initialize shadcn**: `pnpm dlx shadcn@latest init` (will now succeed with proper aliases) +7. **Install required shadcn components**: `pnpm dlx shadcn add button card avatar ...` (BEFORE writing components) +8. **Install additional packages**: TanStack Router, Query, Zustand, etc. +9. **Write custom components** (NOW safe to import from `@/components/ui/*`) +10. **Configure routing and state management** +11. **Implement features** + +**Steps 3, 6, and 7 must happen in this exact order** to avoid TypeScript errors and failed shadcn installations. + +## Complexity Levels + +Understanding complexity helps choose the right stack variation and design approach: + +1. **Micro Tool** (single-purpose) + - Examples: Calculator, converter, color picker, timer + - Stack: `stacks/default-webapp.md` + - Focus: Simple, focused UI with minimal state + +2. **Content Showcase** (information-focused) + - Examples: Landing page, portfolio, blog, documentation + - Stack: `stacks/content-showcase.md` + - Focus: Typography, reading experience, visual hierarchy + +3. **Light Application** (multiple features with basic state) + - Examples: Todo list, meal planner, expense tracker + - Stack: `stacks/default-webapp.md` + - Focus: Feature clarity, data persistence, user flows + +4. **Complex Application** (advanced functionality, multiple views) + - Examples: CRM, analytics dashboard, project management tool + - Stack: `stacks/complex-application.md` or `stacks/data-dashboard.md` + - Focus: Navigation, state management, performance optimization + +## Core Tech Stack (Shared Foundation) + +All Spark App Template applications use this battle-tested foundation: + +### Build & Development +- **Build Tool**: Vite (latest stable) +- **Framework**: React 19+ (leverages new compiler, hooks, and features) +- **Language**: TypeScript +- **Package Manager**: pnpm +- **Linting**: Biome (ESLint fallback for complex plugins) + +### Routing & Data +- **Routing**: TanStack Router (file-based, type-safe) +- **Data Fetching**: TanStack Query +- **Forms**: react-hook-form + Zod validation + +### Styling & UI +- **Styling**: Tailwind CSS v4+ (modern @import syntax) +- **Components**: shadcn/ui (New York style, 45+ components) +- **Icons**: Lucide React (1000+ icons) +- **Color System**: Radix Colors (OKLCH format) +- **Theme**: next-themes (single theme default, dark mode optional) + +### Utilities & Enhancement +- **Animation**: Motion (formerly framer-motion) +- **Notifications**: Sonner +- **Utilities**: CVA (or Tailwind Variants) + clsx + tailwind-merge +- **Error Handling**: react-error-boundary + +## Stack Variations + +All variations share the core foundation above. These templates add specific packages and design guidance: + +### Default Web App (`stacks/default-webapp.md`) +- **Use for**: Most applications, general-purpose tools +- **Additive packages**: None +- **Design focus**: Clean, modern, functional + +### Data Dashboard (`stacks/data-dashboard.md`) +- **Use for**: Analytics, admin panels, data visualization +- **Additive packages**: Recharts (charts), date-fns (date handling) +- **Design focus**: Data density, hierarchical information, scanning patterns + +### Content Showcase (`stacks/content-showcase.md`) +- **Use for**: Marketing sites, portfolios, blogs, documentation +- **Additive packages**: marked (markdown parsing) +- **Design focus**: Typography scale, reading experience, whitespace + +### Complex Application (`stacks/complex-application.md`) +- **Use for**: Multi-view apps, SaaS platforms, enterprise tools +- **Additive packages**: Zustand (state management), date-fns +- **Design focus**: Navigation patterns, state architecture, performance + +## React 19+ Features (Production-Ready) + +Enable these modern React capabilities: + +### React Compiler +- **Status**: Production-ready, battle-tested at Meta +- **Benefits**: Auto-memoization, significantly faster initial loads and interactions +- **Setup**: Compatible with React 17+, configure via compiler config + +### useActionState Hook +- **Use for**: Form handling, async actions, loading states +- **Benefits**: Simplified state management, built-in async handling +- **Pattern**: Consolidates form state, pending state, and error handling + +### useOptimistic Hook +- **Use for**: Instant UI updates before server confirmation +- **Benefits**: Improved perceived performance, better UX +- **Pattern**: Optimistic updates with automatic rollback on failure + +### Server Components +- **Status**: Stable in React 19+ (framework mode only) +- **Frameworks**: Next.js, TanStack Start +- **Note**: For Vite + React SPA apps, use client-side rendering + +## Project Structure Template + +``` +my-app/ +├── index.html +├── package.json +├── tsconfig.json +├── vite.config.ts +├── tailwind.config.js +├── biome.json +├── src/ +│ ├── main.tsx # App entry point +│ ├── App.tsx # Root component +│ ├── index.css # Global styles + theme +│ ├── components/ +│ │ ├── ui/ # shadcn components (don't edit) +│ │ └── ... # Custom components +│ ├── hooks/ +│ │ └── use-mobile.tsx +│ ├── lib/ +│ │ ├── utils.ts # cn() utility +│ │ └── data.ts # Data schemas (if needed) +│ └── routes/ # TanStack Router routes +│ └── __root.tsx +└── public/ # Static assets +``` + +## Design Philosophy + +Beautiful web applications transcend mere functionality - they evoke emotion and form memorable experiences. Follow these principles: + +### Core Principles +1. **Simplicity Through Reduction**: Remove until reaching the simplest effective solution +2. **Material Honesty**: Digital materials have unique properties; embrace them +3. **Obsessive Detail**: Excellence emerges from hundreds of thoughtful decisions +4. **Coherent Design Language**: Every element should feel like part of a unified system +5. **Distinctive Visual Identity**: Create memorable aesthetics, not generic patterns + +### Critical Requirements +- **Use OKLCH color format** (mandatory for 2026) +- **Avoid overused fonts**: Inter, Roboto, Arial, Space Grotesk +- **Choose distinctive typography**: See `references/typography-pairings.md` +- **Validate color contrast**: WCAG AA (4.5:1 normal, 3:1 large text) +- **Single theme by default**: No dark mode unless explicitly requested +- **Variable fonts**: Use single variable font files for performance + +See `references/design-system.md` for comprehensive design guidance. + +## Performance Targets (Core Web Vitals) + +Optimize for these metrics: +- **INP** (Interaction to Next Paint): < 200ms +- **LCP** (Largest Contentful Paint): < 2.5s +- **CLS** (Cumulative Layout Shift): < 0.1 + +Tools to achieve targets: +- React Compiler for automatic memoization +- Vite code-splitting and lazy loading +- Image optimization (WebP, AVIF, lazy loading) +- Font optimization (variable fonts, font-display: swap) + +See `references/performance-checklist.md` for detailed optimization strategies. + +## References + +Access detailed guidance in the `references/` directory: + +1. **design-system.md** - Comprehensive design philosophy, spatial composition, backgrounds, micro-interactions +2. **typography-pairings.md** - Distinctive font combinations with personality guidance +3. **color-palettes.md** - Pre-curated OKLCH palettes with WCAG validation +4. **component-patterns.md** - Common shadcn compositions and usage patterns +5. **performance-checklist.md** - Web Vitals optimization, React Compiler setup +6. **prd-template.md** - Simplified planning framework for new apps +7. **radix-migration-guide.md** - Base UI migration path for Radix concerns + +## Implementation Workflow + +### Step 1: Initialize Project + +**CRITICAL**: Configure path aliases BEFORE running `shadcn init` to avoid validation errors. + +```bash +# Create Vite project +pnpm create vite@latest my-app --template react-ts +# Note: Working directory is now my-app/ - no need to cd + +# Install dependencies +pnpm install + +# Add Tailwind CSS and tooling +pnpm add -D tailwindcss@latest @tailwindcss/vite +pnpm add -D @biomejs/biome +pnpm add -D @tanstack/router-plugin +``` + +**Configure TypeScript Path Aliases** (Required for shadcn): + +Update `tsconfig.json`*: +```json +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} +``` + +Update `tsconfig.app.json`*: +```json +{ + "compilerOptions": { + // ... existing options ... + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} +``` + +Update `vite.config.ts`*: +```typescript +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' +import { TanStackRouterVite } from '@tanstack/router-plugin/vite' +import path from 'path' + +export default defineConfig({ + plugins: [ + TanStackRouterVite(), + react(), + tailwindcss(), + ], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, +}) +``` + +_* Configuration samples based on Vite + React + TypeScript template structure_ + +**Now shadcn init will succeed**: + +```bash +# Initialize shadcn (path aliases now configured) +pnpm dlx shadcn@latest init + +# CRITICAL: Install shadcn components BEFORE writing custom components +# Identify which components you need first +pnpm dlx shadcn@latest add button card input form dialog avatar badge separator + +# Add TanStack packages +pnpm add @tanstack/react-router @tanstack/react-query + +# Add utilities +pnpm add lucide-react motion sonner react-hook-form zod @hookform/resolvers +pnpm add clsx tailwind-merge class-variance-authority +pnpm add react-error-boundary next-themes +``` + +### Step 2: Configure Project + +See stack templates in `stacks/` for specific configuration examples. + +### Step 3: Create PRD (Optional but Recommended) + +Use `references/prd-template.md` to plan: +- Purpose and mission +- Complexity level +- Essential features +- Design direction +- Color and typography choices + +### Step 4: Install shadcn Components FIRST + +**CRITICAL: Component Installation Order** + +ALWAYS install shadcn components BEFORE writing custom components that import them. This prevents TypeScript errors and failed builds. + +❌ **WRONG ORDER** (causes errors): +```bash +# 1. Write PersonDetail.tsx that imports '@/components/ui/card' +# 2. Run pnpm dlx shadcn add card +# 3. Fix TypeScript errors 'Cannot find module @/components/ui/card' +``` + +✅ **CORRECT ORDER**: +```bash +# 1. Plan which shadcn components you need +# Example: Card, Avatar, Badge, Separator, Button + +# 2. Install ALL required shadcn components FIRST +pnpm dlx shadcn@latest add card avatar badge separator button + +# 3. Verify installation +ls src/components/ui/ # Should show: card.tsx, avatar.tsx, badge.tsx, etc. + +# 4. NOW write PersonDetail.tsx that imports from '@/components/ui/*' +# TypeScript will have proper types and components will exist +``` + +**Planning Checklist**: +1. List all UI components needed for your app +2. Identify which are shadcn components (Card, Button, etc.) +3. Run single `shadcn add` command with all components +4. Verify they exist in `src/components/ui/` +5. Write your custom components that import them + +### Step 5: Implement with Best Practices + +- Follow shadcn component patterns +- Use OKLCH colors in `:root` CSS variables +- Implement responsive design (mobile-first) +- Add error boundaries +- Optimize images and fonts +- Test Core Web Vitals + +## Common Patterns + +### Theme Configuration (index.css) + +```css +@import 'tailwindcss'; + +:root { + /* OKLCH colors - mandatory format */ + --background: oklch(0.97 0.01 75); + --foreground: oklch(0.25 0.02 55); + --primary: oklch(0.52 0.14 155); + --accent: oklch(0.72 0.13 55); + + /* Add more theme variables */ + --radius: 0.75rem; +} + +@theme { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-primary: var(--primary); + --color-accent: var(--accent); + + /* Radius system */ + --radius-sm: calc(var(--radius) * 0.5); + --radius-md: var(--radius); + --radius-lg: calc(var(--radius) * 1.5); +} +``` + +_Note: Uses Tailwind CSS v4+ @import syntax. For v3, use @tailwind directives instead._ + +### Form Handling + +```tsx +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; + +const schema = z.object({ + email: z.string().email(), + password: z.string().min(8) +}); + +function LoginForm() { + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: { email: '', password: '' } + }); + + async function onSubmit(data: z.infer) { + // Handle form submission + } + + return
...
; +} +``` + +### Data Fetching + +```tsx +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; + +function UserList() { + const queryClient = useQueryClient(); + + const { data, isLoading } = useQuery({ + queryKey: ['users'], + queryFn: fetchUsers + }); + + const createUser = useMutation({ + mutationFn: createUserAPI, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['users'] }); + } + }); + + if (isLoading) return ; + return ; +} +``` + +## Troubleshooting + +### Radix UI Maintenance Concerns + +Radix UI is receiving fewer updates. For new projects or migration concerns: +- See `references/radix-migration-guide.md` for Base UI migration path +- shadcn/ui now supports Base UI as an alternative +- React Aria is another excellent option (Adobe-backed, superior accessibility) + +### Performance Issues + +If app feels slow: +1. Enable React Compiler for automatic memoization +2. Check Core Web Vitals in Chrome DevTools +3. Use `references/performance-checklist.md` +4. Consider code-splitting with TanStack Router's lazy loading + +### Build Tool Alternatives + +Newer Vite versions with Rolldown bundler may offer significantly faster builds when production-ready. Monitor for stable releases. + +## System Requirements + +- **Node.js**: 18+ (or current LTS version) +- **Package Manager**: pnpm recommended for performance +- **OS**: macOS, Linux, or Windows with WSL2 + +## Next Steps + +After scaffolding: +1. Review the stack template for your complexity level +2. Consult design references for styling +3. Create a PRD to plan features and design +4. Implement following best practices +5. Optimize for Core Web Vitals +6. Deploy to production (Vercel, Netlify, etc.) + +--- + +**Remember**: The goal is production-ready, beautiful, and performant web applications. Start simple, iterate based on user needs, and always prioritize user experience over technical complexity. diff --git a/plugins/spark/skills/spark-app-template/references/color-palettes.md b/plugins/spark/skills/spark-app-template/references/color-palettes.md new file mode 100644 index 0000000..2f69afb --- /dev/null +++ b/plugins/spark/skills/spark-app-template/references/color-palettes.md @@ -0,0 +1,519 @@ +# Color Palettes - Spark App Template + +## OKLCH: The Color Format for 2026 + +OKLCH (Oklab Lightness Chroma Hue) is a perceptually uniform color space. Unlike RGB or HSL, equal numerical changes produce equal perceived differences. + +**Why OKLCH?** +- ✅ Perceptually uniform (50% lightness looks halfway between black and white) +- ✅ Predictable color manipulation +- ✅ Better gamut coverage (more vibrant colors possible) +- ✅ Native browser support (all modern browsers) +- ✅ Easier to maintain consistent contrast + +**OKLCH Format**: +```css +color: oklch(L C H / A); +/* L = Lightness (0-1, where 0.5 is truly middle gray) + C = Chroma (0-0.4, color intensity/saturation) + H = Hue (0-360 degrees) + A = Alpha (0-1, optional) */ +``` + +**Example**: +```css +--primary: oklch(0.55 0.15 265); +/* 55% lightness, medium chroma, blue-purple hue */ +``` + +## Color Palette Structure + +Every Spark App Template app should define these semantic color tokens: + +```css +:root { + /* Base colors */ + --background: oklch(...); + --foreground: oklch(...); + + /* Component backgrounds */ + --card: oklch(...); + --card-foreground: oklch(...); + --popover: oklch(...); + --popover-foreground: oklch(...); + + /* Action colors */ + --primary: oklch(...); + --primary-foreground: oklch(...); + --secondary: oklch(...); + --secondary-foreground: oklch(...); + --accent: oklch(...); + --accent-foreground: oklch(...); + --destructive: oklch(...); + --destructive-foreground: oklch(...); + + /* Supporting colors */ + --muted: oklch(...); + --muted-foreground: oklch(...); + --border: oklch(...); + --input: oklch(...); + --ring: oklch(...); + + /* Optional: Status colors */ + --success: oklch(...); + --warning: oklch(...); + --error: oklch(...); + --info: oklch(...); +} +``` + +## Pre-Curated Palettes + +### Palette 1: Professional Blue (Default) + +Modern, trustworthy, professional. Think Stripe, Linear, Vercel. + +```css +:root { + --background: oklch(0.98 0.01 240); + --foreground: oklch(0.20 0.02 240); + + --card: oklch(1 0 0); + --card-foreground: oklch(0.20 0.02 240); + + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.20 0.02 240); + + --primary: oklch(0.45 0.15 265); /* Deep blue */ + --primary-foreground: oklch(0.99 0 0); + + --secondary: oklch(0.94 0.02 240); + --secondary-foreground: oklch(0.30 0.02 240); + + --accent: oklch(0.68 0.16 240); /* Bright blue */ + --accent-foreground: oklch(0.20 0.02 240); + + --destructive: oklch(0.55 0.20 25); /* Red */ + --destructive-foreground: oklch(0.98 0 0); + + --muted: oklch(0.95 0.01 240); + --muted-foreground: oklch(0.48 0.02 240); + + --border: oklch(0.88 0.02 240); + --input: oklch(0.90 0.01 240); + --ring: oklch(0.45 0.15 265); + + /* Status colors */ + --success: oklch(0.60 0.14 145); /* Green */ + --warning: oklch(0.70 0.16 85); /* Yellow */ + --error: oklch(0.55 0.20 25); /* Red */ + --info: oklch(0.60 0.12 240); /* Blue */ + + --radius: 0.5rem; +} +``` + +**Contrast Checks**: +- Background → Foreground: 12.8:1 ✅ +- Primary → Primary Foreground: 8.2:1 ✅ +- Accent → Accent Foreground: 5.1:1 ✅ + +--- + +### Palette 2: Warm & Inviting + +Organic, friendly, approachable. Think Notion, Figma. + +```css +:root { + --background: oklch(0.98 0.005 85); /* Warm off-white */ + --foreground: oklch(0.22 0.01 75); + + --card: oklch(1 0 0); + --card-foreground: oklch(0.22 0.01 75); + + --primary: oklch(0.50 0.12 35); /* Warm orange-red */ + --primary-foreground: oklch(0.99 0 0); + + --secondary: oklch(0.94 0.02 85); + --secondary-foreground: oklch(0.30 0.01 75); + + --accent: oklch(0.70 0.14 55); /* Warm orange */ + --accent-foreground: oklch(0.22 0.01 75); + + --destructive: oklch(0.52 0.22 30); + --destructive-foreground: oklch(0.98 0 0); + + --muted: oklch(0.96 0.01 85); + --muted-foreground: oklch(0.50 0.01 75); + + --border: oklch(0.88 0.02 85); + --input: oklch(0.92 0.01 85); + --ring: oklch(0.50 0.12 35); + + --success: oklch(0.58 0.15 145); + --warning: oklch(0.75 0.14 85); + --error: oklch(0.52 0.22 30); + --info: oklch(0.58 0.12 240); + + --radius: 0.75rem; /* Larger radius for friendly feel */ +} +``` + +--- + +### Palette 3: Dark Mode Elegant + +Sophisticated dark theme with purple accents. + +```css +:root { + --background: oklch(0.15 0.01 265); /* Deep blue-black */ + --foreground: oklch(0.95 0.01 265); + + --card: oklch(0.18 0.01 265); + --card-foreground: oklch(0.95 0.01 265); + + --popover: oklch(0.18 0.01 265); + --popover-foreground: oklch(0.95 0.01 265); + + --primary: oklch(0.70 0.20 300); /* Bright purple */ + --primary-foreground: oklch(0.15 0.01 265); + + --secondary: oklch(0.22 0.02 265); + --secondary-foreground: oklch(0.85 0.01 265); + + --accent: oklch(0.65 0.25 330); /* Pink-purple */ + --accent-foreground: oklch(0.95 0.01 265); + + --destructive: oklch(0.60 0.24 25); + --destructive-foreground: oklch(0.95 0.01 265); + + --muted: oklch(0.22 0.02 265); + --muted-foreground: oklch(0.60 0.01 265); + + --border: oklch(0.25 0.02 265); + --input: oklch(0.22 0.02 265); + --ring: oklch(0.70 0.20 300); + + --success: oklch(0.65 0.16 145); + --warning: oklch(0.72 0.16 85); + --error: oklch(0.60 0.24 25); + --info: oklch(0.65 0.14 240); + + --radius: 0.5rem; +} +``` + +--- + +### Palette 4: Vibrant & Bold + +High energy, attention-grabbing. Think gaming, creative tools. + +```css +:root { + --background: oklch(0.98 0.01 265); + --foreground: oklch(0.15 0.02 265); + + --card: oklch(1 0 0); + --card-foreground: oklch(0.15 0.02 265); + + --primary: oklch(0.55 0.28 310); /* Hot magenta */ + --primary-foreground: oklch(0.99 0 0); + + --secondary: oklch(0.92 0.03 265); + --secondary-foreground: oklch(0.25 0.02 265); + + --accent: oklch(0.65 0.26 180); /* Cyan */ + --accent-foreground: oklch(0.15 0.02 265); + + --destructive: oklch(0.58 0.26 25); + --destructive-foreground: oklch(0.98 0 0); + + --muted: oklch(0.94 0.02 265); + --muted-foreground: oklch(0.50 0.02 265); + + --border: oklch(0.86 0.03 265); + --input: oklch(0.90 0.02 265); + --ring: oklch(0.55 0.28 310); + + --success: oklch(0.62 0.20 145); + --warning: oklch(0.72 0.18 85); + --error: oklch(0.58 0.26 25); + --info: oklch(0.62 0.16 240); + + --radius: 0.25rem; /* Sharp corners for edgy feel */ +} +``` + +--- + +### Palette 5: Minimalist Monochrome + +Pure focus, maximum clarity. Think Apple, minimalist tools. + +```css +:root { + --background: oklch(1 0 0); /* Pure white */ + --foreground: oklch(0.10 0 0); /* Near black */ + + --card: oklch(0.98 0 0); + --card-foreground: oklch(0.10 0 0); + + --popover: oklch(0.98 0 0); + --popover-foreground: oklch(0.10 0 0); + + --primary: oklch(0.10 0 0); /* Black */ + --primary-foreground: oklch(1 0 0); + + --secondary: oklch(0.94 0 0); + --secondary-foreground: oklch(0.20 0 0); + + --accent: oklch(0.40 0 0); /* Dark gray */ + --accent-foreground: oklch(1 0 0); + + --destructive: oklch(0.30 0 0); + --destructive-foreground: oklch(1 0 0); + + --muted: oklch(0.96 0 0); + --muted-foreground: oklch(0.50 0 0); + + --border: oklch(0.90 0 0); + --input: oklch(0.94 0 0); + --ring: oklch(0.10 0 0); + + --success: oklch(0.50 0 0); + --warning: oklch(0.50 0 0); + --error: oklch(0.30 0 0); + --info: oklch(0.50 0 0); + + --radius: 0rem; /* No rounded corners */ +} +``` + +--- + +### Palette 6: Nature-Inspired Green + +Calm, growth-focused, eco-friendly. Think sustainability, health. + +```css +:root { + --background: oklch(0.98 0.01 145); + --foreground: oklch(0.20 0.02 145); + + --card: oklch(1 0 0); + --card-foreground: oklch(0.20 0.02 145); + + --primary: oklch(0.48 0.14 155); /* Forest green */ + --primary-foreground: oklch(0.99 0 0); + + --secondary: oklch(0.94 0.02 145); + --secondary-foreground: oklch(0.28 0.02 145); + + --accent: oklch(0.65 0.16 135); /* Bright green */ + --accent-foreground: oklch(0.20 0.02 145); + + --destructive: oklch(0.55 0.20 25); + --destructive-foreground: oklch(0.98 0 0); + + --muted: oklch(0.96 0.01 145); + --muted-foreground: oklch(0.50 0.02 145); + + --border: oklch(0.88 0.02 145); + --input: oklch(0.92 0.01 145); + --ring: oklch(0.48 0.14 155); + + --success: oklch(0.62 0.16 145); + --warning: oklch(0.72 0.14 85); + --error: oklch(0.55 0.20 25); + --info: oklch(0.60 0.12 240); + + --radius: 1rem; /* Very rounded for organic feel */ +} +``` + +--- + +## WCAG Contrast Guidelines + +**Required Ratios**: +- **Normal text (< 18px)**: 4.5:1 (AA) or 7:1 (AAA) +- **Large text (≥ 18px or ≥ 14px bold)**: 3:1 (AA) or 4.5:1 (AAA) +- **UI components**: 3:1 minimum + +**Testing Your Colors**: +```css +/* Example: Check if primary text on background passes */ +background: oklch(0.98 0.01 240); /* Light */ +foreground: oklch(0.20 0.02 240); /* Dark */ +/* Contrast ratio: ~13:1 (Passes AAA for all text) ✅ */ +``` + +**Tools**: +- OKLCH Contrast Checker: oklch.com +- Who Can Use: whocanuse.com +- WebAIM Contrast Checker: webaim.org/resources/contrastchecker + +## Validating Your Palette + +Before using any palette, verify these pairings: + +```markdown +✅ Background → Foreground +✅ Card → Card Foreground +✅ Primary → Primary Foreground +✅ Secondary → Secondary Foreground +✅ Accent → Accent Foreground +✅ Muted Background → Muted Foreground +✅ Success/Warning/Error → Background +``` + +## Creating Custom Palettes + +### Step 1: Choose Your Hue + +```css +/* Blue family: 240-270 */ +--hue: 265; + +/* Red family: 0-30 */ +--hue: 25; + +/* Green family: 130-160 */ +--hue: 145; + +/* Purple family: 280-320 */ +--hue: 300; + +/* Orange family: 40-70 */ +--hue: 55; +``` + +### Step 2: Define Lightness Scale + +```css +/* Light theme scale */ +--l-100: 0.98; /* Lightest (backgrounds) */ +--l-200: 0.94; +--l-300: 0.88; +--l-400: 0.80; +--l-500: 0.60; /* Middle */ +--l-600: 0.45; /* Primary actions */ +--l-700: 0.30; +--l-800: 0.20; /* Darkest (text) */ +``` + +### Step 3: Set Chroma (Saturation) + +```css +/* Low chroma = subtle, professional */ +--c-low: 0.05; + +/* Medium chroma = balanced */ +--c-medium: 0.12; + +/* High chroma = vibrant */ +--c-high: 0.20; +``` + +### Step 4: Build Your Palette + +```css +:root { + --background: oklch(0.98 0.01 var(--hue)); + --foreground: oklch(0.20 0.02 var(--hue)); + --primary: oklch(0.45 0.15 var(--hue)); + --accent: oklch(0.68 0.16 var(--hue)); + /* Continue for all tokens */ +} +``` + +## Color Manipulation + +### Lighten/Darken + +```css +/* Original */ +--color: oklch(0.50 0.15 265); + +/* Lighter (increase L) */ +--color-light: oklch(0.70 0.15 265); + +/* Darker (decrease L) */ +--color-dark: oklch(0.30 0.15 265); +``` + +### More/Less Saturated + +```css +/* Original */ +--color: oklch(0.50 0.15 265); + +/* More saturated (increase C) */ +--color-vibrant: oklch(0.50 0.25 265); + +/* Less saturated (decrease C) */ +--color-muted: oklch(0.50 0.05 265); +``` + +### Shift Hue + +```css +/* Original blue */ +--color: oklch(0.50 0.15 265); + +/* Shift to purple */ +--color-purple: oklch(0.50 0.15 300); + +/* Shift to cyan */ +--color-cyan: oklch(0.50 0.15 200); +``` + +### Using color-mix() + +```css +/* Mix two colors */ +.mixed { + background: color-mix(in oklch, var(--primary) 70%, var(--accent) 30%); +} + +/* Lighten with white */ +.lightened { + background: color-mix(in oklch, var(--primary) 80%, white 20%); +} + +/* Darken with black */ +.darkened { + background: color-mix(in oklch, var(--primary) 80%, black 20%); +} +``` + +## Implementing Dark Mode (Optional) + +Only implement dark mode if explicitly requested. Single theme is preferred. + +```css +@media (prefers-color-scheme: dark) { + :root { + --background: oklch(0.15 0.01 265); + --foreground: oklch(0.95 0.01 265); + /* Invert lightness scale */ + } +} +``` + +Or use `next-themes` for manual toggle: + +```tsx +import { ThemeProvider } from 'next-themes'; + + + + +``` + +--- + +**Remember**: Color is emotional. Choose palettes that align with your brand personality, validate contrast for accessibility, and test with real users to ensure your colors communicate effectively. diff --git a/plugins/spark/skills/spark-app-template/references/component-patterns.md b/plugins/spark/skills/spark-app-template/references/component-patterns.md new file mode 100644 index 0000000..6578ecd --- /dev/null +++ b/plugins/spark/skills/spark-app-template/references/component-patterns.md @@ -0,0 +1,977 @@ +# Component Patterns - Spark App Template + +## shadcn/ui Component Usage Guide + +This guide covers common patterns and best practices for using shadcn/ui components in Spark App Template applications. + +## Core Principles + +1. **Copy, Don't Import**: shadcn components are copied into your project, giving you full ownership +2. **Customize Freely**: Edit components in `src/components/ui/` to match your needs +3. **Compose, Don't Duplicate**: Build complex components by composing primitives +4. **Maintain Consistency**: Follow established patterns across your app + +## Common Patterns + +### Forms with Validation + +The canonical pattern for forms combines Form + react-hook-form + Zod: + +```tsx +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { Button } from '@/components/ui/button'; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; + +const formSchema = z.object({ + username: z.string().min(2, 'Username must be at least 2 characters'), + email: z.string().email('Invalid email address'), +}); + +export function ProfileForm() { + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + username: '', + email: '', + }, + }); + + function onSubmit(values: z.infer) { + console.log(values); + } + + return ( +
+ + ( + + Username + + + + + This is your public display name. + + + + )} + /> + + ( + + Email + + + + + + )} + /> + + + + + ); +} +``` + +### Dialogs & Modals + +Standard pattern for modal dialogs: + +```tsx +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; + +export function DeleteConfirmDialog() { + return ( + + + + + + + Are you sure? + + This action cannot be undone. This will permanently delete your + account and remove your data from our servers. + + + + + + + + + ); +} +``` + +### Cards with Actions + +Pattern for content cards with interactive elements: + +```tsx +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; + +interface ProjectCardProps { + title: string; + description: string; + status: 'active' | 'archived'; + onEdit: () => void; + onDelete: () => void; +} + +export function ProjectCard({ + title, + description, + status, + onEdit, + onDelete, +}: ProjectCardProps) { + return ( + + +
+ {title} + + {status} + +
+ {description} +
+ + + + +
+ ); +} +``` + +### Data Tables + +Pattern for displaying tabular data: + +```tsx +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { MoreHorizontal } from 'lucide-react'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; + +interface User { + id: string; + name: string; + email: string; + role: string; + status: 'active' | 'inactive'; +} + +export function UserTable({ users }: { users: User[] }) { + return ( + + + + Name + Email + Role + Status + + + + + {users.map((user) => ( + + {user.name} + {user.email} + {user.role} + + + {user.status} + + + + + + + + + Edit + Delete + + + + + ))} + +
+ ); +} +``` + +### Select Dropdowns + +Pattern for select/dropdown inputs: + +```tsx +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; + +export function StatusSelect() { + return ( + + ); +} +``` + +With react-hook-form: + +```tsx + ( + + Status + + + + )} +/> +``` + +### Toast Notifications + +Using Sonner for toast notifications: + +```tsx +import { toast } from 'sonner'; +import { Button } from '@/components/ui/button'; + +export function ToastExample() { + return ( +
+ + + + +
+ ); +} +``` + +### Tabs for Content Organization + +```tsx +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Card, CardContent } from '@/components/ui/card'; + +export function SettingsTabs() { + return ( + + + General + Security + Notifications + + + + + General settings content + + + + + + + Security settings content + + + + + ); +} +``` + +### Accordion for FAQ / Expandable Content + +```tsx +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; + +export function FAQ() { + return ( + + + How do I get started? + + Getting started is easy! Simply sign up for an account and follow + our onboarding guide. + + + + What payment methods do you accept? + + We accept all major credit cards, PayPal, and bank transfers. + + + + ); +} +``` + +### Command Palette (⌘K) + +```tsx +import { useEffect, useState } from 'react'; +import { useNavigate } from '@tanstack/react-router'; +import { + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, +} from '@/components/ui/command'; + +export function CommandPalette() { + const [open, setOpen] = useState(false); + const navigate = useNavigate(); + + useEffect(() => { + const down = (e: KeyboardEvent) => { + if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + setOpen((open) => !open); + } + }; + document.addEventListener('keydown', down); + return () => document.removeEventListener('keydown', down); + }, []); + + return ( + + + + No results found. + + navigate({ to: '/dashboard' })}> + Dashboard + + navigate({ to: '/settings' })}> + Settings + + + + + Create New Project + Invite Team Member + + + + ); +} +``` + +### Sheet (Drawer) for Mobile Navigation + +```tsx +import { Menu } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from '@/components/ui/sheet'; + +export function MobileNav() { + return ( + + + + + + + Navigation + + Navigate to different sections of the app + + + + + + ); +} +``` + +### Popovers for Contextual Content + +```tsx +import { Button } from '@/components/ui/button'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Calendar } from '@/components/ui/calendar'; + +export function DatePickerPopover() { + const [date, setDate] = useState(); + + return ( + + + + + + + + + ); +} +``` + +### Alert / Alert Dialog for Important Messages + +**Alert** (non-blocking): +```tsx +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; +import { InfoIcon } from 'lucide-react'; + +export function InfoAlert() { + return ( + + + Heads up! + + You can add components to your app using the CLI. + + + ); +} +``` + +**AlertDialog** (blocking confirmation): +```tsx +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog'; +import { Button } from '@/components/ui/button'; + +export function DeleteAlert() { + return ( + + + + + + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete your + account and remove your data from our servers. + + + + Cancel + Continue + + + + ); +} +``` + +## Composition Patterns + +### Combining Components + +Build complex UIs by composing primitives: + +```tsx +// Filter panel combining multiple components +export function FilterPanel() { + return ( + + + Filters + + +
+ + +
+ +
+ + + + + + + + + +
+ + + +
+ + +
+
+
+ ); +} +``` + +## Card Layout Patterns + +### Understanding Card Spacing + +shadcn Card components come with default spacing optimized for content cards but may need adjustment when used as structural containers. + +**Default Card structure:** +- `CardHeader`: Uses `p-6` padding (1.5rem/24px on all sides) +- `CardContent`: Uses `p-6 pt-0` padding (removes top padding to prevent double spacing with CardHeader) +- Card wrapper: May include `py-6` and `gap-6` in some versions + +**Key principle:** "Keep card content focused - cards work best when covering one topic" ([source](https://medium.com/@rivainasution/shadcn-ui-react-series-part-6-card-the-container-i-use-everywhere-without-thinking-a6f9dd711d18)) + +### When to Override Card Defaults + +shadcn explicitly supports overriding defaults via className props ([Card docs](https://ui.shadcn.com/docs/components/card)). Here's when to use each pattern: + +**Content Cards (Keep defaults):** +Use default spacing when Card represents discrete content: + +```tsx +// ✅ Good - Content card benefits from default spacing + + + Project Updates + Latest changes to your project + + +

Each card should cover one focused topic

+
+
+``` + +**Layout Cards (Override with p-0):** +Remove spacing when Card serves as a structural container: + +```tsx +// ✅ Good - Layout card with full control over spacing + +
+

Panel Header

+
+
+ {/* Panel content with independent spacing */} +
+
+``` + +**Partial Overrides:** +When you need asymmetric spacing control: + +```tsx +// ✅ Good - Remove top padding only, preserve bottom + + + {/* Controlled top spacing, natural bottom spacing */} + + +``` + +### Full-Height Card Layouts + +For Cards that need to fill available vertical space: + +```tsx +// Pattern: flex-col on Card, flex-1 on content area + +
+

Fixed Header

+
+
+ {/* Scrollable content fills remaining space */} +
+
+ +
+
+``` + +**Why this works:** Setting `flex: 1` on the content area allows it to "grow and take all available space" while pushing footers to the bottom ([CSS-Tricks](https://css-tricks.com/boxes-fill-height-dont-squish/)). + +### Cards with ScrollArea + +ScrollArea requires careful padding coordination: + +```tsx +// ✅ Correct pattern - padding inside ScrollArea + + + Scrollable Content + + + + {/* Padding applied here, not on CardContent */} +
+ {items.map(item =>
{item.content}
)} +
+
+
+
+``` + +**Key points:** +- Always set explicit height on ScrollArea (e.g., `h-[400px]`) +- Place padding inside ScrollArea, not on parent CardContent +- This prevents double scrollbars and layout issues ([Medium - ScrollArea](https://medium.com/@rivainasution/shadcn-ui-react-series-part-9-scroll-area-controlled-scrolling-without-layout-hacks-4263c6f899f4)) + +### Overflow-Hidden Considerations + +The `overflow-hidden` class is commonly needed for layout cards but intentionally not default: + +```tsx +// When to use overflow-hidden: + {/* Clips content to rounded corners */} + {/* Image respects border-radius */} + ... + +``` + +There's ongoing discussion about making this default, but it remains optional to support both `overflow: hidden` and `overflow: scroll` use cases ([GitHub #2885](https://github.com/shadcn-ui/ui/issues/2885)). + +### Split Panel Pattern + +Common pattern for side-by-side cards in dashboard layouts: + +```tsx +
+ +
+

Left Panel

+
+
+ {/* Scrollable left content */} +
+
+ + +
+

Right Panel

+
+
+ {/* Scrollable right content */} +
+
+
+``` + +### Nested Cards Pattern + +Outer layout card with inner content cards: + +```tsx + + {/* Outer: Layout card with no default spacing */} +
+

Main Panel

+
+ +
+
+ {/* Inner: Content cards keep default spacing */} + + + Section A + + + Content benefits from Card's natural padding + + + + + + Section B + + + Another content card with default spacing + + +
+
+
+``` + +### Card Spacing Decision Tree + +Ask yourself these questions when using Cards: + +1. **Is this a content card or layout card?** + - Content: Represents discrete information → Keep defaults + - Layout: Structural container → Override with `p-0 gap-0` + +2. **Does the Card need to fill available height?** + - Yes → Use `flex flex-col` on Card, `flex-1` on content area + - Add `overflow-auto` on content area if scrolling needed + +3. **Are there nested Cards?** + - Outer Card: Override spacing (`p-0 gap-0`) for layout control + - Inner Cards: Keep defaults for proper content spacing + +4. **Using ScrollArea inside Card?** + - CardContent needs `p-0` + - Move padding into ScrollArea + - Set explicit height on ScrollArea + +5. **Need clipped corners for images?** + - Add `overflow-hidden` to Card wrapper + +## Best Practices + +### 1. Use Variants + +```tsx + + + + +``` + +### 2. Consistent Sizing + +```tsx + + + + +``` + +### 3. Accessible Labels + +```tsx + + +``` + +### 4. Loading States + +```tsx + +``` + +### 5. Empty States + +```tsx +{items.length === 0 ? ( +
+ +

No projects found

+

+ Get started by creating a new project. +

+ +
+) : ( + +)} +``` + +## Common Mistakes to Avoid + +❌ **Don't** nest interactive elements: +```tsx + +``` + +✅ **Do** use asChild: +```tsx + +``` + +❌ **Don't** ignore form validation feedback: +```tsx + {/* Missing FormMessage */} +``` + +✅ **Do** show validation errors: +```tsx + + + + + {/* Shows errors */} + +``` + +❌ **Don't** use inline styles for theming: +```tsx + + +// ✅ Good: Memoized function (if React Compiler not enabled) +const handleClickItem = useCallback(() => { + handleClick(item.id); +}, [item.id]); + + + +// ✅ Best: With React Compiler, write naturally + +// Compiler auto-memoizes this +``` + +## Network Optimization + +### API Request Batching + +```tsx +// Batch multiple queries +const results = useQueries({ + queries: [ + { queryKey: ['user', userId], queryFn: fetchUser }, + { queryKey: ['posts', userId], queryFn: fetchPosts }, + { queryKey: ['comments', userId], queryFn: fetchComments }, + ], +}); +``` + +### Request Deduplication + +TanStack Query automatically deduplicates identical concurrent requests. + +### Compression + +Ensure server sends compressed responses: + +```http +Content-Encoding: gzip +Content-Encoding: br (Brotli, even better) +``` + +## Production Build Optimization + +### Vite Build Settings + +```typescript +// vite.config.ts +export default defineConfig({ + build: { + target: 'es2020', + minify: 'terser', + rollupOptions: { + output: { + manualChunks: { + vendor: ['react', 'react-dom'], + router: ['@tanstack/react-router'], + ui: ['lucide-react', 'motion'], + }, + }, + }, + }, +}); +``` + +### Environment-Specific Code + +```tsx +if (import.meta.env.DEV) { + // Development-only code (stripped in production) + console.log('Debug info'); +} + +if (import.meta.env.PROD) { + // Production-only code + initAnalytics(); +} +``` + +## Performance Checklist + +Use this checklist before deploying: + +- [ ] **Core Web Vitals**: INP < 200ms, LCP < 2.5s, CLS < 0.1 +- [ ] **React Compiler**: Enabled and verified working +- [ ] **Images**: Using WebP/AVIF, lazy loaded, responsive +- [ ] **Fonts**: Variable fonts, font-display: swap, preload critical +- [ ] **Bundle Size**: Analyzed, < 200KB initial bundle gzipped +- [ ] **Code Splitting**: Routes split, heavy components lazy loaded +- [ ] **Caching**: TanStack Query configured, stale times set +- [ ] **Animations**: GPU-accelerated properties only +- [ ] **Virtual Scrolling**: Implemented for lists > 100 items +- [ ] **Network**: Requests batched, compression enabled +- [ ] **Production Build**: Minified, tree-shaken, optimized chunks + +## Monitoring + +Set up continuous monitoring: + +```tsx +// Track Core Web Vitals +import { onCLS, onINP, onLCP } from 'web-vitals'; + +const sendToAnalytics = (metric) => { + // Send to your analytics service + fetch('/api/analytics', { + method: 'POST', + body: JSON.stringify(metric), + }); +}; + +onCLS(sendToAnalytics); +onINP(sendToAnalytics); +onLCP(sendToAnalytics); +``` + +**Tools**: +- Google Search Console (real user metrics) +- Chrome DevTools (Lighthouse) +- WebPageTest (comprehensive testing) +- Vercel Analytics (if deploying on Vercel) + +--- + +**Remember**: Performance is a feature, not an afterthought. Budget your performance from the start, measure continuously, and optimize based on real-world data, not assumptions. diff --git a/plugins/spark/skills/spark-app-template/references/prd-template.md b/plugins/spark/skills/spark-app-template/references/prd-template.md new file mode 100644 index 0000000..efc332f --- /dev/null +++ b/plugins/spark/skills/spark-app-template/references/prd-template.md @@ -0,0 +1,302 @@ +# PRD Template - Spark App Template + +## Purpose + +A Product Requirements Document (PRD) helps structure thinking before implementation. Use this simplified template to plan features, design direction, and success criteria. + +## When to Use + +- Planning new applications +- Adding major features +- Clarifying requirements +- Aligning design decisions +- Documenting design system choices + +## Template + +--- + +# [Application Name] + +## Purpose Statement + +*(One sentence describing what this web app does and why it exists)* + +Example: "A weekly meal planning application that helps users organize their meals and automatically generates consolidated grocery shopping lists." + +--- + +## Experience Qualities + +*(Three adjectives with one-sentence elaborations defining the user experience)* + +1. **[Adjective]** - [One-sentence elaboration] +2. **[Adjective]** - [One-sentence elaboration] +3. **[Adjective]** - [One-sentence elaboration] + +Example: +1. **Effortless** - Planning meals should feel intuitive and quick, not like a chore +2. **Organized** - Clear visual structure makes the week's meals easy to scan and manage +3. **Satisfying** - Generating a shopping list from planned meals should feel like magic + +--- + +## Complexity Level + +*(Select ONE and explain why)* + +- [ ] **Micro Tool** (single-purpose application) + - *Why*: [One-sentence justification] + - *Example*: Calculator app, color picker, unit converter + +- [ ] **Content Showcase** (information-focused) + - *Why*: [One-sentence justification] + - *Example*: Marketing landing page, portfolio, blog + +- [ ] **Light Application** (multiple features with basic state) + - *Why*: [One-sentence justification] + - *Example*: Todo list, meal planner, expense tracker + +- [ ] **Complex Application** (advanced functionality, multiple views) + - *Why*: [One-sentence justification] + - *Example*: CRM, analytics dashboard, project management tool + +--- + +## Essential Features + +*(For each core feature, document:)* + +### Feature 1: [Feature Name] + +- **Functionality**: What it does +- **Purpose**: Why it matters to users +- **Trigger**: How the user initiates it +- **Progression**: Terse UX flow (use → to separate steps) + - Example: `Click slot → Enter meal name + ingredients → Save → Meal saved to slot` +- **Success Criteria**: How we'll know it works + +### Feature 2: [Feature Name] + +*(Repeat structure above)* + +--- + +## Edge Case Handling + +*(How will the app handle unexpected situations?)* + +- **[Edge Case Name]**: [Short one-line solution] +- **[Edge Case Name]**: [Short one-line solution] + +Example: +- **Empty week**: Show friendly empty state with prompt to add first meal +- **No ingredients**: Allow meals without ingredients (eating out, leftovers) +- **Duplicate ingredients**: Combine same ingredients across meals in grocery list + +--- + +## Design Direction + +*(What specific feelings should the design evoke in users?)* + +Example: "The design should feel like a well-organized kitchen bulletin board - warm, practical, and inviting. It should reduce the cognitive load of meal planning by presenting information clearly and making actions obvious." + +--- + +## Color Selection + +*(Describe the color scheme approach and specific colors. Use OKLCH format.)* + +### Color Palette + +- **Primary Color**: `oklch(L C H)` - [What it communicates] +- **Secondary Colors**: `oklch(L C H)` - [Their purposes] +- **Accent Color**: `oklch(L C H)` - [For CTAs and important elements] + +### Foreground/Background Pairings + +*(Document text colors on backgrounds with WCAG AA validation)* + +- `Background (name oklch(L C H))`: `Text color (oklch(L C H))` - Ratio [X.X:1] [✓ or ✗] + +Example: +- `Background (Warm Cream oklch(0.96 0.02 85))`: `Dark brown text (oklch(0.25 0.02 55))` - Ratio 8.5:1 ✓ +- `Primary (Herb Green oklch(0.55 0.15 145))`: `White text (oklch(0.99 0 0))` - Ratio 4.8:1 ✓ + +--- + +## Font Selection + +*(What characteristics should the typefaces convey, and which fonts should be used?)* + +**Chosen Fonts**: +- **Headings**: [Font Name] - [Character description] +- **Body**: [Font Name] - [Character description] +- **Code** *(if applicable)*: [Font Name] + +### Typographic Hierarchy + +- **H1 (Page Title)**: [Font] [Weight]/[Size]px/[Tracking] +- **H2 (Section Headers)**: [Font] [Weight]/[Size]px/[Tracking] +- **H3 (Subsections)**: [Font] [Weight]/[Size]px/[Tracking] +- **Body (Main Text)**: [Font] [Weight]/[Size]px +- **Caption (Supporting)**: [Font] [Weight]/[Size]px + +Example: +- **H1 (App Title)**: Space Grotesk Bold/32px/tight letter spacing +- **Body (Meal Names)**: Source Sans 3 Regular/16px +- **Caption (Ingredients)**: Source Sans 3 Regular/14px/muted color + +--- + +## Animations + +*(How should animations be used? Balance subtle functionality with moments of delight.)* + +Example: "Subtle animations reinforce actions without slowing down the experience - meals should 'pop' into place when added, and the grocery list should build item by item when generated, creating a satisfying moment of accomplishment." + +--- + +## Component Selection + +### UI Components + +*(Which shadcn components will be used?)* + +- **[Component Name]**: [Use case] +- **[Component Name]**: [Use case] + +Example: +- **Card**: For each day's meal container and the grocery list panel +- **Dialog**: For adding/editing meals with ingredient entry +- **Button**: Primary for generate list, secondary for add meal actions +- **Checkbox**: For checking off grocery items + +### Customizations + +*(Any custom components needed beyond shadcn?)* + +- **[Custom Component]**: [Why needed, what it does] + +Example: +- **Meal Slot Component**: Custom component showing empty state vs filled state with hover interactions +- **Ingredient Chip**: Removable tags for ingredients during entry + +### Component States + +*(How should interactive elements behave in different states?)* + +- **[Element] - [State]**: [Behavior/appearance] + +Example: +- **Empty meal slot**: Dashed border, muted plus icon, hover brightens +- **Filled meal slot**: Solid background, meal name visible, hover shows edit option +- **Grocery item checked**: Reduced opacity, strikethrough, checkbox filled + +### Icon Selection + +*(Which Lucide icons represent each action?)* + +- **[Action]**: [Icon name] + +Example: +- **Add meal**: Plus +- **Remove meal**: Trash +- **Generate grocery list**: ShoppingCart +- **Edit meal**: Pencil + +### Spacing System + +*(Consistent padding and margin values using Tailwind scale)* + +- **Gap between [elements]**: gap-[X] +- **Padding inside [containers]**: p-[X] + +Example: +- **Gap between days**: gap-4 +- **Gap between meal slots**: gap-2 +- **Padding inside cards**: p-4 +- **Padding for main container**: p-6 + +### Mobile Responsive Strategy + +*(How components adapt on smaller screens)* + +Example: +- Stack days vertically on mobile +- Grocery list becomes full-screen overlay +- Larger touch targets for meal slots + +--- + +## Success Metrics + +*(How will we measure if the application succeeds?)* + +**User Experience Metrics**: +- [ ] [Metric name and target] +- [ ] [Metric name and target] + +**Technical Metrics**: +- [ ] INP < 200ms +- [ ] LCP < 2.5s +- [ ] CLS < 0.1 + +**Feature Adoption**: +- [ ] [Specific feature usage target] + +--- + +## Out of Scope + +*(What will NOT be included in the initial version?)* + +- [ ] [Feature or capability] +- [ ] [Feature or capability] + +Example: +- Recipe database integration +- Social sharing features +- Advanced meal planning algorithms + +--- + +## Implementation Notes + +*(Technical considerations, dependencies, or constraints)* + +- **Stack**: See Spark App Template default-webapp.md / data-dashboard.md / etc. +- **Data Persistence**: [Where/how data is stored] +- **Third-party Services**: [Any external APIs or services] + +--- + +## Next Steps + +1. [ ] Review PRD with stakeholders +2. [ ] Create color palette and validate contrast +3. [ ] Set up typography in `index.css` +4. [ ] Scaffold project structure +5. [ ] Implement features in priority order +6. [ ] Test with real users +7. [ ] Iterate based on feedback + +--- + +## Example: Complete PRD + +See the spark-fullstack-template PRD.md for a complete example following this structure. + +--- + +**Tips**: +- Be specific, not vague +- Use concrete examples +- Validate color contrast ratios +- Think through edge cases early +- Keep it concise but complete +- Update PRD as requirements evolve +- Use this as a living document, not a one-time exercise + +**Remember**: A good PRD prevents wasted development time. Spend 1 hour planning to save 10 hours of rework. diff --git a/plugins/spark/skills/spark-app-template/references/radix-migration-guide.md b/plugins/spark/skills/spark-app-template/references/radix-migration-guide.md new file mode 100644 index 0000000..fd66199 --- /dev/null +++ b/plugins/spark/skills/spark-app-template/references/radix-migration-guide.md @@ -0,0 +1,279 @@ +# Radix UI Migration Guide - Spark App Template + +## Context + +Radix UI, the foundation of shadcn/ui, has received fewer updates since its acquisition by WorkOS. While Radix remains stable and battle-tested, the ecosystem is evolving with new alternatives. + +## Current Status (January 2026) + +**Radix UI**: +- ✅ Mature, stable, production-ready +- ✅ Powers shadcn/ui (45+ components) +- ✅ Used by millions of production apps +- ⚠️ Slower update cadence +- ⚠️ Uncertainty about long-term roadmap + +**Recommendation for New Projects**: Use Radix with confidence, but be aware of migration paths. + +## Migration Options + +### Option 1: Stay with Radix (Recommended for Most) + +**When to choose**: +- Building production apps today +- Need stability over cutting-edge features +- Want proven components with wide adoption +- Using shadcn/ui ecosystem + +**Action**: No changes needed. Continue using Radix-based shadcn components. + +**Monitoring**: Watch for shadcn/ui announcements about alternative foundations. + +--- + +### Option 2: Migrate to Base UI + +**What is Base UI?** +- Built by original Radix team (now at Vercel/MUI) +- Currently in beta, but "looking really good" per shadcn +- Designed for easy migration from Radix +- Similar API and component structure + +**Status**: Beta (as of January 2026) + +**Migration Path**: + +1. **Wait for shadcn/ui Support** + shadcn/ui now supports Base UI components. You can choose Radix or Base UI when installing: + + ```bash + pnpm dlx shadcn@latest init + # Choose Base UI when prompted + ``` + +2. **Gradual Component Migration** + shadcn components are framework-agnostic. When Base UI components are added to shadcn, you can: + + ```bash + # Install Base UI version of component + pnpm dlx shadcn@latest add button --base-ui + ``` + +3. **Auto-Detection** + The shadcn CLI auto-detects your library and applies the right transformations. + +**Trade-offs**: +- ✅ Future-proof choice +- ✅ Active development +- ✅ Backed by Vercel/MUI +- ⚠️ Still in beta +- ⚠️ Smaller ecosystem currently + +--- + +### Option 3: Migrate to React Aria + +**What is React Aria?** +- Built by Adobe +- Powers React Spectrum (Adobe's design system) +- Superior accessibility implementation +- Behavior-first hooks implementing WAI-ARIA spec +- Production-ready and actively maintained + +**When to choose**: +- Accessibility is top priority +- Need advanced customization +- Want long-term Adobe backing +- Building design systems + +**Migration Effort**: Moderate to High +- React Aria has different API patterns +- Requires rewriting component wrappers +- More control, but more work + +**Example Migration**: + +Before (Radix via shadcn): +```tsx +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; + + + Open + + + Title + + + +``` + +After (React Aria): +```tsx +import { DialogTrigger, Modal, Dialog, Heading } from 'react-aria-components'; + + + + + + Title + {/* Content */} + + + +``` + +**Resources**: +- [React Aria Documentation](https://react-spectrum.adobe.com/react-aria/) +- [React Aria Components](https://react-spectrum.adobe.com/react-aria/components.html) + +--- + +## Practical Migration Strategy + +### Phase 1: Assessment (Now) + +1. **Inventory your Radix usage** + ```bash + grep -r "@radix-ui" src/ + ``` + +2. **Identify critical components** + - Dialog/Modal + - Dropdown Menu + - Select + - Popover + - Tooltip + - etc. + +3. **Evaluate impact** + - How many components use Radix? + - Are they heavily customized? + - What's the migration cost vs. benefit? + +### Phase 2: Monitoring (Ongoing) + +**Watch for**: +- shadcn/ui announcements about Base UI support +- Radix UI update frequency +- Community migration patterns +- Base UI stable release + +**Action Items**: +- Subscribe to shadcn/ui GitHub releases +- Follow @shadcn on Twitter/X +- Monitor Radix UI GitHub activity + +### Phase 3: Migration (When Ready) + +**Trigger Migration When**: +- Base UI reaches stable v1.0 +- shadcn/ui fully supports Base UI +- Radix UI shows signs of abandonment +- Security vulnerabilities go unpatched + +**Migration Approach**: + +1. **Test Environment First** + - Set up new branch + - Migrate one component at a time + - Test thoroughly before production + +2. **Gradual Rollout** + ```bash + # Migrate component by component + pnpm dlx shadcn@latest add button --base-ui + pnpm dlx shadcn@latest add dialog --base-ui + ``` + +3. **Coexistence** + - Both Radix and Base UI can coexist temporarily + - Migrate high-traffic components first + - Monitor for issues + +### Phase 4: Complete Migration (Future) + +**Timeline**: Likely 2026-2027 + +**Steps**: +1. All new components use Base UI +2. Gradual migration of existing components +3. Remove Radix dependencies when complete +4. Update documentation + +--- + +## For Spark App Template Users + +### Current Recommendation + +**Default**: Use Radix-based shadcn/ui +- Production-ready today +- Extensive component library (45+) +- Proven track record +- Wide community support + +**Exception**: If building from scratch in Q3 2026+, consider Base UI if stable. + +### Staying Updated + +Spark App Template will be updated when: +- Base UI reaches stable v1.0 +- shadcn/ui completes Base UI integration +- Migration path is proven and low-risk + +**Check for updates**: +- Spark App Template GitHub releases +- shadcn/ui changelog +- This migration guide (will be updated) + +--- + +## FAQ + +**Q: Should I worry about Radix today?** +A: No. Radix is stable and production-ready. Use it with confidence. + +**Q: When should I migrate?** +A: When Base UI is stable (v1.0+) and shadcn/ui fully supports it, likely mid-to-late 2026. + +**Q: Can I use Radix for new projects now?** +A: Yes. The migration path will be straightforward when Base UI is ready. + +**Q: What about React Aria?** +A: Excellent choice for accessibility-first projects, but requires more custom work. + +**Q: Will shadcn/ui abandon Radix?** +A: No indication of abandonment. shadcn is adding Base UI as an option, not replacing Radix. + +**Q: How much work is the migration?** +A: Minimal if using shadcn/ui. Components can be swapped one at a time with CLI. + +--- + +## Resources + +**Radix UI**: +- Documentation: [radix-ui.com](https://www.radix-ui.com/) +- GitHub: [github.com/radix-ui/primitives](https://github.com/radix-ui/primitives) + +**Base UI**: +- Documentation: [mui.com/base-ui](https://mui.com/base-ui/) +- GitHub: [github.com/mui/base-ui](https://github.com/mui/base-ui) + +**React Aria**: +- Documentation: [react-spectrum.adobe.com/react-aria](https://react-spectrum.adobe.com/react-aria/) +- GitHub: [github.com/adobe/react-spectrum](https://github.com/adobe/react-spectrum) + +**shadcn/ui**: +- Documentation: [ui.shadcn.com](https://ui.shadcn.com/) +- GitHub: [github.com/shadcn-ui/ui](https://github.com/shadcn-ui/ui) + +--- + +**Last Updated**: January 2026 + +**Next Review**: Q2 2026 (when Base UI v1.0 expected) + +--- + +**Remember**: Don't let uncertainty paralyze you. Radix is production-ready today. When Base UI reaches stability, migration will be straightforward. Build now, migrate later if needed. diff --git a/plugins/spark/skills/spark-app-template/references/typography-pairings.md b/plugins/spark/skills/spark-app-template/references/typography-pairings.md new file mode 100644 index 0000000..11c7675 --- /dev/null +++ b/plugins/spark/skills/spark-app-template/references/typography-pairings.md @@ -0,0 +1,475 @@ +# Typography Pairings - Spark App Template + +## Typography as Identity + +Typography is voice made visible. Your font choices communicate personality, professionalism, and brand identity before users read a single word. + +## Fonts to Avoid (Overused) + +These fonts have become clichés in modern web design: + +❌ **Inter** - The default choice for everything. Overused to the point of invisibility. +❌ **Roboto** - Google's everywhere font. Safe but soulless. +❌ **Arial / Helvetica** - Corporate boredom incarnate. +❌ **Space Grotesk** - Was trendy in 2023-2024, now cliché. +❌ **Poppins** - Every Figma tutorial uses this. + +**Why avoid them?** They're not bad fonts, they're just everywhere. Your brand deserves distinction. + +## Font Personality Guide + +Choose fonts based on the character you want to project: + +### Serious & Professional +- IBM Plex Sans, Source Sans 3, Work Sans +- Clean, readable, trustworthy +- Use for: B2B SaaS, enterprise tools, financial services + +### Creative & Distinctive +- Bricolage Grotesque, Cabinet Grotesk, General Sans +- Unique character, memorable +- Use for: Agencies, design tools, creative platforms + +### Technical & Precise +- JetBrains Mono, IBM Plex Mono, Fira Code +- Monospace aesthetic, developer-friendly +- Use for: Developer tools, code editors, technical products + +### Editorial & Elegant +- Newsreader, Playfair Display, Spectral +- Timeless serif styling +- Use for: Publications, content platforms, luxury brands + +### Modern & Friendly +- DM Sans, Plus Jakarta Sans, Outfit +- Approachable, warm, contemporary +- Use for: Consumer apps, community platforms, social products + +### Bold & Expressive +- Darker Grotesque, Lexend, Syne +- Strong personality, attention-grabbing +- Use for: Marketing sites, landing pages, bold brands + +## Recommended Pairings + +### Pairing 1: Technical & Clean +**Headings**: IBM Plex Sans (600, 700) +**Body**: Source Sans 3 (400, 500, 600) + +```html + +``` + +```css +:root { + --font-heading: 'IBM Plex Sans', sans-serif; + --font-body: 'Source Sans 3', sans-serif; +} +``` + +**Character**: Professional, technical, trustworthy +**Best for**: SaaS platforms, B2B tools, developer products +**Why it works**: Both fonts share geometric structure but distinct enough for hierarchy + +--- + +### Pairing 2: Editorial & Sophisticated +**Headings**: Newsreader (600, 700) +**Body**: Source Sans 3 (400, 500, 600) + +```html + +``` + +```css +:root { + --font-heading: 'Newsreader', serif; + --font-body: 'Source Sans 3', sans-serif; +} +``` + +**Character**: Editorial, classic, authoritative +**Best for**: Publications, content platforms, journalism, blogs +**Why it works**: Serif + sans-serif contrast creates clear hierarchy + +--- + +### Pairing 3: Modern & Distinctive +**Headings**: Bricolage Grotesque (600, 700, 800) +**Body**: DM Sans (400, 500, 600) + +```html + +``` + +```css +:root { + --font-heading: 'Bricolage Grotesque', sans-serif; + --font-body: 'DM Sans', sans-serif; +} +``` + +**Character**: Modern, distinctive, creative +**Best for**: Design agencies, creative tools, innovative products +**Why it works**: Bricolage's unique character balanced by DM Sans' neutrality + +--- + +### Pairing 4: Developer-Focused +**Headings**: IBM Plex Sans (600, 700) +**Body**: IBM Plex Sans (400, 500) +**Code**: IBM Plex Mono (400, 500) + +```html + +``` + +```css +:root { + --font-heading: 'IBM Plex Sans', sans-serif; + --font-body: 'IBM Plex Sans', sans-serif; + --font-mono: 'IBM Plex Mono', monospace; +} + +code, pre { + font-family: var(--font-mono); +} +``` + +**Character**: Technical, precise, developer-friendly +**Best for**: Code editors, dev tools, technical documentation +**Why it works**: Unified family creates cohesion; mono variant for code + +--- + +### Pairing 5: Warm & Approachable +**Headings**: Outfit (600, 700, 800) +**Body**: Plus Jakarta Sans (400, 500, 600) + +```html + +``` + +```css +:root { + --font-heading: 'Outfit', sans-serif; + --font-body: 'Plus Jakarta Sans', sans-serif; +} +``` + +**Character**: Friendly, modern, accessible +**Best for**: Consumer apps, community platforms, social products +**Why it works**: Both fonts have rounded, friendly forms + +--- + +### Pairing 6: Bold & Impactful +**Headings**: Syne (700, 800) +**Body**: Work Sans (400, 500, 600) + +```html + +``` + +```css +:root { + --font-heading: 'Syne', sans-serif; + --font-body: 'Work Sans', sans-serif; +} +``` + +**Character**: Bold, attention-grabbing, confident +**Best for**: Marketing sites, landing pages, bold brands +**Why it works**: Syne's distinctive style balanced by Work Sans' neutrality + +--- + +### Pairing 7: Minimalist & Refined +**Headings**: DM Sans (500, 600, 700) +**Body**: DM Sans (400, 500) + +```html + +``` + +```css +:root { + --font-heading: 'DM Sans', sans-serif; + --font-body: 'DM Sans', sans-serif; +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-heading); + letter-spacing: -0.02em; /* Tighter tracking for headings */ +} +``` + +**Character**: Clean, minimalist, modern +**Best for**: Minimalist designs, tools, utilities +**Why it works**: Single-font system with weight differentiation + +--- + +## Typography Scale + +Use these scales with your chosen pairing: + +### Standard Scale (Default) +```css +:root { + --text-xs: 0.75rem; /* 12px */ + --text-sm: 0.875rem; /* 14px */ + --text-base: 1rem; /* 16px */ + --text-lg: 1.125rem; /* 18px */ + --text-xl: 1.25rem; /* 20px */ + --text-2xl: 1.5rem; /* 24px */ + --text-3xl: 1.875rem; /* 30px */ + --text-4xl: 2.25rem; /* 36px */ + --text-5xl: 3rem; /* 48px */ + --text-6xl: 3.75rem; /* 60px */ +} +``` + +### Large Scale (Content-Focused) +```css +:root { + --text-base: 1.125rem; /* 18px body text */ + --text-lg: 1.25rem; /* 20px lead text */ + --text-xl: 1.5rem; /* 24px */ + --text-2xl: 2rem; /* 32px */ + --text-3xl: 2.5rem; /* 40px */ + --text-4xl: 3rem; /* 48px */ + --text-5xl: 3.5rem; /* 56px */ + --text-6xl: 4rem; /* 64px */ +} +``` + +### Compact Scale (Data-Dense) +```css +:root { + --text-xs: 0.6875rem; /* 11px */ + --text-sm: 0.8125rem; /* 13px */ + --text-base: 0.9375rem; /* 15px */ + --text-lg: 1.0625rem; /* 17px */ + --text-xl: 1.25rem; /* 20px */ + --text-2xl: 1.5rem; /* 24px */ + --text-3xl: 2rem; /* 32px */ + --text-4xl: 2.5rem; /* 40px */ +} +``` + +## Line Height Guidelines + +```css +/* Tight - for headings */ +.line-height-tight { + line-height: 1.2; +} + +/* Normal - for UI text */ +.line-height-normal { + line-height: 1.5; +} + +/* Relaxed - for body content */ +.line-height-relaxed { + line-height: 1.7; +} + +/* Loose - for large body text */ +.line-height-loose { + line-height: 1.8; +} +``` + +**Rule of thumb**: +- Larger text → tighter line-height (headings: 1.1-1.3) +- Smaller text → looser line-height (body: 1.5-1.7) +- Narrow columns → looser line-height +- Wide columns → tighter line-height + +## Letter Spacing (Tracking) + +```css +/* Tight - for headings, large text */ +.tracking-tight { + letter-spacing: -0.02em; +} + +/* Normal - default */ +.tracking-normal { + letter-spacing: 0; +} + +/* Wide - for small caps, labels */ +.tracking-wide { + letter-spacing: 0.05em; +} + +/* Wider - for uppercase labels */ +.tracking-wider { + letter-spacing: 0.1em; +} +``` + +**When to use**: +- Large headings (48px+): `-0.02em` to `-0.04em` +- Body text: `0` (default) +- Small uppercase labels: `0.05em` to `0.1em` +- Never positive tracking on lowercase text + +## Font Loading Best Practices + +### Use Variable Fonts When Available + +Variable fonts = one file, multiple weights. + +```html + + + + + + + +``` + +**Performance win**: Fewer HTTP requests, smaller total size, smooth weight interpolation. + +### Font Display Strategy + +```css +@font-face { + font-family: 'Custom Font'; + src: url('/fonts/font.woff2') format('woff2'); + font-display: swap; /* Show fallback immediately, swap when loaded */ + font-weight: 400 700; /* Variable font weight range */ +} +``` + +**Options**: +- `swap` (recommended): Show fallback, swap when loaded +- `optional`: Use custom font only if cached +- `fallback`: Brief invisible period, then show fallback + +### Preload Critical Fonts + +```html + + + + +``` + +**Only preload**: +- Heading font (if custom-hosted) +- Above-the-fold text +- Max 1-2 fonts + +## Fallback Font Stacks + +Define system fallbacks that match your custom font's metrics: + +```css +:root { + /* Sans-serif stacks */ + --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + + /* Serif stacks */ + --font-serif: 'Newsreader', 'Iowan Old Style', 'Apple Garamond', 'Palatino Linotype', 'Times New Roman', serif; + + /* Monospace stacks */ + --font-mono: 'IBM Plex Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Courier New', monospace; +} +``` + +## Responsive Typography + +### Fluid Type Scale + +```css +h1 { + font-size: clamp(2.5rem, 5vw + 1rem, 4rem); + /* Min: 40px, Scales with viewport, Max: 64px */ +} + +h2 { + font-size: clamp(2rem, 4vw + 0.5rem, 3rem); +} + +body { + font-size: clamp(1rem, 2vw, 1.125rem); +} +``` + +### Breakpoint-Based (Tailwind) + +```tsx +

+ Responsive Heading +

+``` + +## Accessibility Considerations + +**Minimum Sizes**: +- Body text: 16px minimum (1rem) +- Small text (captions): 14px minimum (0.875rem) +- Never go below 12px for any text + +**Contrast**: +- Body text: 4.5:1 contrast ratio (WCAG AA) +- Large text (18px+ or 14px bold): 3:1 contrast ratio + +**Line Length**: +- Optimal: 60-75 characters per line +- Max: 90 characters per line +- Use `max-width: 65ch` for readable content blocks + +## Testing Your Typography + +**Checklist**: +- [ ] Readable at all sizes (mobile to desktop) +- [ ] Clear hierarchy (can distinguish h1 from h2 from h3) +- [ ] Sufficient contrast (pass WCAG AA) +- [ ] Line length appropriate (not too wide) +- [ ] Loads quickly (< 100ms font load) +- [ ] Fallback fonts match proportions +- [ ] Accessible (zoom to 200% still readable) + +**Tools**: +- Google Fonts: fonts.google.com +- Type Scale Calculator: type-scale.com +- Font Pairing: fontpair.co +- Contrast Checker: whocanuse.com + +--- + +**Remember**: Typography is not just decoration—it's the primary interface between your content and your users. Choose wisely, test thoroughly, and iterate based on feedback. diff --git a/plugins/spark/skills/spark-app-template/stacks/complex-application.md b/plugins/spark/skills/spark-app-template/stacks/complex-application.md new file mode 100644 index 0000000..1e024bc --- /dev/null +++ b/plugins/spark/skills/spark-app-template/stacks/complex-application.md @@ -0,0 +1,896 @@ +# Complex Application Stack + +## Overview + +The complex application stack is designed for sophisticated multi-view applications requiring advanced state management, complex navigation, and robust architecture. Use for SaaS platforms, enterprise tools, and feature-rich applications. + +## When to Use + +- SaaS platforms +- Enterprise applications +- Multi-user systems +- Complex workflows with many views +- Applications requiring centralized state +- Tools with advanced features and interactions + +## Extends Default Stack + +This stack includes ALL packages from `default-webapp.md` PLUS the following additive packages. + +## Additional Packages + +### State Management +```bash +pnpm add zustand # Lightweight, flexible state management +``` + +### Date & Time Handling +```bash +pnpm add date-fns +``` + +### Additional Utilities +```bash +pnpm add uuid # Generate unique IDs +pnpm add react-resizable-panels # Resizable layouts (often in shadcn) +``` + +### Additional shadcn Components +```bash +# Navigation & Layout +pnpm dlx shadcn@latest add navigation-menu +pnpm dlx shadcn@latest add breadcrumb +pnpm dlx shadcn@latest add sidebar +pnpm dlx shadcn@latest add resizable +pnpm dlx shadcn@latest add sheet + +# Data & Forms +pnpm dlx shadcn@latest add table +pnpm dlx shadcn@latest add select +pnpm dlx shadcn@latest add combobox +pnpm dlx shadcn@latest add command +pnpm dlx shadcn@latest add calendar +pnpm dlx shadcn@latest add popover +pnpm dlx shadcn@latest add dropdown-menu + +# Feedback +pnpm dlx shadcn@latest add alert +pnpm dlx shadcn@latest add alert-dialog +pnpm dlx shadcn@latest add toast +pnpm dlx shadcn@latest add progress +pnpm dlx shadcn@latest add skeleton +``` + +## Architecture Patterns + +### State Management with Zustand + +Create stores for different domains: + +```tsx +// src/stores/useAuthStore.ts +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; + +interface User { + id: string; + name: string; + email: string; +} + +interface AuthState { + user: User | null; + token: string | null; + login: (user: User, token: string) => void; + logout: () => void; +} + +export const useAuthStore = create()( + persist( + (set) => ({ + user: null, + token: null, + login: (user, token) => set({ user, token }), + logout: () => set({ user: null, token: null }), + }), + { + name: 'auth-storage', + } + ) +); +``` + +```tsx +// src/stores/useAppStore.ts +import { create } from 'zustand'; + +interface AppState { + sidebarOpen: boolean; + currentView: string; + toggleSidebar: () => void; + setView: (view: string) => void; +} + +export const useAppStore = create((set) => ({ + sidebarOpen: true, + currentView: 'dashboard', + toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })), + setView: (view) => set({ currentView: view }), +})); +``` + +### Application Layout + +```tsx +// src/components/AppLayout.tsx +import { Outlet } from '@tanstack/react-router'; +import { Sidebar } from './Sidebar'; +import { Header } from './Header'; +import { useAppStore } from '@/stores/useAppStore'; + +export function AppLayout() { + const sidebarOpen = useAppStore((state) => state.sidebarOpen); + + return ( +
+ {/* Sidebar */} + {sidebarOpen && ( + + )} + + {/* Main content */} +
+
+
+ +
+
+
+ ); +} +``` + +## Layout Scrolling Patterns + +Complex applications often need independent scrolling areas (sidebar, main content, modals). Getting scrolling right requires understanding flexbox constraints and when to use ScrollArea vs native overflow. + +### Core Principle: Flexbox Container Pattern + +For scrolling to work in constrained spaces, follow this pattern: + +```tsx +
{/* Define height constraint */} +
Fixed header
+
Scrollable content
+
Fixed footer
+
+``` + +**Key rules:** +- Parent must have explicit height (`h-screen`, `h-full`, or `flex-1` from its parent) +- Fixed elements use `flex-shrink-0` +- Scrollable element uses `flex-1` to fill remaining space +- Scrollable element needs `overflow-auto` or be a `ScrollArea` component + +### Split-Pane Layout (Sidebar + Content) + +Most complex apps use this pattern: + +```tsx +// Full-height split pane with independent scrolling +export function AppShell() { + return ( +
+ {/* Left Sidebar */} +
+
+

Sidebar Header

+
+
+ +
+
+ + {/* Right Content */} +
+
+

Page Header

+
+
+
+ {/* Main content that scrolls */} +
+
+
+
+ ); +} +``` + +**Why this works:** +- Root uses `h-screen overflow-hidden` to constrain total height +- Both panes use `flex flex-col` to stack header + scrollable area +- Headers use `flex-shrink-0` to prevent compression +- Scrollable areas use `flex-1 overflow-auto` to fill remaining space + +### When to Use ScrollArea vs Native Overflow + +**Use native `overflow-auto` (recommended):** +- Full-height scrolling areas (sidebars, main content) +- Simpler implementation, more reliable +- Better browser support and performance +- Mobile-friendly (respects system scroll behavior) +- Works consistently with flexbox layouts + +**Use shadcn `ScrollArea` (advanced):** +- Small contained regions with custom scrollbar styling +- Modals/dialogs with fixed max-height +- When you need precise scrollbar customization +- Note: Can have edge cases with flex-1, prefer native overflow for full-height + +```tsx +{/* Full-height areas - use native overflow (recommended) */} +
+
Sidebar or main content
+
+ +{/* Small contained areas - ScrollArea acceptable */} + +
Fixed height content
+
+``` + +### Common Mistakes to Avoid + +❌ **Don't use calc() for scroll heights:** +```tsx +// BAD: Brittle, breaks on content changes + +``` + +✅ **Do use flexbox:** +```tsx +// GOOD: Flexible, adapts to any layout +
+``` + +❌ **Don't forget parent constraints:** +```tsx +// BAD: flex-1 without parent height does nothing +
+``` + +✅ **Do establish height on parent:** +```tsx +// GOOD: Parent has h-screen +
+
+``` + +❌ **Don't skip overflow-hidden on parent:** +```tsx +// BAD: Can cause layout issues +
+
+``` + +✅ **Do add overflow-hidden to parent:** +```tsx +// GOOD: Prevents flex-1 from growing beyond bounds +
+
+``` + +### Modal with Scrollable Content + +```tsx + + + Modal Title + + +
+ {/* Long scrollable content */} +
+
+ + + +
+``` + +### Debugging Scrolling Issues + +If scrolling doesn't work: + +1. **Check parent height:** Use browser devtools to verify the scrollable element's parent has a constrained height +2. **Verify flex setup:** Ensure `flex flex-col` on parent, `flex-1` on scrollable child +3. **Confirm overflow:** Check if `overflow-auto` or ScrollArea is applied +4. **Look for overflow-hidden:** Parent of scrollable might need `overflow-hidden` to constrain growth + +**Quick diagnostic:** +```tsx +// Add temporary red border to see element bounds +
+``` + +If the red border doesn't have the height you expect, the problem is in the parent flex setup, not the scrolling itself. + +### Protected Routes + +```tsx +// src/routes/_authenticated.tsx +import { createFileRoute, redirect } from '@tanstack/react-router'; +import { useAuthStore } from '@/stores/useAuthStore'; + +export const Route = createFileRoute('/_authenticated')({ + beforeLoad: async () => { + const token = useAuthStore.getState().token; + if (!token) { + throw redirect({ to: '/login' }); + } + }, + component: AppLayout, +}); +``` + +```tsx +// src/routes/_authenticated/dashboard.tsx +export const Route = createFileRoute('/_authenticated/dashboard')({ + component: Dashboard, +}); +``` + +## Navigation Patterns + +### Sidebar Navigation + +```tsx +import { Link } from '@tanstack/react-router'; +import { Home, Users, Settings, FileText } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +const navigation = [ + { name: 'Dashboard', href: '/dashboard', icon: Home }, + { name: 'Users', href: '/users', icon: Users }, + { name: 'Documents', href: '/documents', icon: FileText }, + { name: 'Settings', href: '/settings', icon: Settings }, +]; + +export function Sidebar() { + return ( + + ); +} +``` + +### Hierarchical Sidebar Items + +When displaying hierarchical data (org charts, nested navigation, file trees) in sidebars, use padding-based indentation to prevent text clipping. + +**✅ Correct Pattern:** +```tsx +interface HierarchyItemProps { + person: { name: string; title: string; initials: string }; + level: number; // 0, 1, 2 for depth + isSelected: boolean; +} + +function HierarchyItem({ person, level, isSelected }: HierarchyItemProps) { + const paddingLeft = level === 0 ? 'pl-3' : level === 1 ? 'pl-5' : 'pl-7'; + + return ( + + ); +} +``` + +**Key Rules:** + +❌ **Don't use margin for indentation:** +```tsx + + Page {page} + +
+
+ ); +} +``` + +## Complex Forms + +### Multi-Step Form + +```tsx +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { Button } from '@/components/ui/button'; +import { Progress } from '@/components/ui/progress'; + +const step1Schema = z.object({ + name: z.string().min(2), + email: z.string().email(), +}); + +const step2Schema = z.object({ + company: z.string().min(2), + role: z.string(), +}); + +export function MultiStepForm() { + const [step, setStep] = useState(1); + const [formData, setFormData] = useState({}); + + const schema = step === 1 ? step1Schema : step2Schema; + const form = useForm({ + resolver: zodResolver(schema), + }); + + const onSubmit = (data: any) => { + if (step === 1) { + setFormData({ ...formData, ...data }); + setStep(2); + } else { + // Final submission + const finalData = { ...formData, ...data }; + console.log(finalData); + } + }; + + const progress = (step / 2) * 100; + + return ( +
+ + +
+ {step === 1 && ( + <> + {/* Step 1 fields */} + + + + )} + + {step === 2 && ( + <> + {/* Step 2 fields */} + + + + )} + +
+ {step > 1 && ( + + )} + +
+
+
+ ); +} +``` + +## Performance Optimization + +### Code Splitting by Route + +TanStack Router handles this automatically. Each route is lazy-loaded. + +### Virtual Scrolling for Large Lists + +```tsx +import { useVirtualizer } from '@tanstack/react-virtual'; +import { useRef } from 'react'; + +export function VirtualList({ items }: { items: any[] }) { + const parentRef = useRef(null); + + const virtualizer = useVirtualizer({ + count: items.length, + getScrollElement: () => parentRef.current, + estimateSize: () => 50, + }); + + return ( +
+
+ {virtualizer.getVirtualItems().map((virtualItem) => ( +
+ {items[virtualItem.index].name} +
+ ))} +
+
+ ); +} +``` + +### React Compiler + +Enable for automatic memoization: + +```bash +pnpm add -D babel-plugin-react-compiler +``` + +Configure in Vite: +```typescript +// vite.config.ts +export default defineConfig({ + plugins: [ + react({ + babel: { + plugins: [ + ['babel-plugin-react-compiler', {}], + ], + }, + }), + ], +}); +``` + +## Testing Strategy + +### Component Testing + +```tsx +import { render, screen } from '@testing-library/react'; +import { Button } from './button'; + +test('renders button with text', () => { + render(); + expect(screen.getByText('Click me')).toBeInTheDocument(); +}); +``` + +### E2E Testing (Playwright) + +```bash +pnpm add -D @playwright/test +``` + +```typescript +// tests/login.spec.ts +import { test, expect } from '@playwright/test'; + +test('user can log in', async ({ page }) => { + await page.goto('/login'); + await page.fill('[name="email"]', 'user@example.com'); + await page.fill('[name="password"]', 'password'); + await page.click('button[type="submit"]'); + await expect(page).toHaveURL('/dashboard'); +}); +``` + +## Security Considerations + +- Validate all inputs with Zod +- Sanitize user-generated content +- Use HTTPS in production +- Implement CSRF protection +- Set secure cookie flags +- Use environment variables for secrets +- Implement rate limiting +- Regular dependency updates + +## Deployment + +### Environment Variables + +```bash +# .env.production +VITE_API_URL=https://api.production.com +VITE_APP_URL=https://app.production.com +``` + +Access in code: +```typescript +const API_URL = import.meta.env.VITE_API_URL; +``` + +### Build Optimization + +```bash +# Production build +pnpm build + +# Analyze bundle size +pnpm add -D rollup-plugin-visualizer +``` + +## Next Steps + +1. Define application architecture and feature domains +2. Set up Zustand stores for global state +3. Create protected route structure +4. Implement authentication flow +5. Build core navigation (sidebar, command palette) +6. Develop feature modules +7. Add comprehensive error handling +8. Optimize performance (code splitting, caching) +9. Implement testing strategy +10. Deploy to production diff --git a/plugins/spark/skills/spark-app-template/stacks/content-showcase.md b/plugins/spark/skills/spark-app-template/stacks/content-showcase.md new file mode 100644 index 0000000..f239d2a --- /dev/null +++ b/plugins/spark/skills/spark-app-template/stacks/content-showcase.md @@ -0,0 +1,452 @@ +# Content Showcase Stack + +## Overview + +The content showcase stack is optimized for content-focused applications including marketing sites, portfolios, blogs, documentation sites, and landing pages where typography and reading experience are paramount. + +## When to Use + +- Marketing websites and landing pages +- Portfolio sites +- Blog platforms +- Documentation sites +- Content-heavy applications +- Informational websites + +## Extends Default Stack + +This stack includes ALL packages from `default-webapp.md` PLUS the following additive packages. + +## Additional Packages + +### Content Processing +```bash +pnpm add marked # Markdown to HTML +pnpm add dompurify # Sanitize HTML (security) +``` + +### Additional shadcn Components +```bash +# Content components +pnpm dlx shadcn@latest add separator +pnpm dlx shadcn@latest add breadcrumb +pnpm dlx shadcn@latest add navigation-menu +pnpm dlx shadcn@latest add accordion +``` + +## Design Guidance + +### Typography-First Design + +Content showcases live or die by their typography. The reading experience must be exceptional. + +**Type Scale** (larger than default): +```css +/* Add to index.css */ +h1: 3.5rem (56px), line-height: 1.1, font-weight: 700 +h2: 2.5rem (40px), line-height: 1.2, font-weight: 600 +h3: 2rem (32px), line-height: 1.3, font-weight: 600 +h4: 1.5rem (24px), line-height: 1.4, font-weight: 600 +body: 1.125rem (18px), line-height: 1.7, font-weight: 400 +lead: 1.25rem (20px), line-height: 1.6, font-weight: 400 +``` + +**Line Length**: Optimal reading is 60-75 characters per line +```css +.prose { + max-width: 65ch; +} +``` + +**Vertical Rhythm**: Consistent spacing between elements +```css +/* Spacing scale */ +h1: mb-6 +h2: mt-12 mb-4 +h3: mt-8 mb-3 +p: mb-4 +``` + +### Distinctive Font Pairings + +**Recommended for Content**: + +**Option 1: Editorial** +- Headings: **Playfair Display** (serif, elegant) +- Body: **Source Sans 3** (sans-serif, readable) +- Use: Magazine-style, luxury brands, editorial content + +**Option 2: Modern Technical** +- Headings: **Space Grotesk** (geometric, modern) +- Body: **IBM Plex Sans** (clean, professional) +- Use: Tech companies, startups, modern brands + +**Option 3: Classic** +- Headings: **Newsreader** (serif, traditional) +- Body: **Source Sans 3** (sans-serif) +- Use: Publications, journalism, classic brands + +Update `index.html`: +```html + + +``` + +Update `index.css`: +```css +body { + font-family: 'Source Sans 3', sans-serif; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Playfair Display', serif; +} +``` + +### Color for Content + +**Muted, Sophisticated Palette**: +```css +:root { + /* Warm, readable background */ + --background: oklch(0.98 0.005 85); + --foreground: oklch(0.22 0.01 75); + + /* Subtle, elegant primary */ + --primary: oklch(0.40 0.08 240); + --primary-foreground: oklch(0.99 0 0); + + /* Warm accent for links */ + --accent: oklch(0.55 0.12 35); + --accent-foreground: oklch(0.22 0.01 75); + + /* Larger radius for softer feel */ + --radius: 0.75rem; +} +``` + +### Whitespace & Breathing Room + +Content needs generous spacing: +- **Section padding**: py-16 to py-24 (mobile), py-24 to py-32 (desktop) +- **Content width**: max-w-4xl or max-w-5xl centered +- **Grid gaps**: gap-8 to gap-12 +- **Margins**: Generous margins around content blocks + +## Layout Patterns + +### Hero Section + +```tsx +export function Hero() { + return ( +
+
+
+

+ Build Beautiful Web Experiences +

+

+ A comprehensive toolkit for creating production-ready applications + with exceptional design and performance. +

+
+ + +
+
+
+
+ ); +} +``` + +### Content Grid (Features, Services, etc.) + +```tsx +import { Card, CardContent } from '@/components/ui/card'; + +const features = [ + { + title: 'Fast Performance', + description: 'Built with modern tools for lightning-fast load times.', + icon: Zap + }, + // ... +]; + +export function Features() { + return ( +
+
+
+

Why Choose Us

+

+ Everything you need to build exceptional web applications +

+
+ +
+ {features.map((feature) => ( + + + +

{feature.title}

+

+ {feature.description} +

+
+
+ ))} +
+
+
+ ); +} +``` + +### Long-Form Content (Blog Post, Article) + +```tsx +import { Separator } from '@/components/ui/separator'; + +export function Article({ content }: { content: string }) { + return ( +
+
+ {/* Article header */} +
+

Article Title

+
+ + + 5 min read +
+
+ + {/* Article content */} +
+
+
+ ); +} +``` + +## Markdown Processing + +### Convert Markdown to HTML + +```tsx +import { marked } from 'marked'; +import DOMPurify from 'dompurify'; + +// Configure marked for better output +marked.setOptions({ + gfm: true, // GitHub Flavored Markdown + breaks: true, // Convert \n to
+}); + +export function renderMarkdown(markdown: string): string { + const html = marked(markdown); + return DOMPurify.sanitize(html); +} +``` + +### Markdown Component + +```tsx +interface MarkdownProps { + content: string; +} + +export function Markdown({ content }: MarkdownProps) { + const html = renderMarkdown(content); + + return ( +
+ ); +} +``` + +## Navigation Patterns + +### Header Navigation + +```tsx +import { NavigationMenu, NavigationMenuItem, NavigationMenuLink, NavigationMenuList } from '@/components/ui/navigation-menu'; +import { Button } from '@/components/ui/button'; + +export function Header() { + return ( +
+
+
+ Brand + + + + + Features + + + Pricing + + + Blog + + + +
+ +
+ + +
+
+
+ ); +} +``` + +### Footer + +```tsx +export function Footer() { + return ( +
+
+
+
+

Product

+ +
+ {/* More columns */} +
+ +
+

© 2026 Your Company. All rights reserved.

+
+
+
+ ); +} +``` + +## SEO Optimization + +### Meta Tags (in route components) + +```tsx +// src/routes/index.tsx +import { createFileRoute } from '@tanstack/react-router'; +import { Helmet } from 'react-helmet-async'; // Install if needed + +export const Route = createFileRoute('/')({ + component: HomePage, +}); + +function HomePage() { + return ( + <> + + Your App - Tagline + + + + + + + + {/* Page content */} + + ); +} +``` + +## Performance for Content Sites + +### Image Optimization + +```tsx +// Use WebP with fallbacks + + + + Description + +``` + +### Font Loading Strategy + +Already using `display=swap` for optimal font loading. Consider: +```html + + +``` + +### Code Splitting + +Content pages should load instantly: +```tsx +// Lazy load heavy components +const HeavyComponent = lazy(() => import('./HeavyComponent')); + +}> + + +``` + +## Accessibility + +- Use semantic HTML (`
`, `
`, `