Layout Patterns & Scale
Common page layouts with specific dimensions, Tailwind configuration patterns, and how to customise spacing, sidebar widths, and container sizes.
Every project needs the same few layout shells: a landing page, a dashboard, an auth screen. This page provides concrete dimensions, Tailwind code examples, and instructions for customising each pattern. The goal is not to copy-paste — it is to understand where dimensions are defined so you can change them intentionally.
Layout Archetypes
| Pattern | Used for | Key dimensions | Layout component |
|---|---|---|---|
| Landing / Marketing | Homepages, product pages, campaign sites | 1200–1280px content max-width, full-width sections | LandingLayout |
| Dashboard | Admin panels, SaaS, internal tools | 240–280px sidebar, 56px header, fluid main | DashboardLayout |
| Auth | Login, signup, password reset, MFA | 384–448px centred card, full viewport height | AuthLayout |
| Documentation | Docs, help centres, knowledge bases | 260px sidebar, 200px TOC, max content ~720px | DocsLayout (Fumadocs) |
Each layout shell is a React component — define it once, wrap every page in the same shell, change it in one place.
Where dimensions live
When you see a sidebar width of 256px or a header of 56px, that value is defined in one of three places:
1. CSS custom properties (recommended)
In globals.css, define layout dimensions as variables:
:root {
--sidebar-width: 256px;
--sidebar-collapsed: 64px;
--header-height: 56px;
--content-max-width: 720px;
}Use them anywhere with var(--sidebar-width). To change a dashboard sidebar from 256px to 300px, change one line.
2. Tailwind @theme
In globals.css with Tailwind v4:
@theme {
--width-sidebar: 256px;
--height-header: 56px;
}Then use w-(--width-sidebar) in your components.
3. Tailwind arbitrary values (quick, not systematic)
For one-off overrides, w-[300px] or max-w-[720px] inline. Fine for prototyping; push to a variable or theme token before shipping.
Marketing / Landing Page
The classic landing page structure: full-width sections with contained inner content, hero at top, alternating content rows, footer.
export function LandingLayout({ children }: { children: React.ReactNode }) {
return (
<div className="min-h-screen">
{/* Nav */}
<header className="sticky top-0 z-50 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
<span className="text-lg font-semibold">Logo</span>
<nav className="flex items-center gap-6 text-sm">
<a href="#features" className="text-muted-foreground hover:text-foreground">
Features
</a>
<a href="#pricing" className="text-muted-foreground hover:text-foreground">
Pricing
</a>
<a
href="/login"
className="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground"
>
Get Started
</a>
</nav>
</div>
</header>
{children}
{/* Footer */}
<footer className="border-t">
<div className="mx-auto max-w-7xl px-6 py-12 lg:px-8">
<p className="text-sm text-muted-foreground">
© {new Date().getFullYear()} Company. All rights reserved.
</p>
</div>
</footer>
</div>
)
}Hero section
export default function HomePage() {
return (
<LandingLayout>
{/* Hero: full viewport height, centered content */}
<section className="flex min-h-[calc(100vh-4rem)] items-center justify-center px-4">
<div className="mx-auto max-w-3xl text-center">
<h1 className="text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">
Product headline that states the value clearly
</h1>
<p className="mt-6 text-lg leading-relaxed text-muted-foreground sm:text-xl">
Supporting copy that explains what the product does, who it is for,
and why it matters. Keep it under three sentences.
</p>
<div className="mt-10 flex items-center justify-center gap-4">
<a
href="/signup"
className="rounded-lg bg-primary px-6 py-3 text-sm font-medium text-primary-foreground"
>
Start free trial
</a>
<a
href="#features"
className="rounded-lg border px-6 py-3 text-sm font-medium"
>
Learn more
</a>
</div>
</div>
</section>
{/* Features section: alternating content */}
<section id="features" className="py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl text-center">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
Features section heading
</h2>
<p className="mt-4 text-lg text-muted-foreground">
Optional description of what these features accomplish.
</p>
</div>
<div className="mt-16 grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
{features.map((feature) => (
<div
key={feature.title}
className="rounded-xl border bg-card p-6"
>
<h3 className="text-base font-semibold">{feature.title}</h3>
<p className="mt-2 text-sm text-muted-foreground">
{feature.description}
</p>
</div>
))}
</div>
</div>
</section>
</LandingLayout>
)
}Landing page dimensions at a glance
| Element | Tailwind | Value | Device | Responsive |
|---|---|---|---|---|
| Content max-width | max-w-7xl | 1280px | All | px-4 → px-6 → px-8 |
| Header height | h-16 | 64px | All | Sticky |
| Section padding | py-24 | 96px | Desktop | py-16 mobile |
| Hero padding | py-32 | 128px | Desktop | py-20 mobile |
| Hero title | text-4xl | 36px | Mobile | sm:text-5xl lg:text-6xl |
| Grid columns | grid-cols-1 | 1 | Mobile | sm:2 lg:3 |
Dashboard Layout
A persistent sidebar, top header, and scrollable main content area. This is the most common pattern for SaaS products, admin panels, and internal tools.
export function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="grid h-screen" style={{ gridTemplateColumns: 'var(--sidebar-width) 1fr' }}>
{/* Sidebar */}
<aside className="flex flex-col border-r bg-sidebar text-sidebar-foreground">
<div className="flex h-14 items-center gap-2 border-b px-4">
<span className="text-sm font-semibold">App</span>
</div>
<nav className="flex-1 overflow-y-auto p-3">
<div className="flex flex-col gap-1">
<SidebarLink href="/dashboard" active>Dashboard</SidebarLink>
<SidebarLink href="/dashboard/projects">Projects</SidebarLink>
<SidebarLink href="/dashboard/team">Team</SidebarLink>
<SidebarLink href="/dashboard/settings">Settings</SidebarLink>
</div>
</nav>
<div className="border-t p-3">
<UserMenu />
</div>
</aside>
{/* Main area */}
<div className="flex flex-col overflow-hidden">
{/* Header */}
<header className="flex h-14 items-center justify-between border-b px-6">
<h1 className="text-sm font-medium">Dashboard</h1>
<div className="flex items-center gap-3">
<SearchInput />
<NotificationBell />
</div>
</header>
{/* Scrollable content */}
<main className="flex-1 overflow-y-auto p-6">
{children}
</main>
</div>
</div>
)
}
function SidebarLink({
href,
active,
children,
}: {
href: string
active?: boolean
children: React.ReactNode
}) {
return (
<a
href={href}
className={
active
? 'rounded-lg bg-sidebar-primary/10 px-3 py-2 text-sm font-medium text-sidebar-primary'
: 'rounded-lg px-3 py-2 text-sm text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-foreground'
}
>
{children}
</a>
)
}How to tweak sidebar width
:root {
/* Change this value to make the sidebar wider or narrower */
--sidebar-width: 256px; /* default */
/* --sidebar-width: 280px; slightly wider */
/* --sidebar-width: 300px; spacious */
/* --sidebar-width: 220px; compact */
--sidebar-collapsed: 64px;
}Every layout component references var(--sidebar-width). Change it here, every page updates. No hunting through component files.
Collapsible sidebar
// Add to DashboardLayout to support collapsing
const [collapsed, setCollapsed] = useState(false)
<div
className="grid h-screen transition-[grid-template-columns] duration-300"
style={{
gridTemplateColumns: collapsed
? 'var(--sidebar-collapsed) 1fr'
: 'var(--sidebar-width) 1fr',
}}
>
<aside className="...">
<button onClick={() => setCollapsed(!collapsed)}>
{collapsed ? <PanelRightOpen /> : <PanelRightClose />}
</button>
{/* Render icon-only nav when collapsed */}
</aside>
{/* ... */}
</div>Dashboard dimensions
| Element | Tailwind / CSS var | Value | Notes |
|---|---|---|---|
| Sidebar width | --sidebar-width | 256px | Change one variable to rescale |
| Sidebar collapsed | --sidebar-collapsed | 64px | Icon-only width |
| Header height | h-14 | 56px | Match across all dashboard pages |
| Content padding | p-6 | 24px | Consistent across all main areas |
| Sidebar link height | py-2 | 8px v + text | Links at 36px tap target minimum |
Auth Page
A centred card on a subtle background — no navigation chrome, just the form the user needs to complete.
export function AuthLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-muted/40 px-4">
{/* Logo / branding above the card */}
<a href="/" className="mb-8 flex items-center gap-2 text-lg font-semibold">
<svg className="size-6" viewBox="0 0 24 24" fill="currentColor">
<rect width="24" height="24" rx="6" />
</svg>
Brand
</a>
{/* Auth card */}
<div className="w-full max-w-sm rounded-xl border bg-card p-8 shadow-sm">
{children}
</div>
{/* Footer link */}
<p className="mt-6 text-center text-sm text-muted-foreground">
<a href="/help" className="underline underline-offset-4 hover:text-foreground">
Need help?
</a>
</p>
</div>
)
}Auth page with form
export default function LoginPage() {
return (
<AuthLayout>
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-2 text-center">
<h1 className="text-2xl font-bold tracking-tight">Welcome back</h1>
<p className="text-sm text-muted-foreground">
Enter your email to sign in to your account
</p>
</div>
<form className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<label htmlFor="email" className="text-sm font-medium">
Email
</label>
<input
id="email"
type="email"
placeholder="name@example.com"
className="rounded-lg border bg-background px-3 py-2 text-sm"
/>
</div>
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<label htmlFor="password" className="text-sm font-medium">
Password
</label>
<a
href="/auth/reset-password"
className="text-sm underline underline-offset-4 hover:text-foreground"
>
Forgot?
</a>
</div>
<input
id="password"
type="password"
className="rounded-lg border bg-background px-3 py-2 text-sm"
/>
</div>
<button
type="submit"
className="rounded-lg bg-primary px-4 py-2.5 text-sm font-medium text-primary-foreground"
>
Sign in
</button>
</form>
<p className="text-center text-sm text-muted-foreground">
No account?{' '}
<a
href="/auth/signup"
className="underline underline-offset-4 hover:text-foreground"
>
Sign up
</a>
</p>
</div>
</AuthLayout>
)
}Auth page dimensions
| Element | Tailwind | Value | Notes |
|---|---|---|---|
| Card max-width | max-w-sm | 384px | Wide enough for email + password |
| Larger card | max-w-md | 448px | Good for signup with more fields |
| Card padding | p-8 | 32px | Generous breathing room on the form |
| Background | bg-muted/40 | Subtle tint | Distinguishes from card surface |
| Logo margin | mb-8 | 32px | Vertical rhythm above card |
| Form field gap | gap-4 | 16px | Between label + input + button |
Responsive adjustments
Dashboard on mobile
Below the lg breakpoint (1024px), the persistent sidebar hides. Use a sheet/drawer triggered by a hamburger button in the header:
{/* Mobile sidebar trigger */}
<button
className="lg:hidden"
onClick={() => setMobileOpen(true)}
>
<MenuIcon className="size-5" />
</button>
{/* Mobile sidebar drawer */}
{mobileOpen && (
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
<SheetContent side="left" className="w-(--sidebar-width)">
{/* Same sidebar content */}
</SheetContent>
</Sheet>
)}Auth page on mobile
// Card fills more of the viewport on narrow screens
<div className="w-full max-w-sm px-0 sm:px-8">
{/* On mobile, edge-to-edge with screen padding */}
</div>Landing page on mobile
- Hero: reduce to
min-h-[80vh]if100vhfeels too tall - Features grid: single column
- Navigation: hamburger menu below
mdbreakpoint - Section padding:
py-16instead ofpy-24
Layout dimension reference
| Layout | Dimension | Tailwind / Variable | Recommended value |
|---|---|---|---|
| Container max | Landing | max-w-7xl | 1280px |
| Container max | Content-heavy | max-w-5xl | 1024px |
| Container max | Docs prose | max-w-3xl | 768px |
| Sidebar width | Dashboard | --sidebar-width | 256–280px |
| Sidebar width | Docs nav | --sidebar-width | 260px |
| TOC width | Docs | --toc-width | 200px |
| Header height | Dashboard | h-14 | 56px |
| Header height | Landing | h-16 | 64px |
| Card width | Auth | max-w-sm | 384px |
| Card width | Settings form | max-w-2xl | 672px |
| Section padding | Landing | py-24 | 96px |
| Content padding | Dashboard | p-6 | 24px |
| Card padding | Auth | p-8 | 32px |
Related pages
- Information Architecture — Page hierarchy and routing
- Responsive Design — Breakpoint strategy and fluid patterns
- Component Architecture — Layout component hierarchy
- Design System — Token-driven spacing and sizing
Information Architecture
Map the structural skeleton of the application — how pages, content, and navigation connect before any visual design begins.
Responsive Design
Strategy, patterns, and testing for interfaces that adapt across devices — breakpoints, fluid typography, layout patterns, touch, images, and responsive testing.