# Main Container

Level: atom.

## Purpose and use

`wbl-main-container` supplies the shared desktop content frame beside Sidebar.
It owns the fixed 12px outer inset, white rounded surface, strict 20px top and
side insets, and the single vertical scrollport for routed page content. Its
container-owned layout rail uses the named size selected by the page layout and
centers it when the available surface is wider. Place Header Widget, tabs,
filters, tables, or another page composition in its one default slot. Do not
use it as a card inside a page or as a replacement for a table's horizontal
scroll wrapper. A `wbl-table` projected into it follows the shared Table
composition rule below; other projected content remains in the selected rail.

## Anatomy and behavior

The outer frame fills the height supplied by the application shell. Its inner
surface has `--color-background-level-1-base` and `--radius-xl`; padding uses
`--space-300` and `--space-500`. The keyboard-focusable scrollport remains the
one vertical scrolling owner and contains the internal layout rail around the
default projection. The rail fills the available width up to its selected named
maximum, then uses automatic inline margins to center it; every limit therefore
applies inside the surface side padding. The host exposes the selected size as
`data-size` for its own layout styling.

The scrollport does not provide horizontal scrolling. Normal projected content
remains in the rail, so the page itself never becomes a horizontal scroll owner.
For every `wbl-table` projected into this container, the Table's Filter Row, any
rendered Action Bar, and scroll frame/grid remain rail-aligned at every available
width. In contained or paginated mode, any native scrollbar gutter stays inside
the Table viewport; it never changes the viewport's outer size, rail-aligned
edges, or the Main Container rail. This is a Main Container composition rule,
not a second projection slot, screen exception, or Table public API. The Table
keeps its own horizontal viewport and sticky columns.

Each page layout owns one Main Container and chooses its `size`; projected
children never select or override it. The rail is centered in the working area
to Sidebar's right and therefore naturally follows Sidebar resizing. Modal and
drawer layers live outside this atom. `l` and `xl` are layout-level exceptions
that require an explicitly designed page; ordinary current routes use `m`.

`bottomPadding` is `false` by default. When it is `true`, a static 20px spacer
follows the scrollport inside the surface; this is for short, non-scrolling page
content. It is intentionally omitted for the standard routed screens.

## Accessibility and content

With `verticalScroll=true`, the scrollport has `tabindex="0"` so keyboard users
can reach it. With `false`, Main Container removes that tab stop because the
contained Table viewport owns scrolling and focus. The app's
existing `<main>` remains the only landmark; this atom does not add a landmark
or an accessible name. Content projected into the slot must supply its own
headings, controls, and accessible labels.

## Local component contract

This is the owned contract for `component.main-container`. The implementation,
focused test, export, and Storybook entry below are part of D-038; the layout
regression requirements below must be verified together before review.

### Public type values

```ts
export type WblMainContainerSize = 'xs' | 's' | 'm' | 'l' | 'xl';
```

| Value | Maximum inline size | Intended page-layout use |
| --- | ---: | --- |
| `xs` | 840px | Small forms and text-focused pages |
| `s` | 1080px | Large forms, onboarding, and settings |
| `m` | 1440px | Default analytics, tenders, and current routes |
| `l` | 2200px | Explicitly designed registries and heavy tables |
| `xl` | none | Explicitly designed maps, Gantt, or future full-width views |

### Classes and selectors

- `WblMainContainerComponent` — `wbl-main-container`
  (`src/app/design-system/primitives/main-container/main-container.component.ts`).

### Inputs

- `WblMainContainerComponent.bottomPadding = input(false)`
- `WblMainContainerComponent.size = input<WblMainContainerSize>('m')`
- `WblMainContainerComponent.verticalScroll = input(true)`

The layout rail and scrollport container are internal. Consumers select a named
`size`, never an arbitrary CSS length. `verticalScroll=false` disables Main
Container `overflow-y`, removes its `tabindex`, and stretches the internal rail
to 100% of the available height.

### Models

None.

### Outputs

None.

### Slots and projection markers

- One default Angular content-projection slot, wrapped by the container-owned
  layout rail inside the vertical scrollport. The rail is internal anatomy, not
  a configurable slot or a public selector.

### Layout regression requirements

- At an available surface width at or below the selected size's maximum, normal
  projected content uses the available width inside the surface padding.
- Above a selected `xs`, `s`, `m`, or `l` maximum, the rail uses that maximum
  and remains centered. `xl` has no maximum.
- For every projected `wbl-table`, Filter Row, any rendered Action Bar, and the
  Table scroll frame/grid share the same selected-rail inline edges at every
  size and viewport width. Table's contained/paginated viewport never expands
  beyond those edges to reserve a native scrollbar gutter; any such gutter is
  contained inside the viewport.
- The fixed 12px outer inset, existing surface tokens, vertical scrollport,
  optional `bottomPadding` spacer, and absence of a Main Container horizontal
  scroll owner are preserved. The Table owns horizontal overflow and fixed
  edges where its grid requires them; projected children do not gain a breakout
  layout from this rule.

### Local evidence

- Code: `src/app/design-system/primitives/main-container/main-container.component.ts`.
- Focused test: `src/app/design-system/primitives/main-container/main-container.component.spec.ts`.
- Public export: `src/app/design-system/index.ts`.
- Storybook: `storybook/stories/main-container.stories.ts`
  (`Design System/Primitives/wbl-main-container`).

## Figma status

The current layout-policy evidence is
[`21918:41024`](https://www.figma.com/design/EO9tuCsqwuHL8cZ4Eb2psR/%F0%9F%9A%9B-WB-Logistics-UI-kit?node-id=21918-41024).
It does not establish a published component key or native variable binding, so
no published assembly sidecar is recorded yet; a key must be read before one is
added.

## Provenance

The current local implementation, focused test, public export, Storybook entry,
and the exact Figma node above are the active evidence. Historical migration
artifacts do not define its API.

## Fixed-header composition

Transport Requests passes `[verticalScroll]="false"`, which keeps Header Widget,
tabs, and Table's Filter Row visible while the Table rows viewport owns vertical
scrolling. Existing consumers keep Main Container as their vertical scrollport
through the `true` default. The current implementation, focused tests, and
`TableContainedWithinRail` Storybook story verify both modes.
