Layout
Page regions and groups. The intent decides the element (<main>, <section>, <form>, <ul>, <li>) and the arrangement.
Other components
Examples
A form
layout.form renders the <form>, layout.field groups each field, layout.actions holds the buttons.
<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 renders a <ul> and layout.item its <li>s.
Design review
Tomorrow, 10:00Release planning
Friday, 14:30
<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.
Two-step sign-in
Ask for a code from your phone as well as your password.
<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.
Check your email to confirm your address.
<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.*
80 intentslayout.pageThe main column of an app page, used once per page
<main>layout.articleA reading column for a whole page: articles, documents, settings
<main>layout.taskA 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.headerThe site header at the top of every page: the site name and its navigation
<header>layout.shellThe 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.subnavA 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.asideContent beside the main column: a docs sidebar, filters, a table of contents
<aside>layout.footerThe site footer at the bottom of every page
<footer>layout.sectionA 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.groupA few closely related elements kept together, e.g. a heading and its text
<div>layout.headlineContent 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.labelledAn 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.formA form: renders <form> and stacks its fields
<form>layout.fieldOne form field: its label, input, help text and error
<div>layout.settingOne setting changed in place: its label and help text at the start, its switch or menu at the end. Settings pages, preferences
<div>layout.actionsCommands side by side: a form’s buttons, a dialog’s choices
<div>layout.pitch.actionsThe 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.toolbarControls that act on the content below them: filters, views, refresh
<div>layout.choicesThe options for one setting, side by side in one track with the chosen one filled: theme, view, units
<div>layout.metaA line of small facts about an item: time, author, counts
<div>layout.factsLabelled 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.factOne labelled fact in a layout.facts: a text.term above its text.value
<div>layout.siblingsLinks 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.listingA 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.tableRecords 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.scrollerContent 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.pagerThe row of nav.paging links below a long list or table. Give it aria-label="Pagination"
<nav>layout.blocksAn 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.cellOne block of a layout.blocks; data-on when it is filled
<div>layout.badgedAn 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.chartA 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.dockA 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.showcaseA 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.itemOne 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.overlayControls 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.pointsPoints made in running text, in no particular order, each a layout.point: the items of a bulleted list in prose
<ul>layout.stepsPoints made in running text that follow an order, each a layout.point: steps, a ranking
<ol>layout.pointOne point in a layout.points or layout.steps list, holding its text
<li>layout.listA list of items of the same kind: articles, people, notifications. Children are layout.item
<ul>layout.itemOne entry in a layout.list
<li>layout.panelA 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.featureA 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.copyThe pitch in a layout.feature: its heading, a lead and a link to more, centred against the demo
<section>layout.feature.demoThe live demo in a layout.feature, on its own inset panel
<div>layout.previewA 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.barThe 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.stageWhere a layout.preview renders its live example, recessed and centred
<div>layout.preview.codeThe 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.indexA 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.termA 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.situationA 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.teaserA 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.marketingThe main column of a marketing page: the home page, with its own typeface and roomier sections
<main>layout.commandA 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.registryThe registry shown behind a demo app, decorative: the entries for what is on screen, the current one lit
<div>layout.registry.trackThe moving list inside a layout.registry; it slides so the current entry is in view
<div>layout.registry.entryOne intent in a layout.registry; data-active on the one the demo points at
<div>layout.stageThe backdrop a product demo sits on: a band behind the window, with the layout.registry behind the app
<div>layout.windowA framed preview of an app inside a page: a title bar above the app
<div>layout.window.barThe toolbar of a layout.window, drawn like a browser: back, forward and reload, the address, and its controls on the right
<div>layout.terminalA small agent terminal floating over an example app: the conversation and checks that produced it
<div>layout.terminal.barThe title bar of a layout.terminal: window buttons, the title in the middle, its controls on the right
<div>layout.terminal.logThe 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.agentAn 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.tallyA row of running counts above something that produces them, each a layout.tally.item
<div>layout.tally.itemOne count in a layout.tally: a text.tally (with a text.delta beside it) over a text.caption naming it
<div>layout.arrivalWraps 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.bandA 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.demoAn example app shown inside a page, in its own theme
<div>layout.exampleA full example app in the home page’s tabs, in the site font and its own accent
<div>layout.peekShows 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.meterAn 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.fillThe 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.markerOutlines 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.collectionEqual 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.noticeA message about the page that the user can dismiss: check your email, password updated
<div>layout.loadingStands 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.dividerA 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.disclosureContent that opens and closes in place under a text.summary line: an FAQ answer, advanced settings
<details>layout.disclosure.panelA 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.
intentrequiredWhat this region is:
layout.page,layout.form,layout.list...LayoutIntentclassNameLayout and spacing only: margins, sizes, flex and grid, gaps, padding, text alignment. Visual classes warn in development and fail
prongs check.stringhideBelowHidden on screens narrower than this breakpoint: the desktop half of a region built twice.
'sm' | 'md' | 'lg'showBelowShown only on screens narrower than this breakpoint: the phone half.
'sm' | 'md' | 'lg'alignPushes the region to that end of its row or column, like a group of controls at the end of a header.
'start' | 'center' | 'end'refReaches the rendered element.
Ref<HTMLElement>unsafeClassNameBypasses the intent. Needs a
// prongs-unsafe: <reason>comment on the line above, andprongs checkcounts it.string
Rules
The only component with a
className, and only for layout.Hide or show by screen size with
hideBelowandshowBelow, and push a region to one end withalign, rather than classes.A box that needs its own background or border is a different intent (
layout.panel,layout.notice), not a class.Pass
onSubmitto alayout.form.