components · git:20260103.9f62587 · 2026-01-03 · sha256 e8f969e7cb94daa7
components git:20260103.9f62587A
Immutable. This exact content is served forever at /api/v1/blob/e8f969e7cb94daa7.
---
description: Component development patterns - Shadcn first, atomic design, Figma parity
globs: src/components/**/*
alwaysApply: false
---
# Component Rules
Rules for developing UI components. Based on component-guidelines.md and figma-context.md.
## Core Principles
### 1. Shadcn First
**ALWAYS check if Shadcn has an equivalent before creating custom components.**
```bash
# Check Shadcn for component
npx shadcn@latest add button --yes
# Then adapt to project structure
```
### 2. Figma Parity
**100% visual match with Figma designs** - no approximations.
- Use exact sizes, colors, spacing from Figma
- Verify using screenshots or MCP Figma tools
- Test all states (hover, focus, disabled, active)
### 3. Atomic Design
```
atoms/ → Basic components (Button, Input, Avatar, Badge)
molecules/ → Atom combinations (InputField, SearchBar, PostCard)
organisms/ → Complex features (PostFeed, UserProfile, Forms)
templates/ → Page layouts
```
## File Structure
```
src/components/atoms/Button/
├── Button.tsx # Main component
├── Button.test.tsx # Unit + snapshot tests
├── Button.types.ts # Type definitions
└── index.ts # Exports
```
## Component Template
```tsx
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/libs';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
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-8 px-3 text-xs',
lg: 'h-12 px-6',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, ...props }, ref) => (
<button
ref={ref}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
);
Button.displayName = 'Button';
export { Button, buttonVariants };
```
## Imports
```tsx
// ✅ Correct
import { cn } from '@/libs';
import { Button } from '@/components/atoms';
// ❌ Wrong
import { cn } from 'src/lib/utils';
import { cn } from '../../libs/utils';
```
## Exports
```tsx
// src/components/atoms/index.ts
export * from './Button';
export * from './Input';
export * from './Avatar';
```
## Design System Integration
### Colors
```tsx
// ✅ Use design tokens
<div className="bg-primary text-primary-foreground" />
<div className="bg-muted text-muted-foreground" />
<div className="border-border" />
// ❌ Don't hardcode
<div style={{ backgroundColor: '#ffffff' }} />
<div className="bg-[#1a1a1a]" />
```
### Sizing
```tsx
// ✅ Follow Figma scale
<Avatar className="h-10 w-10" /> // 40px
<Avatar className="h-8 w-8" /> // 32px
<Avatar className="h-6 w-6" /> // 24px
// ❌ Arbitrary sizes
<Avatar className="h-[37px] w-[37px]" />
```
### Spacing
```tsx
// ✅ Use Tailwind spacing scale
<div className="p-4 gap-2 space-y-4" />
// ❌ Arbitrary spacing
<div className="p-[13px]" />
```
## Testing Requirements
Every component needs:
1. **Test file**: `Component.test.tsx`
2. **Sanity test**: Renders without errors
3. **Functional tests**: Click, hover, interactions
4. **Snapshot tests**: All variants/states
```tsx
// See rules/component-testing.mdc for details
describe('Button', () => {
it('renders with default props', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button')).toBeInTheDocument();
});
});
describe('Button - Snapshots', () => {
it('matches snapshot for default variant', () => {
const { container } = render(<Button>Default</Button>);
expect(container.firstChild).toMatchSnapshot();
});
});
```
## Migration Checklist
When migrating/creating a component:
- [ ] Check if Shadcn has equivalent
- [ ] Analyze Figma design
- [ ] Install Shadcn if available: `npx shadcn@latest add [component]`
- [ ] Move to correct atomic level
- [ ] Adapt imports to use `@/libs`
- [ ] Implement all Figma variants
- [ ] Use CVA for variant management
- [ ] Create tests (unit + snapshot)
- [ ] Update exports in `atoms/index.ts`
- [ ] Test build: `npm run build`
- [ ] Test in browser
## Common Pitfalls
### Ref Forwarding
```tsx
// ✅ Forward refs correctly
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => <button ref={ref} {...props} />
);
// ❌ Missing forwardRef
const Button = (props: ButtonProps) => <button {...props} />;
```
### Test Queries
```tsx
// ✅ Use data-testid for reliable queries
<Button data-testid="submit-btn">Submit</Button>
const button = screen.getByTestId('submit-btn');
// ⚠️ Role queries may not work for all components
const button = screen.getByRole('button'); // May be fragile
```
### Export Updates
```tsx
// ✅ Remember to update atoms/index.ts
export * from './NewComponent';
// ❌ Forget to export = import errors
```
## Quick Checklist
When creating/modifying components:
- [ ] Shadcn checked first?
- [ ] File structure follows pattern?
- [ ] Using `@/` import aliases?
- [ ] Design tokens (not hardcoded colors)?
- [ ] Figma sizing/spacing matched?
- [ ] Tests created (unit + snapshot)?
- [ ] Exported in index.ts?
- [ ] Build passes?
---
**Reference**: `.cursor/docs/component-guidelines.md`, `.cursor/docs/figma-context.md`