Comprehensive guidance for building web apps with opinionated defaults for tech stack, design system, and code standards...
Spark App Template provides defaults and guidance for building web applications. When a user asks to build a web app, this skill provides the technical foundation and design direction to create functional applications.
Activate Spark App Template when the user:
CRITICAL: Follow this exact order to avoid configuration errors:
pnpm create vite@latest my-app --template react-tspnpm installpnpm add -D tailwindcss @tailwindcss/vite @biomejs/biomepnpm dlx shadcn@latest init (will now succeed with proper aliases)pnpm dlx shadcn add button card avatar ... (BEFORE writing components)@/components/ui/*)Steps 3, 6, and 7 must happen in this exact order to avoid TypeScript errors and failed shadcn installations.
Understanding complexity helps choose the right stack variation and design approach:
Micro Tool (single-purpose)
stacks/default-webapp.mdContent Showcase (information-focused)
stacks/content-showcase.mdLight Application (multiple features with basic state)
stacks/default-webapp.mdComplex Application (advanced functionality, multiple views)
stacks/complex-application.md or stacks/data-dashboard.mdAll Spark App Template applications use this foundation:
All variations share the core foundation above. These templates add specific packages and design guidance:
stacks/default-webapp.md)stacks/data-dashboard.md)stacks/content-showcase.md)stacks/complex-application.md)Enable these modern React capabilities:
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
Beautiful web applications transcend mere functionality - they evoke emotion and form memorable experiences. Follow these principles:
references/typography-pairings.mdSee references/design-system.md for comprehensive design guidance.
Optimize for these metrics:
Tools to achieve targets:
See references/performance-checklist.md for detailed optimization strategies.
Access detailed guidance in the references/ directory:
CRITICAL: Configure path aliases BEFORE running shadcn init to avoid validation errors.
# 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*:
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
Update tsconfig.app.json*:
{
"compilerOptions": {
// ... existing options ...
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
Update vite.config.ts*:
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:
# 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
See stack templates in stacks/ for specific configuration examples.
Use references/prd-template.md to plan:
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):
# 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:
# 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:
shadcn add command with all componentssrc/components/ui/:root CSS variables@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.
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<typeof schema>) {
// Handle form submission
}
return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;
}
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 <LoadingSpinner />;
return <UserGrid users={data} onCreate={createUser.mutate} />;
}
Radix UI is receiving fewer updates. For new projects or migration concerns:
references/radix-migration-guide.md for Base UI migration pathIf app feels slow:
references/performance-checklist.mdNewer Vite versions with Rolldown bundler may offer faster builds when stable. Monitor for stable releases.
After scaffolding:
Remember: The goal is functional and performant web applications. Start simple, iterate based on user needs, and prioritize user experience over technical complexity.