---
title: Main
description: Primary content area in app-shell layouts
visibility: guest
draft: false
---

# Main

The primary content area inside an [`<x-wirekit::app-shell>`](./app-shell.md). It renders a semantic `<main>` element that claims the remaining vertical space (`flex-1`) and scrolls independently from the header and sidebar (`overflow-y-auto`).

Content you place inside `Main` is laid out with a padding scale matched to the surrounding shell, and optionally constrained to a centered container width.

## Basic Usage

By default, the component renders with padding `lg` (`--space-wk-lg`, 1.5rem). Drop any WireKit components inside.

:::preview{title="Main with default padding"}
<x-wirekit::main>
    <x-wirekit::stack gap="md">
        <x-wirekit::heading level="1">Dashboard</x-wirekit::heading>
        <x-wirekit::text variant="muted">Your content goes here.</x-wirekit::text>
    </x-wirekit::stack>
</x-wirekit::main>
:::

## Padding Scale

The `padding` prop accepts `none`, `sm`, `md`, `lg`, `xl`. Each value maps to a `--space-wk-*` design token so padding scales consistently with the rest of the shell.

:::preview{title="Main with extra-large padding"}
<x-wirekit::main padding="xl">
    <x-wirekit::stack gap="md">
        <x-wirekit::heading level="1">Spacious layout</x-wirekit::heading>
        <x-wirekit::text>padding="xl" uses --space-wk-xl (2.5rem) on all sides.</x-wirekit::text>
    </x-wirekit::stack>
</x-wirekit::main>
:::

:::preview{title="Main with no padding"}
<x-wirekit::main padding="none">
    <x-wirekit::section background="muted" padding="lg">
        <x-wirekit::heading level="2">Edge-to-edge section</x-wirekit::heading>
        <x-wirekit::text>Use padding="none" when the inner sections control their own spacing.</x-wirekit::text>
    </x-wirekit::section>
</x-wirekit::main>
:::

## Container Layout

Set `:container="true"` to center the children inside `max-w-[var(--size-wk-container-2xl)]` (96rem = 1536px). The outer `<main>` still owns the full scroll area; the centered container only affects where the inner content sits horizontally.

::: info
The centering becomes visually apparent only when the viewport is **wider than 96rem (1536px)**. At narrower widths, `max-width` doesn't constrain anything and the inner content fills the available area edge-to-edge — the `mx-auto` is there but has no excess space to center into. The preview below makes this explicit with two visible reference boxes: the dashed outer box is the `<main>` element, the solid inner box is the centering wrapper.
:::

:::preview{title="Contained main — dashed outer = <main>, solid inner = centering wrapper"}
<x-wirekit::main :container="true" style="background: var(--color-wk-bg-muted); outline: 1px dashed var(--color-wk-border); outline-offset: -1px;">
    <div style="background: var(--color-wk-bg-elevated); border: 1px solid var(--color-wk-accent); padding: 1rem; border-radius: var(--radius-wk-md);">
        <x-wirekit::stack gap="md">
            <x-wirekit::heading level="1">Centered content</x-wirekit::heading>
            <x-wirekit::text variant="muted">Width-constrained to the 2xl container token (96rem). Open this page in a viewport wider than 1536px to see the solid inner box visibly centered inside the dashed outer area; at preview-box widths the two coincide because the max-width doesn't engage.</x-wirekit::text>
        </x-wirekit::stack>
    </div>
</x-wirekit::main>
:::

## Default Padding Override

The default padding is read from `config('wirekit.components.main.padding')`. Override it in `config/wirekit.php` to change the default across an entire app:

```php
// config/wirekit.php
return [
    'components' => [
        'main' => [
            'padding' => 'md', // was 'lg'
        ],
    ],
];
```

## Max-Width Cap

The `max` prop caps the content width to the matching `--size-wk-container-*` token tier. Default `"2xl"` (96rem / 1536px) prevents dashboards from stretching edge-to-edge on 1900px+ monitors — the historical pre-2.3.0 default was unbounded, which produced uncomfortably wide reading measures on large screens.

```blade
<x-wirekit::main>{{-- max="2xl" by default --}}
    {{ $slot }}
</x-wirekit::main>

<x-wirekit::main max="lg">{{-- tighter cap for documentation pages --}}
    {{ $slot }}
</x-wirekit::main>

<x-wirekit::main max="none">{{-- restore pre-2.3.0 unbounded behavior --}}
    {{ $slot }}
</x-wirekit::main>
```

Accepts `sm`, `md`, `lg`, `xl`, `2xl` (default), `full`, `none`. The config key `wirekit.components.main.max` overrides the default app-wide.

Because `main` already centers and width-caps its content, **pass page content directly** — don't wrap it in an extra `<x-wirekit::container>` or a `max-w-*` div, which double-caps the width. Reach for `max="none"` only when you want the content to fill the viewport.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `padding` | `string` | `'lg'` (from config) | One of `none`, `sm`, `md`, `lg`, `xl`. Applied to all four sides. Invalid values raise a `WireKit::validateProp` error. |
| `max` | `string\|null` | `'2xl'` (from config) | Caps the content width to `max-w-[--size-wk-container-{max}]`. One of `sm`, `md`, `lg`, `xl`, `2xl`, `full`, `none`. `none` disables the cap entirely. |
| `container` | `bool` | `false` | Center the children inside `max-w-[--size-wk-container-2xl]`. |
| `id` | `string\|null` | `null` | When set, the element receives the given `id` AND `tabindex="-1"` so JS-routing fragment navigation moves keyboard focus INTO the landmark. Pairs with `<x-wirekit::skip-link>` (which targets `#main-content` by default). |
| `scope` | `string\|null` | `null` | Named [personalization scope](/customization) for block-level class overrides. |

## Accessibility

- Rendered as a semantic `<main>` element — announced by assistive tech as the main landmark
- Pair with `<x-wirekit::skip-link>` for a WCAG 2.4.1 (Bypass Blocks) baseline: set `id="main-content"` here and the skip-link's default target lands on this element with keyboard focus
- `overflow-y-auto` preserves keyboard scrolling (`Space`, `PageDown`, arrow keys) and never traps focus
- When the content overflows horizontally, no hidden scroll area is created — keep wide content (tables, code blocks) responsive or in a dedicated `<x-wirekit::scroll-area>` wrapper

## Keyboard Interaction

This component is a layout wrapper. Keyboard interaction is delegated to its children.

## Design Tokens

| Token | Used for |
| --- | --- |
| `--space-wk-sm` | `padding="sm"` |
| `--space-wk-md` | `padding="md"` |
| `--space-wk-lg` | `padding="lg"` (default) |
| `--space-wk-xl` | `padding="xl"` |
| `--size-wk-container-2xl` | Inner max width when `container` is set |

## See Also

- [App Shell](./app-shell.md) — the parent layout component
- [Container](./container.md) — standalone width-constrained wrapper (outside of app-shell)
- [Section](./section.md) — full-width band with background/divider variants, often nested inside `Main`
