/* istanbul ignore file - Design system demo page covered by e2e tests */ /** * Spacing Patterns Demo * Interactive demonstrations of spacing philosophy and best practices * Access: /dev/spacing */ import type { Metadata } from 'next'; import dynamic from 'next/dynamic'; import { Link } from '@/lib/i18n/routing'; import { Ruler } from 'lucide-react'; import { DevBreadcrumbs } from '@/components/dev/DevBreadcrumbs'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; // Code-split heavy dev components const Example = dynamic( () => import('@/components/dev/Example').then((mod) => ({ default: mod.Example })), { loading: () =>
} ); const ExampleSection = dynamic( () => import('@/components/dev/Example').then((mod) => ({ default: mod.ExampleSection })), { loading: () =>
} ); const BeforeAfter = dynamic( () => import('@/components/dev/BeforeAfter').then((mod) => ({ default: mod.BeforeAfter })), { loading: () =>
} ); export const metadata: Metadata = { title: 'Spacing Patterns | Dev', description: 'Parent-controlled spacing philosophy and visual demonstrations', }; export default function SpacingPage() { return (
{/* Breadcrumbs */} {/* Content */}
{/* Introduction */}

The Golden Rule: Parents control spacing, not children. Use gap, space-y, and space-x utilities on the parent container. Avoid margins on children except for exceptions.

gap space-y space-x avoid margin
{/* Spacing Scale */} Common Spacing Values Use consistent spacing values from the scale
{[ { class: '2', px: '8px', rem: '0.5rem', use: 'Tight (label → input)' }, { class: '4', px: '16px', rem: '1rem', use: 'Standard (form fields)' }, { class: '6', px: '24px', rem: '1.5rem', use: 'Section spacing' }, { class: '8', px: '32px', rem: '2rem', use: 'Large gaps' }, { class: '12', px: '48px', rem: '3rem', use: 'Section dividers' }, ].map((item) => (
gap-{item.class} {item.px} {item.rem} {item.use}
))}
{/* Gap for Flex/Grid */}
{/* Vertical */}
Item 1
Item 2
{/* Grid */}
{/* Cards */}
`} >
{/* Horizontal */}

Horizontal (gap-4)

{/* Vertical */}

Vertical (gap-4)

Item 1
Item 2
Item 3
{/* Grid */}

Grid (gap-6)

{[1, 2, 3].map((i) => (
Card {i}
))}
{/* Space-y for Stacks */}
First item (no margin)
Second item (mt-4)
Third item (mt-4)
{/* Form example */}
`} >
{/* Visual demo */}

Visual Demo (space-y-4)

First item (no margin)
Second item (mt-4)
Third item (mt-4)
{/* Form example */}

Form Example (space-y-4)

Email
Password
{/* Anti-pattern: Child Margins */}
Child 1
no margin
Child 2
mt-4
Child 3
mt-4
), }} after={{ caption: 'Parent controls spacing with space-y-4', content: (
Child 1
parent uses space-y-4
Child 2
clean, no margin
Child 3
clean, no margin
), }} />
), }} after={{ caption: 'Gap on parent - clean and flexible', content: (
), }} /> {/* Decision Tree */}
Spacing Decision Tree
{/* Gap */}
Use gap When...
  • Parent is flex or grid
  • All children need equal spacing
  • Responsive spacing (gap-4 md:gap-6)
flex gap-4
grid grid-cols-3 gap-6
{/* Space-y */}
Use space-y When...
  • Vertical stack without flex/grid
  • Form fields
  • Content sections
space-y-4
space-y-6
{/* Margin */}
Use margin Only when...
  • Exception case (one child needs different spacing)
  • Negative margin for overlap effects
  • Cannot modify parent (external component)
mt-8 {/* exception */}
-mt-4 {/* overlap */}
{/* Padding */}
Use padding When...
  • Internal spacing within a component
  • Card/container padding
  • Button padding
p-4 {/* all sides */}
px-4 py-2 {/* horizontal & vertical */}
{/* Common Patterns */}
Form Fields Parent: space-y-4, Field: space-y-2 {`
`}
Email
Password
Button Group Use flex gap-4 {`
`}
Card Grid Use grid with gap-6 {`
...
`}
Content Stack Use space-y-6 for sections {`
...
...
`}
{/* Footer */} ); }