designComponents

Search the design system…

Search the design system…

layouts

DSPageShell

Distilled list-page layout: title row at the top, scroll-owned body in the middle, mono-caps stats footer at the bottom. The recurring template behind every /list page in CIP / Archi / Supertest.

The footer's negative margins bleed past the AppShell's `p-6` gutter so the divider hits the viewport edges. Pass `footer={null}` to omit it. Pass `headerActions` for the right-aligned slot — usually filters or a primary `+ New` button.

Install

Pull this component (and its dependencies) straight into your app via the shadcn CLI:

npx shadcn@latest add https://design.oapps.io/r/ds-page-shell.json

Or import from the workspace package:

import { DSPageShell } from "@8maverik8/design";

Examples

Audiences list page

Audiences

(DSTable goes here)

147 audiences · last updated 2m ago

<DSPageShell
  title="Audiences"
  headerActions={<Button><Plus />New audience</Button>}
  footer="147 audiences · last updated 2m ago"
>
  <DSTable …/>
</DSPageShell>

Anatomy

  • titlePage heading — usually the entity name ("Audiences", "Campaigns").
  • headerLeftMetaOptional content rendered next to the title (refresh spinner, badge).
  • headerActionsRight-aligned slot — filters, search, primary action.
  • footerMono-caps stats line at the bottom. Pass `null` to omit.
  • childrenBody. Owns its own scroll — wrap your DSTable or list directly inside.

Guidelines

  • Use this for *every* product list page so headers, footers and scroll behavior stay identical across products.
  • Render your own h1 outside DSPageShell.Two title rows on the same page is a sign you should split into separate pages.
  • Split a table page with anything between the header row and the table itself.Title + headerActions row, then immediately the table — that's the entire structure. Do NOT slot dashboard widgets, KPI cards, charts, or summary tiles between the header and the table. If the page is genuinely a dashboard *with* a table, it's a different page model — build a /dashboard page, then link to a separate /list page for the data. Mixing the two breaks visual coherence and trains users to scroll past metrics to reach the data they actually came for.
  • Add description text under (or above, or beside) the title on a table page.On a list page the table itself IS the description. A subtitle / muted description line under "Audiences" or "Campaigns" adds noise and pushes the table down. If users need explanation, put it in an Empty-state when the list is empty, or in a `?` tooltip on a column header — never on the page chrome.
  • Stick badges, chips, tags, or status pills next to the title in DSPageShell.Status / context belongs in the row of the entity it describes, not in the page header. The header is for the entity *type* ("Audiences") and global actions only — no per-record decoration. The same goes for floating chips inside `headerActions`: keep that slot to interactive controls (Button, Filter, Search Input) only.
  • Keep ALL controls in headerActions at the same height (default `size`).The most common DS regression: agents pair `<Button size="sm">` with a default `<Input>` and the search field reads as visually shorter than the buttons. Use Button's and Input's matching size variants — both have `default` (32px), `sm` (28px), `xs` (24px), `lg` (40px), `toolbar` (26px). Never mix sizes within a single header row.
  • Order headerActions left-to-right: filters → search → primary action.Same grammar across every product. Filter dropdowns first (most-changed), search second (occasional use), `+ New` last (it's a destination, not a knob).