component-library
Component Library Development
Build scalable, documented component libraries and design systems.
Instructions
- Start with design tokens - Colors, spacing, typography as variables
- Use atomic design - Atoms -> Molecules -> Organisms -> Templates
- Document with Storybook - Interactive component documentation
- Design flexible APIs - Props for variants, not endless components
- Test components - Visual regression and interaction testing
Design Tokens
Token Structure
// tokens/colors.ts
export const colors = {
// Primitives (raw values)
gray: {
50: '#f9fafb',
100: '#f3f4f6',
200: '#e5e7eb',
300: '#d1d5db',
400: '#9ca3af',
500: '#6b7280',
600: '#4b5563',
700: '#374151',
800: '#1f2937',
900: '#111827',
},
blue: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
};
// Semantic tokens (purpose-based)
export const semanticColors = {
text: {
primary: colors.gray[900],
secondary: colors.gray[600],
disabled: colors.gray[400],
inverse: colors.gray[50],
},
background: {
default: colors.gray[50],
paper: '#ffffff',
subtle: colors.gray[100],
},
border: {
default: colors.gray[200],
focus: colors.blue[500],
},
action: {
primary: colors.blue[600],
primaryHover: colors.blue[700],
},
};
Spacing & Typography Tokens
// tokens/spacing.ts
export const spacing = {
px: '1px',
0.5: '0.125rem', // 2px
1: '0.25rem', // 4px
2: '0.5rem', // 8px
3: '0.75rem', // 12px
4: '1rem', // 16px
6: '1.5rem', // 24px
8: '2rem', // 32px
12: '3rem', // 48px
16: '4rem', // 64px
};
// tokens/typography.ts
export const typography = {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
fontSize: {
xs: ['0.75rem', { lineHeight: '1rem' }],
sm: ['0.875rem', { lineHeight: '1.25rem' }],
base: ['1rem', { lineHeight: '1.5rem' }],
lg: ['1.125rem', { lineHeight: '1.75rem' }],
xl: ['1.25rem', { lineHeight: '1.75rem' }],
'2xl': ['1.5rem', { lineHeight: '2rem' }],
},
fontWeight: {
normal: '400',
medium: '500',
semibold: '600',
bold: '700',
},
};
Component Patterns
Variant-Based Components (CVA)
// Using class-variance-authority
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
// Base styles
'inline-flex items-center justify-center rounded-lg font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus-visible:ring-blue-500',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:ring-gray-500',
outline: 'border border-gray-300 bg-transparent hover:bg-gray-100 focus-visible:ring-gray-500',
ghost: 'hover:bg-gray-100 focus-visible:ring-gray-500',
danger: 'bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
loading?: boolean;
}
export function Button({
className,
variant,
size,
loading,
disabled,
children,
...props
}: ButtonProps) {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
disabled={disabled || loading}
{...props}
>
{loading && <Spinner className="mr-2 h-4 w-4" />}
{children}
</button>
);
}
Compound Components
import { createContext, useContext, useState, ReactNode } from 'react';
// Context for compound component
interface TabsContextType {
activeTab: string;
setActiveTab: (id: string) => void;
}
const TabsContext = createContext<TabsContextType | null>(null);
function useTabs() {
const context = useContext(TabsContext);
if (!context) {
throw new Error('Tabs components must be used within Tabs');
}
return context;
}
// Root component
interface TabsProps {
defaultValue: string;
children: ReactNode;
className?: string;
}
export function Tabs({ defaultValue, children, className }: TabsProps) {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className={className}>{children}</div>
</TabsContext.Provider>
);
}
// Tab list
Tabs.List = function TabsList({ children, className }: { children: ReactNode; className?: string }) {
return (
<div role="tablist" className={cn('flex border-b', className)}>
{children}
</div>
);
};
// Individual tab trigger
Tabs.Trigger = function TabsTrigger({
value,
children,
className,
}: {
value: string;
children: ReactNode;
className?: string;
}) {
const { activeTab, setActiveTab } = useTabs();
const isActive = activeTab === value;
return (
<button
role="tab"
aria-selected={isActive}
onClick={() => setActiveTab(value)}
className={cn(
'px-4 py-2 text-sm font-medium transition-colors',
isActive
? 'border-b-2 border-blue-600 text-blue-600'
: 'text-gray-500 hover:text-gray-700',
className
)}
>
{children}
</button>
);
};
// Tab content panel
Tabs.Content = function TabsContent({
value,
children,
className,
}: {
value: string;
children: ReactNode;
className?: string;
}) {
const { activeTab } = useTabs();
if (activeTab !== value) return null;
return (
<div role="tabpanel" className={cn('py-4', className)}>
{children}
</div>
);
};
// Usage
<Tabs defaultValue="tab1">
<Tabs.List>
<Tabs.Trigger value="tab1">Overview</Tabs.Trigger>
<Tabs.Trigger value="tab2">Details</Tabs.Trigger>
<Tabs.Trigger value="tab3">Settings</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="tab1">Overview content</Tabs.Content>
<Tabs.Content value="tab2">Details content</Tabs.Content>
<Tabs.Content value="tab3">Settings content</Tabs.Content>
</Tabs>
Storybook Documentation
Story Structure
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'outline', 'ghost', 'danger'],
description: 'The visual style of the button',
},
size: {
control: 'radio',
options: ['sm', 'md', 'lg'],
},
loading: {
control: 'boolean',
},
disabled: {
control: 'boolean',
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
children: 'Primary Button',
variant: 'primary',
},
};
export const AllVariants: Story = {
render: () => (
<div className="flex flex-wrap gap-4">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="danger">Danger</Button>
</div>
),
};
export const Sizes: Story = {
render: () => (
<div className="flex items-center gap-4">
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>
),
};
Package Structure
my-component-library/
├── src/
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.test.tsx
│ │ │ ├── Button.stories.tsx
│ │ │ └── index.ts
│ │ ├── Input/
│ │ ├── Card/
│ │ └── index.ts
│ ├── tokens/
│ │ ├── colors.ts
│ │ ├── spacing.ts
│ │ ├── typography.ts
│ │ └── index.ts
│ ├── hooks/
│ │ └── index.ts
│ ├── utils/
│ │ ├── cn.ts
│ │ └── index.ts
│ └── index.ts
├── .storybook/
├── package.json
└── tsconfig.json
Best Practices
- Single responsibility - One component, one job
- Composition over inheritance - Compose small components
- Prop drilling limits - Max 3 levels, then use context
- Forward refs - Allow parent access to DOM
- Semantic versioning - Breaking changes = major version
- Changelog - Document all changes
When to Use
- Building shared UI libraries
- Creating design systems
- Standardizing UI across teams
- Open-source component packages
- Enterprise design consistency
Notes
- Use Radix or Headless UI for accessible primitives
- Consider tree-shaking for bundle size
- Test across browsers and screen sizes
- Document accessibility requirements
More from housegarofalo/claude-code-base
mqtt-iot
Configure MQTT brokers (Mosquitto, EMQX) for IoT messaging, device communication, and smart home integration. Manage topics, QoS levels, authentication, and bridging. Use when setting up IoT messaging, smart home communication, or device-to-cloud connectivity. (project)
22devops-engineer-agent
Infrastructure and DevOps specialist. Manages Docker, Kubernetes, CI/CD pipelines, and cloud deployments. Expert in GitHub Actions, Azure DevOps, Terraform, and container orchestration. Use for deployment automation, infrastructure setup, or CI/CD optimization.
6react-typescript
Build modern React applications with TypeScript. Covers React 18+ patterns, hooks, component architecture, state management (Zustand, Redux Toolkit), server components, and best practices. Use for React development, TypeScript integration, component design, and frontend architecture.
5power-automate
Expert guidance for Power Automate development including cloud flows, desktop flows, Dataverse connector, expression functions, custom connectors, error handling, and child flow patterns. Use when building automated workflows, writing flow expressions, creating custom connectors from OpenAPI, or implementing error handling patterns.
5svelte-kit
Expert guidance for SvelteKit 2 with Svelte 5 runes, server-side rendering, and modern patterns. Covers $state, $derived, $effect, form actions, load functions, and API routes. Use for SvelteKit applications, Svelte 5 runes, and full-stack Svelte development.
5matter-thread
>
5