- Eliminated redundant components, pages, and layouts related to authentication (`login`, `register`, `password-reset`, etc.), user settings, admin, and demos. - Simplified the frontend structure by removing unused dynamic imports, forms, and test code. - Refactored configurations and metadata imports to exclude references to removed features. - Streamlined the project for future development and improved maintainability by discarding legacy and unused code.
481 lines
19 KiB
TypeScript
481 lines
19 KiB
TypeScript
/**
|
|
* Layout Patterns Demo
|
|
* Interactive demonstrations of essential layout patterns
|
|
* Access: /dev/layouts
|
|
*/
|
|
|
|
import type { Metadata } from 'next';
|
|
import { Link } from '@/lib/i18n/routing';
|
|
import { Grid3x3 } 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';
|
|
import { Example, ExampleSection } from '@/components/dev/Example';
|
|
import { BeforeAfter } from '@/components/dev/BeforeAfter';
|
|
|
|
export const metadata: Metadata = {
|
|
title: 'Layout Patterns | Dev',
|
|
description: 'Essential layout patterns with before/after examples',
|
|
};
|
|
|
|
export default function LayoutsPage() {
|
|
return (
|
|
<div className="bg-background">
|
|
{/* Breadcrumbs */}
|
|
<DevBreadcrumbs items={[{ label: 'Layouts' }]} />
|
|
|
|
{/* Content */}
|
|
<main className="container mx-auto px-4 py-12">
|
|
<div className="space-y-12">
|
|
{/* Introduction */}
|
|
<div className="max-w-3xl space-y-4">
|
|
<p className="text-muted-foreground">
|
|
These 5 essential layout patterns cover 80% of interface needs. Each pattern includes
|
|
live examples, before/after comparisons, and copy-paste code.
|
|
</p>
|
|
<div className="flex flex-wrap gap-2">
|
|
<Badge variant="outline">Grid vs Flex</Badge>
|
|
<Badge variant="outline">Responsive</Badge>
|
|
<Badge variant="outline">Mobile-first</Badge>
|
|
<Badge variant="outline">Best practices</Badge>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 1. Page Container */}
|
|
<ExampleSection
|
|
id="page-container"
|
|
title="1. Page Container"
|
|
description="Standard page layout with constrained width"
|
|
>
|
|
<Example
|
|
title="Page Container Pattern"
|
|
description="Responsive container with padding and max-width"
|
|
code={`<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>Content Card</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p>Your main content goes here.</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>`}
|
|
>
|
|
<div className="rounded-lg border bg-muted/30 p-2">
|
|
<div className="container mx-auto px-4 py-8 bg-background rounded">
|
|
<div className="max-w-4xl mx-auto space-y-6">
|
|
<h2 className="text-2xl font-bold">Page Title</h2>
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Content Card</CardTitle>
|
|
<CardDescription>Constrained to max-w-4xl for readability</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-sm text-muted-foreground">
|
|
Your main content goes here. The max-w-4xl constraint ensures comfortable
|
|
reading width.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Example>
|
|
|
|
<BeforeAfter
|
|
title="Common Mistake: No Width Constraint"
|
|
description="Content should not span full viewport width"
|
|
before={{
|
|
caption: 'No max-width, hard to read on wide screens',
|
|
content: (
|
|
<div className="w-full space-y-4 bg-background p-4 rounded">
|
|
<h3 className="font-semibold">Full Width Content</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
This text spans the entire width, making it hard to read on large screens.
|
|
Lines become too long.
|
|
</p>
|
|
</div>
|
|
),
|
|
}}
|
|
after={{
|
|
caption: 'Constrained with max-w for better readability',
|
|
content: (
|
|
<div className="max-w-2xl mx-auto space-y-4 bg-background p-4 rounded">
|
|
<h3 className="font-semibold">Constrained Content</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
This text has a max-width, creating comfortable line lengths for reading.
|
|
</p>
|
|
</div>
|
|
),
|
|
}}
|
|
/>
|
|
</ExampleSection>
|
|
|
|
{/* 2. Dashboard Grid */}
|
|
<ExampleSection
|
|
id="dashboard-grid"
|
|
title="2. Dashboard Grid"
|
|
description="Responsive card grid for metrics and data"
|
|
>
|
|
<Example
|
|
title="Responsive Grid Pattern"
|
|
description="1 → 2 → 3 columns progression with grid"
|
|
code={`<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{items.map(item => (
|
|
<Card key={item.id}>
|
|
<CardHeader>
|
|
<CardTitle>{item.title}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>{item.content}</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>`}
|
|
>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
|
<Card key={i}>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-sm font-medium">Metric {i}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{(Math.random() * 1000).toFixed(0)}</div>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
+{(Math.random() * 20).toFixed(1)}% from last month
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
</Example>
|
|
|
|
<BeforeAfter
|
|
title="Grid vs Flex for Equal Columns"
|
|
description="Use Grid for equal-width columns, not Flex"
|
|
before={{
|
|
caption: 'flex with flex-1 - uneven wrapping',
|
|
content: (
|
|
<div className="flex flex-wrap gap-4">
|
|
<div className="flex-1 min-w-[200px] rounded border bg-background p-4">
|
|
<div className="text-xs">flex-1</div>
|
|
</div>
|
|
<div className="flex-1 min-w-[200px] rounded border bg-background p-4">
|
|
<div className="text-xs">flex-1</div>
|
|
</div>
|
|
<div className="flex-1 min-w-[200px] rounded border bg-background p-4">
|
|
<div className="text-xs">flex-1 (odd one out)</div>
|
|
</div>
|
|
</div>
|
|
),
|
|
}}
|
|
after={{
|
|
caption: 'grid with grid-cols - consistent sizing',
|
|
content: (
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
|
<div className="rounded border bg-background p-4">
|
|
<div className="text-xs">grid-cols</div>
|
|
</div>
|
|
<div className="rounded border bg-background p-4">
|
|
<div className="text-xs">grid-cols</div>
|
|
</div>
|
|
<div className="rounded border bg-background p-4">
|
|
<div className="text-xs">grid-cols (perfect)</div>
|
|
</div>
|
|
</div>
|
|
),
|
|
}}
|
|
/>
|
|
</ExampleSection>
|
|
|
|
{/* 3. Form Layout */}
|
|
<ExampleSection
|
|
id="form-layout"
|
|
title="3. Form Layout"
|
|
description="Centered form with appropriate max-width"
|
|
>
|
|
<Example
|
|
title="Centered Form Pattern"
|
|
description="Form constrained to max-w-md"
|
|
code={`<div className="container mx-auto px-4 py-8">
|
|
<Card className="max-w-md mx-auto">
|
|
<CardHeader>
|
|
<CardTitle>Login</CardTitle>
|
|
<CardDescription>Enter your credentials</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<form className="space-y-4">
|
|
{/* Form fields */}
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
</div>`}
|
|
>
|
|
<div className="container mx-auto px-4 py-8">
|
|
<Card className="max-w-md mx-auto">
|
|
<CardHeader>
|
|
<CardTitle>Login</CardTitle>
|
|
<CardDescription>Enter your credentials to continue</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<form className="space-y-4">
|
|
<div className="space-y-2">
|
|
<div className="text-sm font-medium">Email</div>
|
|
<div className="h-10 rounded-md border bg-background"></div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<div className="text-sm font-medium">Password</div>
|
|
<div className="h-10 rounded-md border bg-background"></div>
|
|
</div>
|
|
<Button className="w-full">Sign In</Button>
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</Example>
|
|
</ExampleSection>
|
|
|
|
{/* 4. Sidebar Layout */}
|
|
<ExampleSection
|
|
id="sidebar-layout"
|
|
title="4. Sidebar Layout"
|
|
description="Two-column layout with fixed sidebar"
|
|
>
|
|
<Example
|
|
title="Sidebar + Main Content"
|
|
description="Grid with fixed sidebar width"
|
|
code={`<div className="grid lg:grid-cols-[240px_1fr] gap-6">
|
|
{/* Sidebar */}
|
|
<aside className="space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Navigation</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>{/* Nav items */}</CardContent>
|
|
</Card>
|
|
</aside>
|
|
|
|
{/* Main Content */}
|
|
<main className="space-y-4">
|
|
<Card>{/* Content */}</Card>
|
|
</main>
|
|
</div>`}
|
|
>
|
|
<div className="grid lg:grid-cols-[240px_1fr] gap-6">
|
|
{/* Sidebar */}
|
|
<aside className="space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">Navigation</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-2">
|
|
{['Dashboard', 'Settings', 'Profile'].map((item) => (
|
|
<div key={item} className="rounded-md bg-muted px-3 py-2 text-sm">
|
|
{item}
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
</aside>
|
|
|
|
{/* Main Content */}
|
|
<main className="space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Main Content</CardTitle>
|
|
<CardDescription>Fixed 240px sidebar, fluid main area</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-sm text-muted-foreground">
|
|
The sidebar remains 240px wide while the main content area flexes to fill
|
|
remaining space.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</main>
|
|
</div>
|
|
</Example>
|
|
</ExampleSection>
|
|
|
|
{/* 5. Centered Content */}
|
|
<ExampleSection
|
|
id="centered-content"
|
|
title="5. Centered Content"
|
|
description="Vertically and horizontally centered layouts"
|
|
>
|
|
<Example
|
|
title="Center with Flexbox"
|
|
description="Full-height centered content"
|
|
code={`<div className="flex min-h-screen items-center justify-center">
|
|
<Card className="max-w-md w-full">
|
|
<CardHeader>
|
|
<CardTitle>Centered Card</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p>Content perfectly centered on screen.</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>`}
|
|
>
|
|
<div className="flex min-h-[400px] items-center justify-center rounded-lg border bg-muted/30 p-4">
|
|
<Card className="max-w-sm w-full">
|
|
<CardHeader>
|
|
<CardTitle>Centered Card</CardTitle>
|
|
<CardDescription>Centered vertically and horizontally</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-sm text-muted-foreground">
|
|
Perfect for login screens, error pages, and loading states.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</Example>
|
|
</ExampleSection>
|
|
|
|
{/* Decision Tree */}
|
|
<ExampleSection
|
|
id="decision-tree"
|
|
title="Decision Tree: Grid vs Flex"
|
|
description="When to use each layout method"
|
|
>
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center gap-2">
|
|
<Grid3x3 className="h-5 w-5 text-primary" />
|
|
<CardTitle>Grid vs Flex Quick Guide</CardTitle>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<div className="space-y-4">
|
|
<div className="space-y-2">
|
|
<div className="flex items-center gap-2">
|
|
<Badge variant="default">Use Grid</Badge>
|
|
<span className="text-sm font-medium">When you need...</span>
|
|
</div>
|
|
<ul className="ml-6 space-y-1 text-sm text-muted-foreground list-disc">
|
|
<li>Equal-width columns (dashboard cards)</li>
|
|
<li>2D layout (rows AND columns)</li>
|
|
<li>Consistent grid structure</li>
|
|
<li>Auto-fill/auto-fit responsive grids</li>
|
|
</ul>
|
|
<div className="rounded-lg border bg-muted/30 p-3 font-mono text-xs">
|
|
grid grid-cols-3 gap-6
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<div className="flex items-center gap-2">
|
|
<Badge variant="secondary">Use Flex</Badge>
|
|
<span className="text-sm font-medium">When you need...</span>
|
|
</div>
|
|
<ul className="ml-6 space-y-1 text-sm text-muted-foreground list-disc">
|
|
<li>Variable-width items (buttons, tags)</li>
|
|
<li>1D layout (row OR column)</li>
|
|
<li>Center alignment</li>
|
|
<li>Space-between/around distribution</li>
|
|
</ul>
|
|
<div className="rounded-lg border bg-muted/30 p-3 font-mono text-xs">
|
|
flex gap-4 items-center
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</ExampleSection>
|
|
|
|
{/* Responsive Patterns */}
|
|
<ExampleSection
|
|
id="responsive"
|
|
title="Responsive Patterns"
|
|
description="Mobile-first breakpoint strategies"
|
|
>
|
|
<div className="grid gap-6 lg:grid-cols-2">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">1 → 2 → 3 Progression</CardTitle>
|
|
<CardDescription>Most common pattern</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<code className="text-xs">grid-cols-1 md:grid-cols-2 lg:grid-cols-3</code>
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
Mobile: 1 column
|
|
<br />
|
|
Tablet: 2 columns
|
|
<br />
|
|
Desktop: 3 columns
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">1 → 2 → 4 Progression</CardTitle>
|
|
<CardDescription>For smaller cards</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<code className="text-xs">grid-cols-1 md:grid-cols-2 lg:grid-cols-4</code>
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
Mobile: 1 column
|
|
<br />
|
|
Tablet: 2 columns
|
|
<br />
|
|
Desktop: 4 columns
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">Stack → Row</CardTitle>
|
|
<CardDescription>Form buttons, toolbars</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<code className="text-xs">flex flex-col sm:flex-row</code>
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
Mobile: Stacked vertically
|
|
<br />
|
|
Tablet+: Horizontal row
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">Hide Sidebar</CardTitle>
|
|
<CardDescription>Mobile navigation</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<code className="text-xs">hidden lg:block</code>
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
Mobile: Hidden (use menu)
|
|
<br />
|
|
Desktop: Visible sidebar
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</ExampleSection>
|
|
</div>
|
|
</main>
|
|
|
|
{/* Footer */}
|
|
<footer className="mt-16 border-t py-6">
|
|
<div className="container mx-auto px-4 text-center">
|
|
<p className="text-sm text-muted-foreground">
|
|
Learn more:{' '}
|
|
<Link
|
|
href="/dev/docs/design-system/03-layouts"
|
|
className="font-medium hover:text-foreground"
|
|
>
|
|
Layout Documentation
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
);
|
|
}
|