Claude
Skills
Sign in
Back

UI Design

Included with Lifetime
$97 forever

This skill should be used when the user asks to "create a component", "build static UI", "design with TypeScript", "use compound components", "implement contract-first UI", "create React component", "build with Shadcn", or mentions TypeScript interfaces for components, compound component patterns, or Server Components. Provides contract-first static UI methodology with compound components.

Design

What this skill does


# UI Design

Contract-first static UI methodology using TypeScript interfaces and compound components.

## Purpose

UI Design is the second step in the UI development workflow, following wireframing. Define TypeScript interfaces first (the "contract"), then implement compound components with Tailwind CSS styling. Server Components are the default.

## When to Use

- After wireframes are approved (WIREFRAME.md exists)
- When implementing new UI components
- When creating reusable component libraries
- When defining component APIs with TypeScript

## Core Principles

### Contract-First Development

Define TypeScript interfaces before implementation:

```typescript
// 1. Define the contract first
interface FeatureCardProps {
  title: string;
  description: string;
  icon: IconName;
  href?: string;
}

// 2. Then implement the component
function FeatureCard({ title, description, icon, href }: FeatureCardProps) {
  // Implementation
}
```

### Server Components by Default

All components are React Server Components unless they need:
- Event handlers (onClick, onChange)
- Browser APIs (localStorage, window)
- React hooks (useState, useEffect)

```typescript
// Server Component (default) - no "use client" directive
export function FeatureCard({ title, description }: FeatureCardProps) {
  return (
    <article className="rounded-lg border p-6">
      <h3 className="text-lg font-semibold">{title}</h3>
      <p className="text-muted-foreground">{description}</p>
    </article>
  );
}
```

### Compound Component Pattern

Structure complex components with composable parts:

```typescript
// Root component provides context
function Card({ children, className }: CardProps) {
  return (
    <div className={cn("rounded-lg border bg-card", className)}>
      {children}
    </div>
  );
}

// Sub-components for each section
function CardHeader({ children, className }: CardHeaderProps) {
  return (
    <div className={cn("flex flex-col space-y-1.5 p-6", className)}>
      {children}
    </div>
  );
}

function CardTitle({ children, className }: CardTitleProps) {
  return (
    <h3 className={cn("text-2xl font-semibold", className)}>
      {children}
    </h3>
  );
}

function CardContent({ children, className }: CardContentProps) {
  return (
    <div className={cn("p-6 pt-0", className)}>
      {children}
    </div>
  );
}

function CardFooter({ children, className }: CardFooterProps) {
  return (
    <div className={cn("flex items-center p-6 pt-0", className)}>
      {children}
    </div>
  );
}

// Export compound component
export { Card, CardHeader, CardTitle, CardContent, CardFooter };
```

## UI Design Workflow

### Step 1: Define TypeScript Interfaces

Start with the component contract:

```typescript
// types.ts
import type { ReactNode } from 'react';

export interface CardProps {
  children: ReactNode;
  className?: string;
  variant?: 'default' | 'outlined' | 'elevated';
}

export interface CardHeaderProps {
  children: ReactNode;
  className?: string;
}

export interface CardTitleProps {
  children: ReactNode;
  className?: string;
  as?: 'h1' | 'h2' | 'h3' | 'h4';
}

export interface CardDescriptionProps {
  children: ReactNode;
  className?: string;
}

export interface CardContentProps {
  children: ReactNode;
  className?: string;
}

export interface CardFooterProps {
  children: ReactNode;
  className?: string;
  align?: 'start' | 'center' | 'end' | 'between';
}
```

### Step 2: Create Compound Components

Implement each part of the compound component:

```typescript
// card.tsx
import { cn } from '@/lib/utils';
import type {
  CardProps,
  CardHeaderProps,
  CardTitleProps,
  CardDescriptionProps,
  CardContentProps,
  CardFooterProps,
} from './types';

const variantStyles = {
  default: 'bg-card text-card-foreground',
  outlined: 'border-2 bg-transparent',
  elevated: 'bg-card shadow-lg',
} as const;

export function Card({ children, className, variant = 'default' }: CardProps) {
  return (
    <div
      className={cn(
        'rounded-lg border',
        variantStyles[variant],
        className
      )}
    >
      {children}
    </div>
  );
}

export function CardHeader({ children, className }: CardHeaderProps) {
  return (
    <div className={cn('flex flex-col space-y-1.5 p-6', className)}>
      {children}
    </div>
  );
}

export function CardTitle({
  children,
  className,
  as: Tag = 'h3',
}: CardTitleProps) {
  return (
    <Tag className={cn('text-2xl font-semibold leading-none tracking-tight', className)}>
      {children}
    </Tag>
  );
}

export function CardDescription({ children, className }: CardDescriptionProps) {
  return (
    <p className={cn('text-sm text-muted-foreground', className)}>
      {children}
    </p>
  );
}

export function CardContent({ children, className }: CardContentProps) {
  return (
    <div className={cn('p-6 pt-0', className)}>
      {children}
    </div>
  );
}

const alignStyles = {
  start: 'justify-start',
  center: 'justify-center',
  end: 'justify-end',
  between: 'justify-between',
} as const;

export function CardFooter({
  children,
  className,
  align = 'start',
}: CardFooterProps) {
  return (
    <div className={cn('flex items-center p-6 pt-0', alignStyles[align], className)}>
      {children}
    </div>
  );
}
```

### Step 3: Create Barrel Export

Organize exports in index.ts:

```typescript
// index.ts
export {
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
} from './card';

export type {
  CardProps,
  CardHeaderProps,
  CardTitleProps,
  CardDescriptionProps,
  CardContentProps,
  CardFooterProps,
} from './types';
```

### Step 4: Style with Tailwind CSS

Apply mobile-first responsive styling:

```typescript
export function FeatureGrid({ features }: FeatureGridProps) {
  return (
    <div className={cn(
      // Mobile: single column
      'grid grid-cols-1 gap-4',
      // Tablet: two columns
      'md:grid-cols-2 md:gap-6',
      // Desktop: three columns
      'lg:grid-cols-3 lg:gap-8'
    )}>
      {features.map((feature) => (
        <Card key={feature.id}>
          <CardHeader>
            <CardTitle>{feature.title}</CardTitle>
            <CardDescription>{feature.description}</CardDescription>
          </CardHeader>
        </Card>
      ))}
    </div>
  );
}
```

## Component Library Integration

### Shadcn UI

Install and use Shadcn components as the foundation:

```bash
npx shadcn@latest add button card input
```

Reference: https://ui.shadcn.com/docs/components

Extend Shadcn components when needed:

```typescript
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';

interface LoadingButtonProps extends React.ComponentProps<typeof Button> {
  loading?: boolean;
}

export function LoadingButton({
  loading,
  children,
  disabled,
  className,
  ...props
}: LoadingButtonProps) {
  return (
    <Button
      disabled={disabled || loading}
      className={cn(loading && 'cursor-wait', className)}
      {...props}
    >
      {loading ? <Spinner className="mr-2 h-4 w-4" /> : null}
      {children}
    </Button>
  );
}
```

### Radix Primitives

Use Radix for accessible, unstyled primitives:

```typescript
import * as Dialog from '@radix-ui/react-dialog';

export function Modal({ open, onOpenChange, children }: ModalProps) {
  return (
    <Dialog.Root open={open} onOpenChange={onOpenChange}>
      <Dialog.Portal>
        <Dialog.Overlay className="fixed inset-0 bg-black/50" />
        <Dialog.Content className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white p-6">
          {children}
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}
```

Reference: https://www.radix-ui.com/primitives/docs/overview/introduction

### AI Elements

Use AI Elements for AI-specific UI patterns:

```typescript
import { Chat, Message, MessageInput } from '@ai-elements/react';

export function ChatInterface({ messages, onSend }: ChatInterfaceProps) {
  return (
    <Chat>
      {me

Related in Design