Design Engineering
Design & Architecture

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

PatternUsed forKey dimensionsLayout component
Landing / MarketingHomepages, product pages, campaign sites1200–1280px content max-width, full-width sectionsLandingLayout
DashboardAdmin panels, SaaS, internal tools240–280px sidebar, 56px header, fluid mainDashboardLayout
AuthLogin, signup, password reset, MFA384–448px centred card, full viewport heightAuthLayout
DocumentationDocs, help centres, knowledge bases260px sidebar, 200px TOC, max content ~720pxDocsLayout (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:

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.

components/layout/LandingLayout.tsx
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">
            &copy; {new Date().getFullYear()} Company. All rights reserved.
          </p>
        </div>
      </footer>
    </div>
  )
}

Hero section

app/page.tsx (marketing home)
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

ElementTailwindValueDeviceResponsive
Content max-widthmax-w-7xl1280pxAllpx-4px-6px-8
Header heighth-1664pxAllSticky
Section paddingpy-2496pxDesktoppy-16 mobile
Hero paddingpy-32128pxDesktoppy-20 mobile
Hero titletext-4xl36pxMobilesm:text-5xl lg:text-6xl
Grid columnsgrid-cols-11Mobilesm: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.

components/layout/DashboardLayout.tsx
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

globals.css
: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

ElementTailwind / CSS varValueNotes
Sidebar width--sidebar-width256pxChange one variable to rescale
Sidebar collapsed--sidebar-collapsed64pxIcon-only width
Header heighth-1456pxMatch across all dashboard pages
Content paddingp-624pxConsistent across all main areas
Sidebar link heightpy-28px v + textLinks 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.

components/layout/AuthLayout.tsx
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

app/auth/login/page.tsx
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

ElementTailwindValueNotes
Card max-widthmax-w-sm384pxWide enough for email + password
Larger cardmax-w-md448pxGood for signup with more fields
Card paddingp-832pxGenerous breathing room on the form
Backgroundbg-muted/40Subtle tintDistinguishes from card surface
Logo marginmb-832pxVertical rhythm above card
Form field gapgap-416pxBetween 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] if 100vh feels too tall
  • Features grid: single column
  • Navigation: hamburger menu below md breakpoint
  • Section padding: py-16 instead of py-24

Layout dimension reference

LayoutDimensionTailwind / VariableRecommended value
Container maxLandingmax-w-7xl1280px
Container maxContent-heavymax-w-5xl1024px
Container maxDocs prosemax-w-3xl768px
Sidebar widthDashboard--sidebar-width256–280px
Sidebar widthDocs nav--sidebar-width260px
TOC widthDocs--toc-width200px
Header heightDashboardh-1456px
Header heightLandingh-1664px
Card widthAuthmax-w-sm384px
Card widthSettings formmax-w-2xl672px
Section paddingLandingpy-2496px
Content paddingDashboardp-624px
Card paddingAuthp-832px

On this page