# Layout

Page regions and groups. The intent decides the element (`<main>`, `<section>`, `<form>`, `<ul>`, `<li>`) and the arrangement.

Renders: `main` `header` `div` `aside` `footer` `section` `form` `nav` `table` `figure` `ul` `ol` `li` `hr` `details` · Intents: [80](#intents-it-renders) · Domains: [`layout.*`](https://www.prongs.dev/docs/intents/layout.md)

## Examples

### A form

[`layout.form`](https://www.prongs.dev/docs/intents/layout/form.md) renders the `<form>`, [`layout.field`](https://www.prongs.dev/docs/intents/layout/field.md) groups each field, [`layout.actions`](https://www.prongs.dev/docs/intents/layout/actions.md) holds the buttons.

```tsx
<Layout intent="layout.form" className="w-full max-w-sm">
  <Layout intent="layout.field">
    <Text intent="text.label" as="label" htmlFor="example-name">Name</Text>
    <Field intent="field.text" id="example-name" />
  </Layout>
  <Layout intent="layout.actions">
    <Action intent="action.save">Save</Action>
  </Layout>
</Layout>
```

### A list

[`layout.list`](https://www.prongs.dev/docs/intents/layout/list.md) renders a `<ul>` and [`layout.item`](https://www.prongs.dev/docs/intents/layout/item.md) its `<li>`s.

```tsx
<Layout intent="layout.list" className="w-full">
  <Layout intent="layout.item">
    <Text intent="heading.item">Design review</Text>
    <Text intent="text.meta">Tomorrow, 10:00</Text>
  </Layout>
  <Layout intent="layout.item">
    <Text intent="heading.item">Release planning</Text>
    <Text intent="text.meta">Friday, 14:30</Text>
  </Layout>
</Layout>
```

### A box about one thing

A box with its own surface is an intent too, not a class.

```tsx
<Layout intent="layout.panel" className="w-full max-w-sm">
  <Text intent="heading.item">Two-step sign-in</Text>
  <Text intent="text.body">Ask for a code from your phone as well as your password.</Text>
  <Layout intent="layout.actions">
    <Action intent="action.start">Turn on</Action>
  </Layout>
</Layout>
```

### A notice

A message about the page, with a way to dismiss it.

```tsx
<Layout intent="layout.notice" className="w-full">
  <Text intent="feedback.info">Check your email to confirm your address.</Text>
  <Action intent="action.dismiss" icon={<X aria-hidden className="size-4" />} label="Dismiss" />
</Layout>
```

## Intents it renders

One domain, with its own page of neighbours and examples. The intent decides the element; these are the starter intents.

### Layout [`layout.*`](https://www.prongs.dev/docs/intents/layout.md)

80 intents

| Intent | Use it for | Resolves to |
|---|---|---|
| [`layout.page`](https://www.prongs.dev/docs/intents/layout/page.md) | The main column of an app page, used once per page | `<main>` |
| [`layout.article`](https://www.prongs.dev/docs/intents/layout/article.md) | A reading column for a whole page: articles, documents, settings | `<main>` |
| [`layout.task`](https://www.prongs.dev/docs/intents/layout/task.md) | A page for one short task, centred on the screen: sign in, reset password, a confirmation. Its headings, text and actions are centred; a form or section inside takes the full width | `<main>` |
| [`layout.header`](https://www.prongs.dev/docs/intents/layout/header.md) | The site header at the top of every page: the site name and its navigation | `<header>` |
| [`layout.shell`](https://www.prongs.dev/docs/intents/layout/shell.md) | The frame of an app with a sidebar: one layout.aside beside the main column. Used once, around the signed-in part of the app | `<div>` |
| [`layout.subnav`](https://www.prongs.dev/docs/intents/layout/subnav.md) | A page with its own section nav beside its content: settings, account, a project’s admin. Put the nav first (a nav of nav.page links), then the content. On phones the nav sits above it | `<div>` |
| [`layout.aside`](https://www.prongs.dev/docs/intents/layout/aside.md) | Content beside the main column: a docs sidebar, filters, a table of contents | `<aside>` |
| [`layout.footer`](https://www.prongs.dev/docs/intents/layout/footer.md) | The site footer at the bottom of every page | `<footer>` |
| [`layout.section`](https://www.prongs.dev/docs/intents/layout/section.md) | A part of a page made of separate blocks, with or without a title: a section under its heading, the cards in a column, a panel’s contents | `<section>` |
| [`layout.group`](https://www.prongs.dev/docs/intents/layout/group.md) | A few closely related elements kept together, e.g. a heading and its text | `<div>` |
| [`layout.headline`](https://www.prongs.dev/docs/intents/layout/headline.md) | Content at the start of a line and its controls at the end: a section’s title and its button, a panel’s title bar, a list entry with its actions. The controls drop below when the line runs out of room | `<div>` |
| [`layout.labelled`](https://www.prongs.dev/docs/intents/layout/labelled.md) | An icon, avatar or logo beside its text: a person’s photo and name, an icon and its label. The text shrinks and wraps; the icon keeps its size | `<div>` |
| [`layout.form`](https://www.prongs.dev/docs/intents/layout/form.md) | A form: renders <form> and stacks its fields | `<form>` |
| [`layout.field`](https://www.prongs.dev/docs/intents/layout/field.md) | One form field: its label, input, help text and error | `<div>` |
| [`layout.setting`](https://www.prongs.dev/docs/intents/layout/setting.md) | One setting changed in place: its label and help text at the start, its switch or menu at the end. Settings pages, preferences | `<div>` |
| [`layout.actions`](https://www.prongs.dev/docs/intents/layout/actions.md) | Commands side by side: a form’s buttons, a dialog’s choices | `<div>` |
| [`layout.pitch.actions`](https://www.prongs.dev/docs/intents/layout/pitch-actions.md) | The main calls to action under a page’s pitch headline: the install command and the guide beside it, a size up from other buttons | `<div>` |
| [`layout.toolbar`](https://www.prongs.dev/docs/intents/layout/toolbar.md) | Controls that act on the content below them: filters, views, refresh | `<div>` |
| [`layout.choices`](https://www.prongs.dev/docs/intents/layout/choices.md) | The options for one setting, side by side in one track with the chosen one filled: theme, view, units | `<div>` |
| [`layout.meta`](https://www.prongs.dev/docs/intents/layout/meta.md) | A line of small facts about an item: time, author, counts | `<div>` |
| [`layout.facts`](https://www.prongs.dev/docs/intents/layout/facts.md) | Labelled facts about the subject of a page, side by side under its title with rules between: profile, status, component. Children are layout.fact | `<div>` |
| [`layout.fact`](https://www.prongs.dev/docs/intents/layout/fact.md) | One labelled fact in a layout.facts: a text.term above its text.value | `<div>` |
| [`layout.siblings`](https://www.prongs.dev/docs/intents/layout/siblings.md) | Links to the pages beside this one, side by side with the open one filled: the other intents in a domain. One line; scrolls sideways when it overflows, with no scrollbar. Pass data-fade="start", "end" or "both" to fade the edges that hide more. Children are nav.page; pass current on the open one | `<nav>` |
| [`layout.listing`](https://www.prongs.dev/docs/intents/layout/listing.md) | A code block with the bar above it: what the code is (TSX, Terminal) and a Copy button. Holds a layout.group bar, then one text.code | `<div>` |
| [`layout.table`](https://www.prongs.dev/docs/intents/layout/table.md) | Records with the same fields, compared across: members, invoices, API keys. Write plain thead, tbody, tr, th scope="col" and td inside, with no classes; data-off on a tr greys out a row that is switched off. data-head="bar" sets the header row on a bar and pads the outer columns, for a table that runs edge to edge in a panel with no padding. With more columns than fit on a phone, wrap it in a layout.scroller. data-arrival makes its rows rise into place; give each tr a growing animationDelay to stagger them | `<table>` |
| [`layout.scroller`](https://www.prongs.dev/docs/intents/layout/scroller.md) | Content wider than its column, which the reader scrolls sideways: a table with many columns, a long line of code, a board. Give it tabIndex={0} and an aria-label, so keyboard users can scroll it too | `<div>` |
| [`layout.pager`](https://www.prongs.dev/docs/intents/layout/pager.md) | The row of nav.paging links below a long list or table. Give it aria-label="Pagination" | `<nav>` |
| [`layout.blocks`](https://www.prongs.dev/docs/intents/layout/blocks.md) | An amount shown as a row of blocks, like a battery: the more blocks filled, the more of it. Each block is a layout.blocks.cell, with data-on on the filled ones; data-tone="success", "warning" or "danger" on the row colours them by level. Give it aria-valuenow, aria-valuemin and aria-valuemax | `<div>` |
| [`layout.blocks.cell`](https://www.prongs.dev/docs/intents/layout/blocks-cell.md) | One block of a layout.blocks; data-on when it is filled | `<div>` |
| [`layout.badged`](https://www.prongs.dev/docs/intents/layout/badged.md) | An icon or avatar with a status dot on its corner: a device and whether it is online. Put the icon first and a text.indicator last | `<div>` |
| [`layout.chart`](https://www.prongs.dev/docs/intents/layout/chart.md) | A chart: a trend over time, a breakdown. Renders <figure>; put the chart's svg or canvas inside with a height, and a text.caption that says what it shows | `<figure>` |
| [`layout.dock`](https://www.prongs.dev/docs/intents/layout/dock.md) | A bar that stays at the bottom of the screen while the content under it scrolls or changes: the now-playing bar, a cart total, a long form’s submit button on phones | `<footer>` |
| [`layout.showcase`](https://www.prongs.dev/docs/intents/layout/showcase.md) | A few items side by side where the one in focus takes more of the room: featured photos, picks of the week. On small screens they stack, top to bottom. Children are layout.showcase.item; mark the one in focus with data-active | `<div>` |
| [`layout.showcase.item`](https://www.prongs.dev/docs/intents/layout/showcase-item.md) | One item in a layout.showcase: its media, then its title and caption. Mark the caption with data-caption: it always takes its room, and shows only on the item in focus, so nothing moves. It rises into view when it appears (stagger them with an animationDelay), widens when it has data-active, and its image keeps its height and crops to fit | `<div>` |
| [`layout.overlay`](https://www.prongs.dev/docs/intents/layout/overlay.md) | Controls drawn over a photo or video, like the arrows in a dialog.media: the buttons inside get a translucent dark backing so they read on any image | `<div>` |
| [`layout.points`](https://www.prongs.dev/docs/intents/layout/points.md) | Points made in running text, in no particular order, each a layout.point: the items of a bulleted list in prose | `<ul>` |
| [`layout.steps`](https://www.prongs.dev/docs/intents/layout/steps.md) | Points made in running text that follow an order, each a layout.point: steps, a ranking | `<ol>` |
| [`layout.point`](https://www.prongs.dev/docs/intents/layout/point.md) | One point in a layout.points or layout.steps list, holding its text | `<li>` |
| [`layout.list`](https://www.prongs.dev/docs/intents/layout/list.md) | A list of items of the same kind: articles, people, notifications. Children are layout.item | `<ul>` |
| [`layout.item`](https://www.prongs.dev/docs/intents/layout/item.md) | One entry in a layout.list | `<li>` |
| [`layout.panel`](https://www.prongs.dev/docs/intents/layout/panel.md) | A self-contained box about one thing: a sign-in box, a profile summary. Its border is quieter than a layout.teaser’s, since it does not open anything | `<div>` |
| [`layout.feature`](https://www.prongs.dev/docs/intents/layout/feature.md) | A home page section pairing a pitch with a live demo: a layout.feature.copy and a layout.feature.demo, side by side on wide screens, the copy taking the narrower side | `<div>` |
| [`layout.feature.copy`](https://www.prongs.dev/docs/intents/layout/feature-copy.md) | The pitch in a layout.feature: its heading, a lead and a link to more, centred against the demo | `<section>` |
| [`layout.feature.demo`](https://www.prongs.dev/docs/intents/layout/feature-demo.md) | The live demo in a layout.feature, on its own inset panel | `<div>` |
| [`layout.preview`](https://www.prongs.dev/docs/intents/layout/preview.md) | A live example with its code attached underneath, in one frame with the code block’s edge: a layout.preview.bar, a layout.preview.stage, then a layout.preview.code | `<div>` |
| [`layout.preview.bar`](https://www.prongs.dev/docs/intents/layout/preview-bar.md) | The bar on top of a layout.preview: what it shows on the left, its copy button on the right, ruled off from the stage | `<div>` |
| [`layout.preview.stage`](https://www.prongs.dev/docs/intents/layout/preview-stage.md) | Where a layout.preview renders its live example, recessed and centred | `<div>` |
| [`layout.preview.code`](https://www.prongs.dev/docs/intents/layout/preview-code.md) | The code under a layout.preview’s stage: its code block sits flush, without its own corners or border, ruled off from the stage | `<div>` |
| [`layout.index`](https://www.prongs.dev/docs/intents/layout/index.md) | A bordered box of rows read like a table: props, rules, the intents of a domain. Holds a layout.list of layout.term or layout.situation rows, padded to the box’s edge | `<div>` |
| [`layout.term`](https://www.prongs.dev/docs/intents/layout/term.md) | A term and what it means, side by side on wide screens with the term in a fixed column, stacked on phones: a prop, a token, a rule | `<li>` |
| [`layout.situation`](https://www.prongs.dev/docs/intents/layout/situation.md) | A situation and the intent it calls for, in an "If it is / Use" layout.index. Pass data-current on the row for the page you are on | `<li>` |
| [`layout.teaser`](https://www.prongs.dev/docs/intents/layout/teaser.md) | A box in a layout.collection that previews one page and opens it as a whole: its nav.item title covers the box. Its border reads stronger than a layout.panel’s, and it lifts a level on hover | `<div>` |
| [`layout.marketing`](https://www.prongs.dev/docs/intents/layout/marketing.md) | The main column of a marketing page: the home page, with its own typeface and roomier sections | `<main>` |
| [`layout.command`](https://www.prongs.dev/docs/intents/layout/command.md) | A shell command to copy and run: the main call to action wherever it appears. Its copy button covers the whole box, so a click anywhere copies | `<div>` |
| [`layout.registry`](https://www.prongs.dev/docs/intents/layout/registry.md) | The registry shown behind a demo app, decorative: the entries for what is on screen, the current one lit | `<div>` |
| [`layout.registry.track`](https://www.prongs.dev/docs/intents/layout/registry-track.md) | The moving list inside a layout.registry; it slides so the current entry is in view | `<div>` |
| [`layout.registry.entry`](https://www.prongs.dev/docs/intents/layout/registry-entry.md) | One intent in a layout.registry; data-active on the one the demo points at | `<div>` |
| [`layout.stage`](https://www.prongs.dev/docs/intents/layout/stage.md) | The backdrop a product demo sits on: a band behind the window, with the layout.registry behind the app | `<div>` |
| [`layout.window`](https://www.prongs.dev/docs/intents/layout/window.md) | A framed preview of an app inside a page: a title bar above the app | `<div>` |
| [`layout.window.bar`](https://www.prongs.dev/docs/intents/layout/window-bar.md) | The toolbar of a layout.window, drawn like a browser: back, forward and reload, the address, and its controls on the right | `<div>` |
| [`layout.terminal`](https://www.prongs.dev/docs/intents/layout/terminal.md) | A small agent terminal floating over an example app: the conversation and checks that produced it | `<div>` |
| [`layout.terminal.bar`](https://www.prongs.dev/docs/intents/layout/terminal-bar.md) | The title bar of a layout.terminal: window buttons, the title in the middle, its controls on the right | `<div>` |
| [`layout.terminal.log`](https://www.prongs.dev/docs/intents/layout/terminal-log.md) | The lines in a layout.terminal: the newest at the bottom, older ones fading out at the top. data-still shows a finished run whole, with no fade | `<div>` |
| [`layout.agent`](https://www.prongs.dev/docs/intents/layout/agent.md) | An agent terminal shown in the page flow, at a fixed height: a layout.terminal.bar, then a layout.terminal.log. data-still lets a finished run set its own height | `<div>` |
| [`layout.tally`](https://www.prongs.dev/docs/intents/layout/tally.md) | A row of running counts above something that produces them, each a layout.tally.item | `<div>` |
| [`layout.tally.item`](https://www.prongs.dev/docs/intents/layout/tally-item.md) | One count in a layout.tally: a text.tally (with a text.delta beside it) over a text.caption naming it | `<div>` |
| [`layout.arrival`](https://www.prongs.dev/docs/intents/layout/arrival.md) | Wraps something that rises into place as it appears, like the cards of a feed. Give each a growing style={{ animationDelay }} to stagger a row of them, and a key that changes to play it again | `<div>` |
| [`layout.band`](https://www.prongs.dev/docs/intents/layout/band.md) | A full-width strip a step lighter than the page, edge to edge, for the section a page closes on: the last call to action | `<section>` |
| [`layout.demo`](https://www.prongs.dev/docs/intents/layout/demo.md) | An example app shown inside a page, in its own theme | `<div>` |
| [`layout.example`](https://www.prongs.dev/docs/intents/layout/example.md) | A full example app in the home page’s tabs, in the site font and its own accent | `<div>` |
| [`layout.peek`](https://www.prongs.dev/docs/intents/layout/peek.md) | Shows the top of something tall, fading out at the bottom: an example app inside its frame on the home page, the frame drawn whole around it | `<div>` |
| [`layout.meter`](https://www.prongs.dev/docs/intents/layout/meter.md) | An amount within a known range, shown as a filled track: battery, storage used, a quota. Give it aria-label, aria-valuenow, aria-valuemin and aria-valuemax, and one layout.meter.fill inside | `<div>` |
| [`layout.meter.fill`](https://www.prongs.dev/docs/intents/layout/meter-fill.md) | The filled part of a layout.meter. Set its width to the amount with style={{ width: "62%" }}; data-tone="warning" or "danger" when the amount is a problem, "muted" or "faint" for a value it is compared with | `<div>` |
| [`layout.marker`](https://www.prongs.dev/docs/intents/layout/marker.md) | Outlines the element an inspector points at, drawn over the page at its position, with its text.marker above. data-mode="outline" draws only a faint dashed outline, for every intent at once | `<div>` |
| [`layout.collection`](https://www.prongs.dev/docs/intents/layout/collection.md) | Equal items shown side by side as a grid: people to follow, featured sources. It fits as many columns as its own width allows, so it works in a sidebar as well as across a page; set columns from a breakpoint only when the count matters | `<div>` |
| [`layout.notice`](https://www.prongs.dev/docs/intents/layout/notice.md) | A message about the page that the user can dismiss: check your email, password updated | `<div>` |
| [`layout.loading`](https://www.prongs.dev/docs/intents/layout/loading.md) | Stands in for content that is still loading: a full-width line of text unless you give it the shape of what will appear | `<div>` |
| [`layout.divider`](https://www.prongs.dev/docs/intents/layout/divider.md) | A break between two parts of the same content: groups in a menu, sections of a panel. Runs across whichever way its parent flows | `<hr>` |
| [`layout.disclosure`](https://www.prongs.dev/docs/intents/layout/disclosure.md) | Content that opens and closes in place under a text.summary line: an FAQ answer, advanced settings | `<details>` |
| [`layout.disclosure.panel`](https://www.prongs.dev/docs/intents/layout/disclosure-panel.md) | A layout.disclosure in a bordered box, for a long block of optional detail: its text.summary is a row with a chevron that turns when open, and a line under it separates the content. Pad the content yourself | `<details>` |

## Props

Plus its element’s usual attributes (`id`, `onClick`, `aria-*`, `data-*`). `className` is for layout only.

| Prop | Type | What it does |
|---|---|---|
| `intent` (required) | `LayoutIntent` | What this region is: [`layout.page`](https://www.prongs.dev/docs/intents/layout/page.md), [`layout.form`](https://www.prongs.dev/docs/intents/layout/form.md), [`layout.list`](https://www.prongs.dev/docs/intents/layout/list.md)... |
| `className` | `string` | Layout and spacing only: margins, sizes, flex and grid, gaps, padding, text alignment. Visual classes warn in development and fail `prongs check`. |
| `hideBelow` | `'sm' \| 'md' \| 'lg'` | Hidden on screens narrower than this breakpoint: the desktop half of a region built twice. |
| `showBelow` | `'sm' \| 'md' \| 'lg'` | Shown only on screens narrower than this breakpoint: the phone half. |
| `align` | `'start' \| 'center' \| 'end'` | Pushes the region to that end of its row or column, like a group of controls at the end of a header. |
| `ref` | `Ref<HTMLElement>` | Reaches the rendered element. |
| `unsafeClassName` | `string` | Bypasses the intent. Needs a `// prongs-unsafe: <reason>` comment on the line above, and `prongs check` counts it. |

## Rules

- The only component with a `className`, and only for layout.
- Hide or show by screen size with `hideBelow` and `showBelow`, and push a region to one end with `align`, rather than classes.
- A box that needs its own background or border is a different intent ([`layout.panel`](https://www.prongs.dev/docs/intents/layout/panel.md), [`layout.notice`](https://www.prongs.dev/docs/intents/layout/notice.md)), not a class.
- Pass `onSubmit` to a [`layout.form`](https://www.prongs.dev/docs/intents/layout/form.md).
