- Consolidated multi-line arguments into single lines where appropriate in `useAuth`. - Improved spacing and readability in data processing across components (`ProfileSettingsForm`, `PasswordChangeForm`, `SessionCard`). - Applied consistent table and markdown formatting in design system docs (e.g., `README.md`, `08-ai-guidelines.md`, `00-quick-start.md`). - Updated code snippets to ensure adherence to Prettier rules and streamlined JSX structures.
17 KiB
Spacing Philosophy
Master the "parent controls children" spacing strategy that eliminates 90% of layout inconsistencies. Learn when to use margin, padding, or gap—and why children should never add their own margins.
Table of Contents
- The Golden Rules
- Parent Controls Children Strategy
- Decision Tree: Margin vs Padding vs Gap
- Common Patterns
- Before/After Examples
- Anti-Patterns to Avoid
- Quick Reference
The Golden Rules
These 5 rules eliminate 90% of spacing inconsistencies:
Rule 1: Parent Controls Children
Children don't add their own margins. The parent controls spacing between siblings.
// ✅ CORRECT - Parent controls spacing
<div className="space-y-4">
<Card>Item 1</Card>
<Card>Item 2</Card>
<Card>Item 3</Card>
</div>
// ❌ WRONG - Children add margins
<div>
<Card className="mb-4">Item 1</Card>
<Card className="mb-4">Item 2</Card>
<Card>Item 3</Card> {/* Inconsistent: last one has no margin */}
</div>
Why this matters:
- Eliminates "last child" edge cases
- Makes components reusable (they work in any context)
- Changes propagate from one place (parent)
- Prevents margin collapsing bugs
Rule 2: Use Gap for Siblings
For flex and grid layouts, use gap-* to space siblings.
// ✅ CORRECT - Gap for flex/grid
<div className="flex gap-4">
<Button>Cancel</Button>
<Button>Save</Button>
</div>
<div className="grid grid-cols-3 gap-6">
<Card>1</Card>
<Card>2</Card>
<Card>3</Card>
</div>
// ❌ WRONG - Children with margins
<div className="flex">
<Button className="mr-4">Cancel</Button>
<Button>Save</Button>
</div>
Rule 3: Use Padding for Internal Spacing
Padding is for spacing inside a component, between the border and content.
// ✅ CORRECT - Padding for internal spacing
<Card className="p-6">
<CardTitle>Title</CardTitle>
<CardContent>Content</CardContent>
</Card>
// ❌ WRONG - Using margin for internal spacing
<Card>
<CardTitle className="m-6">Title</CardTitle>
</Card>
Rule 4: Use space-y for Vertical Stacks
For vertical stacks (not flex/grid), use space-y-* utility.
// ✅ CORRECT - space-y for stacks
<form className="space-y-4">
<Input />
<Input />
<Button />
</form>
// ❌ WRONG - Children with margins
<form>
<Input className="mb-4" />
<Input className="mb-4" />
<Button />
</form>
How space-y works:
/* space-y-4 applies margin-top to all children except first */
.space-y-4 > * + * {
margin-top: 1rem; /* 16px */
}
Rule 5: Margins Only for Exceptions
Use margin only when a specific child needs different spacing from its siblings.
// ✅ CORRECT - Margin for exception
<div className="space-y-4">
<Card>Normal spacing</Card>
<Card>Normal spacing</Card>
<Card className="mt-8">Extra spacing above this one</Card>
<Card>Normal spacing</Card>
</div>
// Use case: Visually group related items
<div className="space-y-4">
<h2>Section 1</h2>
<p>Content</p>
<h2 className="mt-12">Section 2</h2> {/* Extra margin to separate sections */}
<p>Content</p>
</div>
Parent Controls Children Strategy
The Problem with Child-Controlled Spacing
When children control their own margins:
// ❌ ANTI-PATTERN
function TodoItem({ className }: { className?: string }) {
return <div className={cn('mb-4', className)}>Todo</div>;
}
// Usage
<div>
<TodoItem /> {/* Has mb-4 */}
<TodoItem /> {/* Has mb-4 */}
<TodoItem /> {/* Has mb-4 - unwanted margin at bottom! */}
</div>;
Problems:
- ❌ Last item has unwanted margin
- ❌ Can't change spacing without modifying component
- ❌ Margin collapsing creates unpredictable spacing
- ❌ Component not reusable in different contexts
The Solution: Parent-Controlled Spacing
// ✅ CORRECT PATTERN
function TodoItem({ className }: { className?: string }) {
return <div className={className}>Todo</div>; // No margin!
}
// Parent controls spacing
<div className="space-y-4">
<TodoItem />
<TodoItem />
<TodoItem /> {/* No unwanted margin */}
</div>
// Different context, different spacing
<div className="space-y-2">
<TodoItem />
<TodoItem />
</div>
// Another context, flex layout
<div className="flex gap-6">
<TodoItem />
<TodoItem />
</div>
Benefits:
- ✅ No edge cases (last child, first child, only child)
- ✅ Spacing controlled in one place
- ✅ Component works in any layout context
- ✅ No margin collapsing surprises
- ✅ Easier to maintain and modify
Decision Tree: Margin vs Padding vs Gap
Use this flowchart to choose the right spacing method:
┌─────────────────────────────────────────────┐
│ What are you spacing? │
└─────────────┬───────────────────────────────┘
│
┌──────┴──────┐
│ │
▼ ▼
Siblings? Inside a component?
│ │
│ └──> USE PADDING
│ className="p-4"
│
├──> Is parent using flex or grid?
│ │
│ ├─YES──> USE GAP
│ │ className="flex gap-4"
│ │ className="grid gap-6"
│ │
│ └─NO───> USE SPACE-Y or SPACE-X
│ className="space-y-4"
│ className="space-x-2"
│
└──> Exception case?
(One child needs different spacing)
│
└──> USE MARGIN
className="mt-8"
Common Patterns
Pattern 1: Form Fields (Vertical Stack)
// ✅ CORRECT
<form className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" />
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input id="password" />
</div>
<Button type="submit">Submit</Button>
</form>
Spacing breakdown:
space-y-4on form: 16px between field groupsspace-y-2on field group: 8px between label and input- No margins on children
Pattern 2: Button Group (Horizontal Flex)
// ✅ CORRECT
<div className="flex gap-4">
<Button variant="outline">Cancel</Button>
<Button>Save</Button>
</div>
// Responsive: stack on mobile, row on desktop
<div className="flex flex-col sm:flex-row gap-4">
<Button variant="outline">Cancel</Button>
<Button>Save</Button>
</div>
Why gap over space-x:
- Works with
flex-wrap - Works with
flex-col(changes direction) - Consistent spacing in all directions
Pattern 3: Card Grid
// ✅ CORRECT
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card>Item 1</Card>
<Card>Item 2</Card>
<Card>Item 3</Card>
</div>
Why gap:
- Consistent spacing between rows and columns
- Works with responsive grid changes
- No edge cases (first row, last column, etc.)
Pattern 4: Card Internal Spacing
// ✅ CORRECT
<Card className="p-6">
<CardHeader>
<CardTitle>Title</CardTitle>
<CardDescription>Description</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</CardContent>
<CardFooter className="pt-4">
<Button>Action</Button>
</CardFooter>
</Card>
Spacing breakdown:
p-6on Card: 24px internal paddingspace-y-4on CardContent: 16px between paragraphspt-4on CardFooter: Additional top padding for visual separation
Pattern 5: Page Layout
// ✅ CORRECT
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-6">
<h1 className="text-3xl font-bold">Page Title</h1>
<Card>
<CardHeader>
<CardTitle>Section 1</CardTitle>
</CardHeader>
<CardContent>Content</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Section 2</CardTitle>
</CardHeader>
<CardContent>Content</CardContent>
</Card>
</div>
</div>
Spacing breakdown:
px-4: Horizontal padding (prevents edge touching)py-8: Vertical padding (top and bottom spacing)space-y-6: 24px between sections- No margins on children
Before/After Examples
Example 1: Button Group
❌ Before (Child-Controlled)
function ActionButton({ children, className }: Props) {
return <Button className={cn('mr-4', className)}>{children}</Button>;
}
// Usage
<div className="flex">
<ActionButton>Cancel</ActionButton>
<ActionButton>Save</ActionButton>
<ActionButton>Delete</ActionButton> {/* Unwanted mr-4 */}
</div>;
Problems:
- Last button has unwanted margin
- Can't change spacing without modifying component
- Hard to use in vertical layout
✅ After (Parent-Controlled)
function ActionButton({ children, className }: Props) {
return <Button className={className}>{children}</Button>;
}
// Usage
<div className="flex gap-4">
<ActionButton>Cancel</ActionButton>
<ActionButton>Save</ActionButton>
<ActionButton>Delete</ActionButton>
</div>
// Different context: vertical
<div className="flex flex-col gap-2">
<ActionButton>Cancel</ActionButton>
<ActionButton>Save</ActionButton>
</div>
Benefits:
- No edge cases
- Reusable in any layout
- Easy to change spacing
Example 2: List Items
❌ Before (Child-Controlled)
function ListItem({ title, description }: Props) {
return (
<div className="mb-6 p-4 border rounded">
<h3 className="mb-2">{title}</h3>
<p>{description}</p>
</div>
);
}
<div>
<ListItem title="Item 1" description="..." />
<ListItem title="Item 2" description="..." />
<ListItem title="Item 3" description="..." /> {/* Unwanted mb-6 */}
</div>;
Problems:
- Last item has unwanted bottom margin
- Can't change list spacing without modifying component
- Internal
mb-2hard to override
✅ After (Parent-Controlled)
function ListItem({ title, description }: Props) {
return (
<div className="p-4 border rounded">
<div className="space-y-2">
<h3 className="font-semibold">{title}</h3>
<p>{description}</p>
</div>
</div>
);
}
<div className="space-y-6">
<ListItem title="Item 1" description="..." />
<ListItem title="Item 2" description="..." />
<ListItem title="Item 3" description="..." />
</div>
// Different context: compact spacing
<div className="space-y-2">
<ListItem title="Item 1" description="..." />
<ListItem title="Item 2" description="..." />
</div>
Benefits:
- No unwanted margins
- Internal spacing controlled by
space-y-2 - Reusable with different spacings
Example 3: Form Fields
❌ Before (Mixed Strategy)
<form>
<div className="mb-4">
<Label htmlFor="name">Name</Label>
<Input id="name" className="mt-2" />
</div>
<div className="mb-4">
<Label htmlFor="email">Email</Label>
<Input id="email" className="mt-2" />
</div>
<Button type="submit" className="mt-6">
Submit
</Button>
</form>
Problems:
- Spacing scattered across children
- Hard to change consistently
- Have to remember
mt-6for button
✅ After (Parent-Controlled)
<form className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Name</Label>
<Input id="name" />
</div>
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" />
</div>
<Button type="submit" className="mt-2">
Submit
</Button>
</form>
Benefits:
- Spacing controlled in 2 places: form (
space-y-4) and field groups (space-y-2) - Easy to change all field spacing at once
- Consistent and predictable
Anti-Patterns to Avoid
Anti-Pattern 1: Last Child Special Case
// ❌ WRONG
{
items.map((item, index) => (
<Card key={item.id} className={index < items.length - 1 ? 'mb-4' : ''}>
{item.name}
</Card>
));
}
// ✅ CORRECT
<div className="space-y-4">
{items.map((item) => (
<Card key={item.id}>{item.name}</Card>
))}
</div>;
Anti-Pattern 2: Negative Margins to Fix Spacing
// ❌ WRONG - Using negative margin to fix unwanted spacing
<div className="-mt-4"> {/* Canceling out previous margin */}
<Card>Content</Card>
</div>
// ✅ CORRECT - Parent controls spacing
<div className="space-y-4">
<Card>Content</Card>
</div>
Why negative margins are bad:
- Indicates broken spacing strategy
- Hard to maintain
- Creates coupling between components
Anti-Pattern 3: Mixing Gap and Child Margins
// ❌ WRONG - Gap + child margins = unpredictable spacing
<div className="flex gap-4">
<Button className="mr-2">Cancel</Button> {/* gap + mr-2 = 24px */}
<Button>Save</Button>
</div>
// ✅ CORRECT - Only gap
<div className="flex gap-4">
<Button>Cancel</Button>
<Button>Save</Button>
</div>
// ✅ CORRECT - Exception case
<div className="flex gap-4">
<Button>Cancel</Button>
<Button className="mr-8">Save</Button> {/* Intentional extra space */}
<Button>Delete</Button>
</div>
Anti-Pattern 4: Using Margins for Layout
// ❌ WRONG - Using margins to create layout
<div>
<div className="ml-64"> {/* Pushing content for sidebar */}
Content
</div>
</div>
// ✅ CORRECT - Use proper layout (flex/grid)
<div className="flex gap-6">
<aside className="w-64">Sidebar</aside>
<main className="flex-1">Content</main>
</div>
Quick Reference
Spacing Method Cheat Sheet
| Use Case | Method | Example |
|---|---|---|
| Flex siblings | gap-* |
flex gap-4 |
| Grid siblings | gap-* |
grid gap-6 |
| Vertical stack | space-y-* |
space-y-4 |
| Horizontal stack | space-x-* |
space-x-2 |
| Inside component | p-* |
p-6 |
| One child exception | m-* |
mt-8 |
Common Spacing Values
| Class | Pixels | Usage |
|---|---|---|
gap-2 or space-y-2 |
8px | Tight (label + input) |
gap-4 or space-y-4 |
16px | Standard (form fields) |
gap-6 or space-y-6 |
24px | Sections (cards) |
gap-8 or space-y-8 |
32px | Large gaps |
p-4 |
16px | Standard padding |
p-6 |
24px | Card padding |
px-4 py-8 |
16px / 32px | Page padding |
Decision Flowchart (Simplified)
Need spacing?
│
├─ Between siblings?
│ ├─ Flex/Grid parent? → gap-*
│ └─ Regular parent? → space-y-* or space-x-*
│
├─ Inside component? → p-*
│
└─ Exception case? → m-* (sparingly)
Best Practices Summary
Do ✅
- Use parent-controlled spacing (
gap,space-y,space-x) - Use
gap-*for flex and grid layouts - Use
space-y-*for vertical stacks (forms, content) - Use
p-*for internal spacing (padding inside components) - Use margin only for exceptions (mt-8 to separate sections)
- Let components be context-agnostic (no built-in margins)
Don't ❌
- ❌ Add margins to reusable components
- ❌ Use last-child selectors or conditional margins
- ❌ Mix gap with child margins
- ❌ Use negative margins to fix spacing
- ❌ Use margins for layout (use flex/grid)
- ❌ Hard-code spacing in child components
Spacing Checklist
Before implementing spacing, verify:
- Parent controls children? Using gap or space-y/x?
- No child margins? Components don't have mb-_ or mr-_?
- Consistent method? Not mixing gap + child margins?
- Reusable components? Work in different contexts?
- No edge cases? No last-child or first-child special handling?
- Semantic spacing? Using design system scale (4, 8, 12, 16...)?
Next Steps
- Practice: Refactor existing components to use parent-controlled spacing
- Explore: Interactive spacing examples
- Reference: Quick Reference Tables
- Layout Patterns: Layouts Guide
Related Documentation:
- Layouts - When to use Grid vs Flex
- Foundations - Spacing scale tokens
- Component Creation - Building reusable components
- Quick Start - Essential patterns
Last Updated: November 2, 2025