web-artifacts-builder
π―Skillfrom bejranonda/llm-autonomous-agent-plugin-for-claude
Builds production-ready web applications using React, Tailwind CSS, and shadcn/ui with a focus on type safety, accessibility, and performance.
Part of
bejranonda/llm-autonomous-agent-plugin-for-claude(25 items)
Installation
npm install framer-motionSkill Details
Modern web development patterns using React + Tailwind CSS + shadcn/ui for building production-quality, accessible, and performant web applications
Overview
This skill provides comprehensive patterns and best practices for building modern web applications using the React + Tailwind CSS + shadcn/ui stack. It emphasizes component-driven development, type safety with TypeScript, accessibility, and performance optimization.
Stack Overview
Core Technologies:
- React 18+: Component-based UI with hooks and concurrent features
- TypeScript: Type-safe development with excellent IDE support
- Tailwind CSS: Utility-first CSS framework for rapid styling
- shadcn/ui: High-quality, accessible React components built on Radix UI
- Vite: Fast build tool with HMR and optimized production builds
Why This Stack:
- Type safety reduces bugs and improves maintainability
- Tailwind enables rapid UI development without context switching
- shadcn/ui provides accessible, customizable components out of the box
- Vite offers excellent developer experience and fast builds
- Modern ecosystem with active community support
Design Considerations:
Based on research from ["Improving frontend design through Skills"](https://claude.com/blog/improving-frontend-design-through-skills):
- Avoid Distributional Defaults: Don't use Inter/Roboto/Open Sans, purple gradients, or plain backgrounds
- Distinctive Typography: Use high-contrast font pairings with extreme weight variations (100-200 or 800-900)
- Intentional Colors: Move beyond generic color schemes with thoughtful palettes
- High-Impact Motion: One well-orchestrated page load beats a dozen random animations
- Motion Library: Use Framer Motion for complex animation choreography in React
- See
frontend-aestheticsskill for comprehensive design guidance
Project Structure
Recommended Directory Layout
```
project-root/
βββ src/
β βββ components/
β β βββ ui/ # shadcn/ui components
β β β βββ button.tsx
β β β βββ card.tsx
β β β βββ dialog.tsx
β β β βββ ...
β β βββ features/ # Feature-specific components
β β β βββ auth/
β β β βββ dashboard/
β β β βββ ...
β β βββ layout/ # Layout components
β β βββ Header.tsx
β β βββ Sidebar.tsx
β β βββ Footer.tsx
β βββ lib/
β β βββ utils.ts # Utility functions (cn, etc.)
β β βββ api.ts # API client
β β βββ hooks/ # Custom React hooks
β βββ pages/ # Page components (if using routing)
β βββ styles/
β β βββ globals.css # Global styles and Tailwind imports
β βββ types/ # TypeScript type definitions
β βββ App.tsx # Main app component
β βββ main.tsx # Entry point
βββ public/ # Static assets
βββ index.html
βββ package.json
βββ tsconfig.json
βββ tailwind.config.js
βββ vite.config.ts
βββ components.json # shadcn/ui configuration
```
Component Patterns
1. Button Component (shadcn/ui style)
```typescript
// src/components/ui/button.tsx
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes
VariantProps
asChild?: boolean
}
const Button = React.forwardRef
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }
```
Usage:
```typescript
import { Button } from "@/components/ui/button"
// Different variants
```
2. Card Component Pattern
```typescript
// src/components/ui/card.tsx
import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef
({ className, ...props }, ref) => (
ref={ref} className={cn( "rounded-xl border bg-card text-card-foreground shadow", className )} {...props} /> ) ) Card.displayName = "Card" const CardHeader = React.forwardRef ({ className, ...props }, ref) => ( ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} /> ) ) CardHeader.displayName = "CardHeader" const CardTitle = React.forwardRef ({ className, ...props }, ref) => ( ref={ref} className={cn("font-semibold leading-none tracking-tight", className)} {...props} /> ) ) CardTitle.displayName = "CardTitle" const CardDescription = React.forwardRef ({ className, ...props }, ref) => ( ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} /> ) ) CardDescription.displayName = "CardDescription" const CardContent = React.forwardRef ({ className, ...props }, ref) => ( ) ) CardContent.displayName = "CardContent" const CardFooter = React.forwardRef ({ className, ...props }, ref) => ( ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} /> ) ) CardFooter.displayName = "CardFooter" export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } ``` Usage: ```typescript import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card" import { Button } from "@/components/ui/button" Card content ``` ```typescript // src/components/ui/dialog.tsx (simplified shadcn/ui pattern) import * as React from "react" import * as DialogPrimitive from "@radix-ui/react-dialog" import { X } from "lucide-react" import { cn } from "@/lib/utils" const Dialog = DialogPrimitive.Root const DialogTrigger = DialogPrimitive.Trigger const DialogPortal = DialogPrimitive.Portal const DialogClose = DialogPrimitive.Close const DialogOverlay = React.forwardRef< React.ElementRef React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( ref={ref} className={cn( "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className )} {...props} /> )) DialogOverlay.displayName = DialogPrimitive.Overlay.displayName const DialogContent = React.forwardRef< React.ElementRef React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( ref={ref} className={cn( "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", className )} {...props} > {children} Close )) DialogContent.displayName = DialogPrimitive.Content.displayName export { Dialog, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogContent } ``` ```javascript /* @type {import('tailwindcss').Config} / export default { darkMode: ["class"], content: [ './pages/*/.{ts,tsx}', './components/*/.{ts,tsx}', './app/*/.{ts,tsx}', './src/*/.{ts,tsx}', ], theme: { container: { center: true, padding: "2rem", screens: { "2xl": "1400px", }, }, extend: { colors: { border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))", }, destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))", }, muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))", }, accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))", }, popover: { DEFAULT: "hsl(var(--popover))", foreground: "hsl(var(--popover-foreground))", }, card: { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, keyframes: { "accordion-down": { from: { height: 0 }, to: { height: "var(--radix-accordion-content-height)" }, }, "accordion-up": { from: { height: "var(--radix-accordion-content-height)" }, to: { height: 0 }, }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", }, }, }, plugins: [require("tailwindcss-animate")], } ``` ```css @tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; --primary: 221.2 83.2% 53.3%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 221.2 83.2% 53.3%; --radius: 0.5rem; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --card: 222.2 84% 4.9%; --card-foreground: 210 40% 98%; --popover: 222.2 84% 4.9%; --popover-foreground: 210 40% 98%; --primary: 217.2 91.2% 59.8%; --primary-foreground: 222.2 47.4% 11.2%; --secondary: 217.2 32.6% 17.5%; --secondary-foreground: 210 40% 98%; --muted: 217.2 32.6% 17.5%; --muted-foreground: 215 20.2% 65.1%; --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%; --ring: 224.3 76.3% 48%; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; } } ``` ```typescript // src/lib/utils.ts import { type ClassValue, clsx } from "clsx" import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } ``` Usage: Merge Tailwind classes without conflicts ```typescript cn("px-2 py-1", "px-3") // Result: "py-1 px-3" (px-3 overrides px-2) ``` ```typescript // Extend HTML attributes interface CustomButtonProps extends React.ButtonHTMLAttributes variant?: 'primary' | 'secondary' | 'outline' size?: 'sm' | 'md' | 'lg' loading?: boolean } // Or use type type CustomButtonProps = { variant?: 'primary' | 'secondary' | 'outline' size?: 'sm' | 'md' | 'lg' loading?: boolean } & React.ButtonHTMLAttributes ``` ```typescript // Define API response types interface User { id: string name: string email: string role: 'admin' | 'user' } interface ApiResponse data: T message: string status: 'success' | 'error' } // Usage const fetchUser = async (id: string): Promise const response = await fetch( return response.json() } ``` ```typescript // Focus management const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Escape') { onClose() } if (e.key === 'Enter' || e.key === ' ') { onSelect() e.preventDefault() } } // Trap focus in modal import { useFocusTrap } from '@/lib/hooks/use-focus-trap' function Modal({ children }: { children: React.ReactNode }) { const modalRef = useFocusTrap() return ( {children} ) } ``` ```typescript // Screen reader support aria-label="Close dialog" aria-pressed={isPressed} aria-expanded={isExpanded} > // Form accessibility id="email" type="email" aria-required="true" aria-invalid={hasError} aria-describedby={hasError ? "email-error" : undefined} /> {hasError && Invalid email ``` ```typescript // Lazy load routes import { lazy, Suspense } from 'react' const Dashboard = lazy(() => import('./pages/Dashboard')) const Settings = lazy(() => import('./pages/Settings')) function App() { return ( ) } ``` ```typescript import { memo, useMemo, useCallback } from 'react' // Memo component const ExpensiveComponent = memo(({ data }: { data: Data[] }) => { return }) // Memo computation function Component({ items }: { items: Item[] }) { const sortedItems = useMemo( () => items.sort((a, b) => a.name.localeCompare(b.name)), [items] ) const handleClick = useCallback(() => { console.log('Clicked') }, []) return } ``` ```bash npm install framer-motion ``` High-Impact Moments Over Random Motion: ```typescript // app/layout.tsx or page wrapper import { motion, AnimatePresence } from 'framer-motion' export default function PageTransition({ children }: { children: React.ReactNode }) { return ( initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] // Custom ease (easeOutExpo) }} > {children} ) } ``` ```typescript import { motion } from 'framer-motion' const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { staggerChildren: 0.1 // Delay between each child animation } } } const item = { hidden: { opacity: 0, y: 20 }, show: { opacity: 1, y: 0 } } export function StaggeredList({ items }: { items: string[] }) { return ( variants={container} initial="hidden" animate="show" className="space-y-2" > {items.map((item, i) => ( key={i} variants={item} className="p-4 bg-card rounded-lg" > {item} ))} ) } ``` ```typescript import { motion } from 'framer-motion' export function AnimatedCard({ children }: { children: React.ReactNode }) { return ( whileHover={{ scale: 1.02, y: -4 }} whileTap={{ scale: 0.98 }} transition={{ type: "spring", stiffness: 400, damping: 17 }} className="rounded-xl border bg-card p-6 shadow-sm cursor-pointer" > {children} ) } ``` ```typescript import { motion, LayoutGroup } from 'framer-motion' export function TabsWithAnimation({ tabs }: { tabs: Tab[] }) { const [activeTab, setActiveTab] = useState(0) return ( {tabs.map((tab, i) => ( key={i} onClick={() => setActiveTab(i)} className="relative px-4 py-2 rounded-md" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {tab.label} {activeTab === i && ( layoutId="activeTab" className="absolute inset-0 bg-primary rounded-md" style={{ zIndex: -1 }} transition={{ type: "spring", stiffness: 500, damping: 30 }} /> )} ))} ) } ``` ```typescript import { motion, useScroll, useTransform } from 'framer-motion' import { useRef } from 'react' export function ParallaxSection() { const ref = useRef(null) const { scrollYProgress } = useScroll({ target: ref, offset: ["start end", "end start"] }) const y = useTransform(scrollYProgress, [0, 1], [0, -100]) const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [0, 1, 0]) return ( ref={ref} style={{ y, opacity }} className="min-h-screen flex items-center justify-center" > ) } ``` ```typescript import { motion, AnimatePresence } from 'framer-motion' export function AnimatedDialog({ open, onClose, children }: { open: boolean onClose: () => void children: React.ReactNode }) { return ( {open && ( <> {/ Backdrop /} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} onClick={onClose} className="fixed inset-0 bg-black/80 z-50" /> {/ Dialog content /} initial={{ opacity: 0, scale: 0.95, y: 20 }} animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.95, y: 20 }} transition={{ type: "spring", stiffness: 300, damping: 30 }} className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 bg-background p-6 rounded-lg shadow-lg max-w-md w-full" > {children} > )} ) } ``` ```typescript import { motion, useDragControls } from 'framer-motion' export function DraggableCard() { const controls = useDragControls() return ( drag dragControls={controls} dragConstraints={{ left: 0, right: 300, top: 0, bottom: 300 }} dragElastic={0.1} whileDrag={{ scale: 1.05, cursor: "grabbing" }} className="w-32 h-32 bg-primary rounded-lg cursor-grab" /> ) } // Swipe to dismiss export function SwipeableBanner({ onDismiss }: { onDismiss: () => void }) { return ( drag="x" dragConstraints={{ left: 0, right: 0 }} onDragEnd={(e, { offset, velocity }) => { if (Math.abs(offset.x) > 100 || Math.abs(velocity.x) > 500) { onDismiss() } }} className="p-4 bg-card border rounded-lg" > Swipe to dismiss ) } ``` ```typescript import { motion } from 'framer-motion' export function LoadingSpinner() { return ( animate={{ rotate: 360 }} transition={{ repeat: Infinity, duration: 1, ease: "linear" }} className="w-8 h-8 border-4 border-primary border-t-transparent rounded-full" /> ) } export function PulseLoader() { return ( {[0, 1, 2].map((i) => ( key={i} animate={{ scale: [1, 1.2, 1], opacity: [0.5, 1, 0.5] }} transition={{ duration: 1, repeat: Infinity, delay: i * 0.2 }} className="w-3 h-3 bg-primary rounded-full" /> ))} ) } ``` ```typescript import { motion, useReducedMotion } from 'framer-motion' export function AccessibleAnimation({ children }: { children: React.ReactNode }) { const shouldReduceMotion = useReducedMotion() return ( initial={shouldReduceMotion ? false : { opacity: 0, y: 20 }} animate={shouldReduceMotion ? false : { opacity: 1, y: 0 }} transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.5 }} > {children} ) } ``` What to Animate (GPU-Accelerated): What to Avoid Animating: Optimization Tips: ```typescript // Use will-change for smoother animations // Lazy load motion components import { motion, LazyMotion, domAnimation } from "framer-motion" // Reduce animation complexity on low-end devices const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches ``` Use CSS Animations: Use Framer Motion: Use this skill when: This stack provides excellent developer experience, type safety, accessibility, and performance for modern web development.3. Dialog/Modal Component
Tailwind CSS Configuration
Enhanced tailwind.config.js
CSS Variables (globals.css)
Utility Functions
cn() - Class Name Utility
TypeScript Best Practices
Component Props Typing
API Response Typing
/api/users/${id})Accessibility Patterns
Keyboard Navigation
ARIA Labels
Performance Optimization
Code Splitting
Memoization
Animation with Framer Motion
Installation
Core Principles
prefers-reduced-motionPage Transitions
Staggered List Animation
Card Hover Effects
Layout Animations (Shared Layout)
Scroll-Based Animations
Parallax Content
Modal / Dialog Animations
Gesture Animations
Loading States with Animation
Respecting Reduced Motion
Performance Best Practices
opacitytransform (translate, scale, rotate)filter (blur, brightness)width, height (causes layout thrashing)top, left, margin, padding (use transform: translate instead)color, background-color (expensive, use sparingly)When to Use Framer Motion vs CSS
When to Apply
More from this repository10
Premium marketplace for revolutionary autonomous AI agents featuring four-tier architecture, intelligent pattern learning, comprehensive quality control, and full-stack validation with 80-90% auto-fix success rates.
Validates web applications by detecting JavaScript errors, capturing screenshots, testing authentication flows, and monitoring browser console across multiple device viewports.
Validates Claude Code plugin manifests and structures to ensure guideline compliance, prevent installation failures, and maintain compatibility.
Provides actionable templates and standards for creating comprehensive, clear technical documentation across programming languages and project types.
Optimizes autonomous agent performance across different LLM models by dynamically adjusting scaling factors, execution strategies, and quality thresholds.
Enforces comprehensive code quality standards, metrics, and best practices across multiple programming languages.
Identifies and transfers contextual code patterns across projects by analyzing technology stacks, architectural styles, domain semantics, and team practices.
Enables reliable web searching by using an autonomous agent as a fallback when standard WebSearch encounters errors or limitations.
Autonomously learns, stores, and retrieves project-specific task execution patterns to continuously improve skill performance and strategy recommendations.
Automates advanced Git operations with intelligent repository analysis, branching strategies, commit optimization, and release workflows.