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.
Implements contract-first static UI components using TypeScript interfaces and compound component patterns. Triggers when users request to create React components, build static UI, or use compound components with TypeScript.
/plugin marketplace add constellos/claude-code-plugins/plugin install enhanced-context@constellos-localThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Contract-first static UI methodology using TypeScript interfaces and compound components.
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.
Define TypeScript interfaces before implementation:
// 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
}
All components are React Server Components unless they need:
// 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>
);
}
Structure complex components with composable parts:
// 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 };
Start with the component contract:
// 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';
}
Implement each part of the compound component:
// 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>
);
}
Organize exports in index.ts:
// index.ts
export {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
} from './card';
export type {
CardProps,
CardHeaderProps,
CardTitleProps,
CardDescriptionProps,
CardContentProps,
CardFooterProps,
} from './types';
Apply mobile-first responsive styling:
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>
);
}
Install and use Shadcn components as the foundation:
npx shadcn@latest add button card input
Reference: https://ui.shadcn.com/docs/components
Extend Shadcn components when needed:
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>
);
}
Use Radix for accessible, unstyled primitives:
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
Use AI Elements for AI-specific UI patterns:
import { Chat, Message, MessageInput } from '@ai-elements/react';
export function ChatInterface({ messages, onSend }: ChatInterfaceProps) {
return (
<Chat>
{messages.map((msg) => (
<Message key={msg.id} role={msg.role}>
{msg.content}
</Message>
))}
<MessageInput onSubmit={onSend} />
</Chat>
);
}
Organize component files consistently:
components/
└── feature-card/
├── index.ts # Barrel exports
├── types.ts # TypeScript interfaces
├── feature-card.tsx # Main component
├── feature-card.test.tsx # Tests
└── WIREFRAME.md # Layout documentation
For compound components:
components/
└── card/
├── index.ts # Exports all parts
├── types.ts # All interfaces
├── card.tsx # Root component
├── card-header.tsx # Sub-component
├── card-content.tsx # Sub-component
├── card-footer.tsx # Sub-component
└── card.test.tsx # Tests
Prefer composable components over prop-heavy ones:
// Avoid: Too many props
<Card
title="Feature"
description="Description"
footer={<Button>Action</Button>}
headerIcon={<Icon />}
/>
// Prefer: Composable structure
<Card>
<CardHeader>
<Icon />
<CardTitle>Feature</CardTitle>
<CardDescription>Description</CardDescription>
</CardHeader>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>
Use discriminated unions for variants:
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'destructive';
type ButtonSize = 'sm' | 'md' | 'lg';
interface ButtonProps {
variant?: ButtonVariant;
size?: ButtonSize;
children: ReactNode;
}
const variantStyles: Record<ButtonVariant, string> = {
primary: 'bg-primary text-primary-foreground hover:bg-primary/90',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
};
const sizeStyles: Record<ButtonSize, string> = {
sm: 'h-8 px-3 text-xs',
md: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base',
};
Support ref forwarding for DOM access:
import { forwardRef } from 'react';
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
'flex h-10 w-full rounded-md border bg-background px-3 py-2',
className
)}
ref={ref}
{...props}
/>
);
}
);
Input.displayName = 'Input';
any, prefer unknown for generic inputstext-foreground, bg-background, not raw colorsBefore proceeding to interaction implementation:
After static UI is complete, proceed to the ui-interaction skill for adding client-side events, local state, and form validation.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.