| name | ui-component-builder |
| description | Build, convert, and optimize React/Vue/Svelte UI components with TypeScript, advanced patterns, animations, and accessibility |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep, Task |
UI Component Builder
Expert skill for building production-ready UI components for component libraries. Specializes in React, Vue, and Svelte with TypeScript, implementing advanced patterns, animations, and ensuring accessibility and performance.
Core Capabilities
1. Component Creation
- Create new components from scratch with TypeScript
- Implement compound components pattern
- Build components with render props
- Create custom hooks and composables
- Support for polymorphic components (as prop pattern)
- Generic component types for maximum reusability
2. Framework Conversion
Convert components seamlessly between frameworks:
- React → Vue: JSX to template/composition API
- React → Svelte: Hooks to reactive declarations
- Vue → React: Template to JSX, composables to hooks
- Svelte → React: Reactive syntax to useState/useEffect
Preserve functionality, props interface, and behavior while adapting to framework idioms.
3. Advanced Patterns Implementation
Compound Components
// Parent component manages shared state
// Child components access via context
<Select>
<Select.Trigger />
<Select.Content>
<Select.Item value="1">Option 1</Select.Item>
</Select.Content>
</Select>
Render Props
<DataFetcher url="/api/data">
{({ data, loading, error }) => (
// Render logic here
)}
</DataFetcher>
Custom Hooks Pattern
// Extract reusable logic
const { isOpen, open, close, toggle } = useDisclosure()
4. Animation Integration
- Integrate Framer Motion for React components
- Use transition/animation APIs for Vue/Svelte
- Create spring physics animations
- Implement gesture controls (drag, hover, tap)
- Build enter/exit animations
- Orchestrate complex animation sequences
- Optimize for 60fps performance
5. TypeScript Excellence
- Strict type safety with proper generic constraints
- Discriminated unions for component variants
- Type inference for props and state
- Utility types (Omit, Pick, Required, Partial)
- Type guards and narrowing
- Proper typing for refs, events, and children
6. Accessibility (a11y) First
- Semantic HTML elements
- ARIA attributes when needed (roles, labels, states)
- Keyboard navigation (Tab, Enter, Escape, Arrow keys)
- Focus management and focus trapping
- Screen reader announcements
- Color contrast compliance (WCAG AA/AAA)
- Reduced motion support
7. Performance Optimization
- React.memo / Vue computed / Svelte reactive statements
- useMemo, useCallback optimization
- Virtual scrolling for large lists
- Code splitting and lazy loading
- Bundle size analysis
- Avoid unnecessary re-renders
- Optimize event handlers
Workflow
Phase 1: Analysis & Planning
Understand Requirements
- Component purpose and use cases
- Required props and variants
- Accessibility requirements
- Animation needs
- Performance constraints
Review Existing Code
- Check for similar components
- Identify reusable patterns
- Review design system tokens
- Check naming conventions
Plan Implementation
- Choose appropriate patterns
- Define component API (props interface)
- Plan state management approach
- Identify edge cases
Phase 2: Implementation
Create Base Structure
- Set up component file(s)
- Define TypeScript interfaces
- Implement basic rendering
- Add prop validation
Implement Core Logic
- Add state management
- Implement event handlers
- Apply business logic
- Handle edge cases
Add Styling
- Use design system tokens
- Implement variants (size, color, state)
- Add responsive behavior
- Support theming (light/dark)
Enhance with Features
- Add animations if needed
- Implement keyboard navigation
- Add ARIA attributes
- Create controlled/uncontrolled variants
Phase 3: Quality Assurance
Accessibility Check
- Test keyboard navigation
- Verify ARIA attributes
- Check screen reader output
- Validate color contrast
Performance Review
- Check for unnecessary renders
- Optimize heavy computations
- Review bundle impact
- Test with React DevTools Profiler
Type Safety Verification
- No TypeScript errors
- Props are properly typed
- Type inference works correctly
- Generic constraints are sound
Cross-browser Testing
- Modern browsers support
- Graceful degradation
- Polyfills if needed
Component Patterns Library
Button Component Template
import { forwardRef, type ButtonHTMLAttributes } from 'react'
import { cva, type VariantProps } from 'class-variance-authority'
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
outline: 'border border-input bg-background hover:bg-accent',
ghost: 'hover:bg-accent hover:text-accent-foreground',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, ...props }, ref) => {
return (
<button
ref={ref}
className={buttonVariants({ variant, size, className })}
{...props}
/>
)
}
)
Button.displayName = 'Button'
export { Button, buttonVariants }
Compound Component Template
import { createContext, useContext, useState } from 'react'
interface TabsContextValue {
activeTab: string
setActiveTab: (tab: string) => void
}
const TabsContext = createContext<TabsContextValue | undefined>(undefined)
export function Tabs({ defaultValue, children }: TabsProps) {
const [activeTab, setActiveTab] = useState(defaultValue)
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div>{children}</div>
</TabsContext.Provider>
)
}
export function TabsList({ children }: TabsListProps) {
return <div role="tablist">{children}</div>
}
export function TabsTrigger({ value, children }: TabsTriggerProps) {
const context = useContext(TabsContext)
const isActive = context?.activeTab === value
return (
<button
role="tab"
aria-selected={isActive}
onClick={() => context?.setActiveTab(value)}
>
{children}
</button>
)
}
export function TabsContent({ value, children }: TabsContentProps) {
const context = useContext(TabsContext)
if (context?.activeTab !== value) return null
return <div role="tabpanel">{children}</div>
}
Animated Component Template (Framer Motion)
import { motion, AnimatePresence } from 'framer-motion'
import { useState } from 'react'
export function Modal({ isOpen, onClose, children }: ModalProps) {
return (
<AnimatePresence>
{isOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
className="fixed inset-0 bg-black/50"
/>
<motion.div
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', duration: 0.3 }}
className="fixed inset-0 flex items-center justify-center"
>
<div className="bg-white rounded-lg p-6 max-w-md w-full">
{children}
</div>
</motion.div>
</>
)}
</AnimatePresence>
)
}
Framework Conversion Guidelines
React to Vue Conversion
// REACT
function Counter() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log('Count changed:', count)
}, [count])
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
// VUE (Composition API)
<script setup lang="ts">
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newCount) => {
console.log('Count changed:', newCount)
})
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
React to Svelte Conversion
// REACT
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
// SVELTE
<script lang="ts">
let count = 0
$: console.log('Count changed:', count)
</script>
<button on:click={() => count++}>
{count}
</button>
Best Practices
Component Structure
- One component per file (with related subcomponents)
- Co-locate types, styles, and utilities
- Export component as named export
- Provide displayName for debugging
Props Design
- Keep props interface simple and focused
- Use discriminated unions for variants
- Provide sensible defaults
- Support both controlled and uncontrolled modes
- Accept className for style overrides
- Forward refs when wrapping native elements
State Management
- Lift state up when needed by multiple components
- Keep component-specific state local
- Use context sparingly (avoid prop drilling)
- Prefer composition over context
Naming Conventions
- PascalCase for components
- camelCase for props and functions
- SCREAMING_SNAKE_CASE for constants
- Prefix custom hooks with "use"
- Prefix event handlers with "handle" or "on"
File Organization
components/
Button/
Button.tsx # Main component
Button.types.ts # Type definitions
Button.styles.ts # Styling (if separate)
Button.test.tsx # Unit tests
index.ts # Public exports
Accessibility Checklist
- Semantic HTML elements used
- Keyboard navigation works
- Focus visible and manageable
- ARIA attributes where needed
- Screen reader tested
- Color contrast meets WCAG AA
- Supports prefers-reduced-motion
Performance Checklist
- No unnecessary re-renders
- Heavy computations memoized
- Event handlers optimized
- Bundle size impact minimal
- Lazy loading where appropriate
- Virtual scrolling for large lists
Common Patterns
Polymorphic Component (as prop)
type AsProp<C extends React.ElementType> = {
as?: C
}
type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P)
type PolymorphicComponentProp<
C extends React.ElementType,
Props = {}
> = React.PropsWithChildren<Props & AsProp<C>> &
Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>>
type TextProps<C extends React.ElementType> = PolymorphicComponentProp<
C,
{ color?: 'primary' | 'secondary' }
>
export function Text<C extends React.ElementType = 'span'>({
as,
color = 'primary',
children,
...props
}: TextProps<C>) {
const Component = as || 'span'
return <Component className={color} {...props}>{children}</Component>
}
// Usage: <Text as="h1">Title</Text>
Controlled/Uncontrolled Pattern
interface InputProps {
value?: string // Controlled
defaultValue?: string // Uncontrolled
onChange?: (value: string) => void
}
function Input({ value, defaultValue, onChange }: InputProps) {
const [internalValue, setInternalValue] = useState(defaultValue ?? '')
const isControlled = value !== undefined
const currentValue = isControlled ? value : internalValue
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value
if (!isControlled) {
setInternalValue(newValue)
}
onChange?.(newValue)
}
return <input value={currentValue} onChange={handleChange} />
}
Focus Trap (for Modals)
import { useEffect, useRef } from 'react'
function useFocusTrap(isActive: boolean) {
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!isActive) return
const container = containerRef.current
if (!container) return
const focusableElements = container.querySelectorAll(
'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])'
)
const firstElement = focusableElements[0] as HTMLElement
const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement
const handleTab = (e: KeyboardEvent) => {
if (e.key !== 'Tab') return
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault()
lastElement.focus()
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault()
firstElement.focus()
}
}
container.addEventListener('keydown', handleTab)
firstElement?.focus()
return () => container.removeEventListener('keydown', handleTab)
}, [isActive])
return containerRef
}
Tools & Dependencies
Essential Tools
- TypeScript: Type safety and better DX
- class-variance-authority (cva): Variant management
- clsx / cn: Conditional className composition
- Framer Motion: Advanced animations
- React Hook Form: Form state management
- Zod: Runtime validation
Development Tools
- Vite: Fast development server
- tsup: TypeScript bundler
- Vitest: Testing framework
- React Testing Library: Component testing
- Storybook: Component development
Tips for Success
- Start Simple: Build basic functionality first, then add complexity
- Think in Components: Break down complex UIs into smaller pieces
- Composition Over Inheritance: Use composition patterns
- Test Early: Write tests as you build
- Document Props: Add JSDoc comments for prop descriptions
- Use Design Tokens: Reference design system values
- Consider Edge Cases: Empty states, loading, errors
- Mobile First: Design for mobile, enhance for desktop
- Performance Budget: Keep bundle size in check
- Accessibility Always: Build accessible from the start
When to Use This Skill
Activate this skill when you need to:
- Create new UI components from scratch
- Convert components between React/Vue/Svelte
- Implement advanced component patterns
- Add animations to components
- Optimize component performance
- Improve component accessibility
- Refactor existing components
- Build compound components
- Create polymorphic components
- Implement custom hooks/composables
Output Format
When creating components, provide:
- Complete Component Code: Fully typed and production-ready
- Usage Example: Show how to use the component
- Props Documentation: Describe all props with types
- Accessibility Notes: Keyboard shortcuts and ARIA usage
- Performance Tips: Any optimization considerations
- Styling Notes: How to customize appearance
Always follow the project's existing conventions and patterns.