Agentic BP DScode-first design system

Using patterns

Patterns are blueprints for whole screens. Start every screen from one, and the layout, spacing and behavior come with it.

How Patterns Work

  • A pattern lays out a screen. It places the header, toolbar, content and footer in the right spots, at every width.
  • It is built only from kit components. It adds no new parts of its own.
  • It keeps no data. Your screen owns the rows, the form values and what is selected, and passes them in.
  • It has slots. Props like header, toolbar and children take kit components you fill in.

How to Use a Pattern

  1. Frame the page with AppShell when it is a full screen with the side navigation and top bar.
  2. Pick the page pattern that matches the screen, using the list below.
  3. Read its contract in contracts/: the slots, the options and the do-nots.
  4. Fill the slots with the kit components listed under “Built from”.
  5. Check it at every width with the Stage control on the pattern's page in this catalog.
  6. Missing something? Stop and decide: add the piece to the kit, or change the screen. Do not build a one-off.

Which One?

  • AccountFlow — Prototyping account screens that link together: find an account, open it, add an account or a product.
  • AppShell — Any product screen: a list, a record, a dashboard.
  • ChatWindow — The assistant docked beside a product screen.
  • Dashboard — A home screen that reports on a job: what is owed, what is due, what closed.
  • FormPage — Making a new record from a page of its own, not from a Modal.
  • GuidedProcessPage — A process of a few steps done in order where each step is a page of fields.
  • ListDetail — Going through records one after another: an invoice queue, an inbox, approvals.
  • ListPage — A screen that lists records with a search, filters and pages.
  • RecordPage — One record with its details in sections.
  • SettingsPage — The settings home: a page whose job is to send people on.

AccountFlow

The account provisioning flow: the accounts list, one account with its tabs, a new account, a new product on an account, and configuring that product alone or many products at once, one page at a time.

Use it for

  • Prototyping account screens that link together: find an account, open it, add an account or a product.
  • Showing how a list, a record and its create forms hand over to each other.
  • Adding products to an account: pick one and configure it, or add many at once in a grid.

Built from

AccordionCalloutAppShellBadgeCellCheckboxDataGridDatePickerDrawerEmptyStateFormFormDisplayFormPageInputLinkLinkListListPageLookupPageHeaderPaginationRadioGroupRecordPageScoreboardSectionSelectTableTabsToolbar

Avoid

  • Do not put the list's New and Export in its Toolbar. Page actions go in the PageHeader; the Toolbar keeps filters, search, the view and refresh.
  • Do not show the Account ID as plain text. It is a link Cell, so people can tell it opens the account.
  • Do not use Inputs to show saved account values. Use FormDisplay.

Starting code

<AccountFlow
  view={view}
  list={<ListPage toolbar={toolbar} pagination={pagination}><Table columns={columns} rows={rows} /></ListPage>}
  account={<RecordPage header={header} tabs={tabs} summary={scores}>{details}</RecordPage>}
  newAccount={<FormPage header={newHeader}><Form id="account" columns={2} sections={sections} onSubmit={create} /></FormPage>}
  newProduct={<FormPage header={productHeader}><Form id="product" columns={2} sections={productSections} onSubmit={add} /></FormPage>}
/>

Open AccountFlow in the catalog

AppShell

The frame every product screen sits in: the bar across the top, the rail under it down the side, the page in the middle, and the assistant beside it.

Use it for

  • Any product screen: a list, a record, a dashboard.
  • A screen the assistant can be opened beside.
  • The assistant's full-screen view, keeping the bar and the side nav.

Built from

AppHeaderAppNavPageHeaderChatWindowSection

Avoid

  • Do not add state to it; it is a blueprint. Keep which nav section, which page, whether the assistant is open and whether the page header is compact in the screen.
  • Do not build the bar, the rail or the page header inside it. Pass the kit pieces.
  • Do not trim, reorder or invent side nav links. Every screen passes the full APP_NAV and APP_NAV_END from @/patterns/AppShell/appNav and marks its own page with current.

Starting code

<AppShell header={<AppHeader />} nav={<AppNav items={APP_NAV} endItems={APP_NAV_END} current={pageId} onNavigate={openPage} expanded={navOpen} />} pageHeader={<PageHeader title="Accounts" />}>{page}</AppShell>

Open AppShell in the catalog

ChatWindow

The assistant window: its header over the turns, the composer at the foot, and the saved chats coming in beside or over them.

Use it for

  • The assistant docked beside a product screen.
  • The assistant's full-screen view, with the saved chats beside it.

Built from

ChatHeaderChatMessageChatComposerChatListEmptyStateIllustrationButton

Avoid

  • Do not add state to it; it is a blueprint. Keep the view, the turns and the panel in the screen.
  • Do not build the header, the turns or the box inside it. Pass the kit pieces.
  • Do not build the Get Started block from EmptyState, Illustration and Buttons in the screen. Pass starters and art.

Starting code

<ChatWindow composer={<ChatComposer onSend={send} />} chatsCount={7} onClose={close}>{turns}</ChatWindow>

Open ChatWindow in the catalog

Dashboard

The home screen of a job: the numbers that matter, grouped, with the charts and lists that explain them.

Use it for

  • A home screen that reports on a job: what is owed, what is due, what closed.
  • Numbers that need grouping, each group with its own charts and lists.
  • A screen where people pick between saved dashboards.

Built from

ToolbarDropdownSectionScoreboardBarChartLineChartPieChartLegendTableTimelineEmptyStateSkeleton

Avoid

  • Do not add state to it; it is a blueprint. Keep the dashboard, the open groups and the filters in the screen.
  • Do not build the bar, the groups or the tiles inside it. Pass the kit pieces.
  • Do not invent a tile grid, and do not put more than five tiles in a row. Two side by side is .layout-split, three is .layout-split--thirds, four or five is .layout-metrics with --fixed-4 or --fixed-5; read contracts/layout.json.

Starting code

<Dashboard label="Accounting dashboard" name="Accounting Dashboard" toolbar={<Toolbar views={saved} view={saved} />}>{groups}</Dashboard>

Open Dashboard in the catalog

FormPage

One record being filled in: its name and the save at the top, anything that has to be read first under that, and the fields in folding groups underneath.

Use it for

  • Making a new record from a page of its own, not from a Modal.
  • Editing a record's fields on a page, where the groups of fields are long enough to fold.
  • The save has to be in reach while a long form is scrolled.

Built from

PageHeaderCalloutFormSectionInputSelectCheckboxDatePickerLookup

Avoid

  • Do not invent CreatePage, EditPage, NewRecordPage or FormLayout.
  • Do not put a Form inside every Section yourself. One Form holds the groups as sections, so one press saves the page.
  • Do not add a second save bar at the foot. The header keeps the save in reach.

Starting code

<FormPage
  columns={2}
  header={<PageHeader breadcrumbs={[{ label: "Home", href: "/" }, { label: "Accounts", href: "/accounts" }]} title="Create account" actions={<><Button onClick={cancel}>Cancel</Button><Button emphasis="strong" intent="brand" type="submit" form="account">Create</Button></>} />}
  notice={<Callout intent="info" onDismiss={hide}>Use this form for the account's general and default billing information.</Callout>}
>
  <Form id="account" labelPosition="start" onSubmit={save} sections={sections} />
</FormPage>

Open FormPage in the catalog

GuidedProcessPage

A process walked one page per step: a dark column per step before it starts, then each step's fields with every step on a dark panel at the start or end edge of the page.

Use it for

  • A process of a few steps done in order where each step is a page of fields.
  • Work that can be saved and picked up again part way through.

Built from

GuidedProcessFormCallout

Avoid

  • Do not invent WizardPage, StepPage, a steps panel or a footer bar of your own. The parts are GuidedProcess.
  • Do not put a PageHeader over it. The header part names the step.
  • Do not use it for a short flow; two or three fields belong in a Modal with Steps.

Starting code

<GuidedProcessPage
  view="step" side="end" stepsOpen={open} onStepsClose={close}
  header={<GuidedProcess part="header" title="Import usage" stepTitle="Map the columns" current={2} total={4} onStepsOpen={openSteps} />}
  steps={<GuidedProcess part="steps" steps={steps} current={2} onClose={close} />}
  footer={<GuidedProcess part="footer" updated="Last updated on September 13, 12:43 PM" actions={actions} onAction={handle} />}
>
  <Form id="step" columns={2} onSubmit={next}>{fields}</Form>
</GuidedProcessPage>

Open GuidedProcessPage in the catalog

ListDetail

A list with the picked record beside it: a ListView on one side and the record in a pane on the other, or one at a time on a phone.

Use it for

  • Going through records one after another: an invoice queue, an inbox, approvals.
  • A record short enough to read in a pane beside the list.
  • A list that must stay in view while its records are read.
  • Records under other records, like an account's invoices: a nested ListView steps in, and the leaf opens beside it.
  • A long queue split by status: a grouped ListView keeps what needs attention on top.
  • Stacked when the list is short and the record needs the full width; inline when people read one record at a time and want the rest of the list to stay in place around it.

Built from

ListViewSectionFormDisplayEmptyStateButton

Avoid

  • Do not add state to it; it is a blueprint. The screen keeps which record is open.
  • Do not use it for records with a lot to show or edit. Drill into a RecordPage instead.
  • Do not build the list inside it. Pass a kit ListView.

Starting code

<ListDetail
  label="Invoices" open={Boolean(invoice)} onBack={() => setOpenId(null)} backLabel="Back to invoices"
  title={invoice?.number} actions={<Button size="sm">Download</Button>}
  list={<ListView label="Invoices" selection="single" interaction="drill" selected={openId ? [openId] : []} onOpen={setOpenId} items={invoices} />}
  empty={<EmptyState icon="receipt_long" title="Pick an invoice" />}
  detail={invoice && <Section title="Invoice Details">…</Section>}
/>

Open ListDetail in the catalog

ListPage

A list of records: the bar to find and filter them, the rows themselves, and the pages under them.

Use it for

  • A screen that lists records with a search, filters and pages.
  • A list that has to show loading, empty and failed states.
  • A list people can lay out more than one way.

Built from

ToolbarTableListViewCardPaginationEmptyStateSkeletonCallout

Avoid

  • Do not add state to it; it is a blueprint. Keep the filters, the rows, the selection and the page in the screen.
  • Do not build the bar, the rows or the pages inside it. Pass the kit pieces.
  • Do not show the bulk bar and the toolbar at once; passing bulk swaps them.

Starting code

<ListPage label="Invoices" toolbar={<Toolbar />} pagination={<Pagination total={248} />}><Table columns={columns} rows={rows} /></ListPage>

Open ListPage in the catalog

RecordPage

One record: its name and actions at the top, the tabs of what belongs to it, the numbers that matter, and its details in sections underneath.

Use it for

  • One record with its details in sections.
  • A record whose related lists live behind tabs.

Built from

PageHeaderTabsCalloutToolbarScoreboardSectionFormFormDisplayTable

Avoid

  • Do not add state to it; it is a blueprint. Keep the tab, the sections and the notice in the screen.
  • Do not build the header, the tabs or the bar inside it. Pass the kit pieces.
  • Do not put page padding around it. It sits in the AppShell page, which owns that.

Starting code

<RecordPage label="Account" columns={2} header={<PageHeader title="Apex Digital Services" badge="Active" />} tabs={<Tabs items={parts} />} summary={<Scoreboard items={numbers} />}>
  <Section title="Account information" collapsible>
    <FormDisplay label="Account name" value="Apex Digital Services" />
    <FormDisplay label="Account number" value="ACC-10428" />
  </Section>
  <Section title="Recent invoices"><Table columns={invoiceColumns} rows={invoices} /></Section>
</RecordPage>

Open RecordPage in the catalog

SettingsPage

The settings home: a grid of the places people can go from here, each with its own icon and color.

Use it for

  • The settings home: a page whose job is to send people on.
  • Another home shaped the same way, like a reports home.
  • A set of places where the icon and its color help people find the same one again.

Built from

NavTileSectionCallout

Avoid

  • Do not add state to it; it is a blueprint.
  • Do not build the tiles inside it. Pass kit Tiles.
  • Do not use kit Cards as the tiles; a Card is for comparing and picking, a NavTile is a way in.

Starting code

<SettingsPage label="Settings">{places.map((p) => <NavTile key={p.id} {...p} />)}</SettingsPage>

Open SettingsPage in the catalog