Object Page Cards
One padded card shell for Object Page body regions — titles, dividers, Descriptions layout, and toolbar control height.
Use this page for Object Page specifics: Descriptions layout, Related List body padding, and toolbar control height. The shell, title, and rhythm tokens are cross-page and live in Page Sections — this page does not redefine them.
Object Page body cards share one padded shell, two title weights, and one toolbar control height. Do not mix a flush-table Related List with a padded Notes card on the same page.
Examples
Padded Card Vs Flush Table
Left side is the flush-table recipe: muted title, hairline under the toolbar, size="sm" Columns, table flush to the card edge. Right side is SectionCard — p-6 padding, 16px semibold title in the shell's own title slot, default-height tools, table inset with gap-4.
Wrong
Attachments
| File | Uploaded |
|---|---|
| po.pdf | 2026-03-01 |
Right
Attachments
| File | Uploaded |
|---|---|
| po.pdf | 2026-03-01 |
"use client";
import { SectionCard } from "@/components/f-ui/section-card/section-card";
import { Button } from "@/components/ui/button";
import { DesignCompare } from "@/demos/_design/design-compare";
function MiniTable() {
return (
<table className="w-full text-sm">
<thead>
<tr className="text-muted-foreground text-xs">
<th className="px-2 py-2 text-left font-medium">File</th>
<th className="px-2 py-2 text-left font-medium">Uploaded</th>
</tr>
</thead>
<tbody>
<tr className="border-t">
<td className="px-2 py-2">po.pdf</td>
<td className="px-2 py-2">2026-03-01</td>
</tr>
</tbody>
</table>
);
}
export function ObjectPageCardsShellDemo() {
return (
<DesignCompare
wrong={
<div className="bg-card overflow-hidden rounded-xl border">
<div className="flex items-center justify-between border-b px-6 py-3">
<h2 className="text-muted-foreground text-sm font-medium">Attachments</h2>
<div className="flex items-center gap-2">
<Button type="button" variant="outline" size="sm">
Columns
</Button>
<Button type="button">Add attachment</Button>
</div>
</div>
<MiniTable />
</div>
}
right={
<SectionCard
title="Attachments"
tools={
<Button type="button" variant="outline">
Columns
</Button>
}
actions={<Button type="button">Add attachment</Button>}
>
<MiniTable />
</SectionCard>
}
/>
);
}Shell
SectionCard is the shell — do not hand-roll the classes, and do not render a title next to it:
<SectionCard title="Attachments" actions={<Button>Add attachment</Button>}>
<AttachmentsTable />
</SectionCard>Padding, title type, page gap, and group rhythm are defined once in Page Sections § Type Ladder and § Rhythm. Object Page adds only these:
| Token | Value | What to do |
|---|---|---|
Related List bodyPadding | default | Shares card p-6. "none" (-mx-6 -mb-6) is escape hatch only — not the Table recipe. |
| Header summary | content + extraContent | Do not stuff a page Alert into the left content column beside Amount |
Descriptions bordered | Never inside a card | It paints a second card (p-4 + extra border). The host owns the shell; Descriptions is unbordered content. |
The problem this prevents is mixed density: header padded with px-6, table flush with p-0, and a border-b hairline pretending to be title→body air.
Titles
The problem is treating every heading as the same muted GroupHeading. A card that owns one topic (Attachments, Activity, Line items) uses the shell's title. Peer groups inside one card (Contact, Fulfillment, Notes) stay muted SectionGroupHeading.
| Role | Where | When |
|---|---|---|
| Card title | SectionCard / RelatedList title prop | Attachments, Activity, Line items |
| In-card group | SectionGroupHeading (muted) | Contact, Fulfillment, Notes inside a shared card |
Descriptions title | Do not pass on Object Page | The card owns the title |
One title owner. Related List owns the card title (title + optional count) — do not wrap it in another group heading. Do not pass Descriptions.title when the field grid already sits in the card. Notes that share a card with Addresses stay muted — do not upgrade Notes to match Attachments.
Copy is sentence case (Line items, Add attachment) — Sentence Case. No CSS uppercase.
Dividers
The problem is a border-b under the card title (Ant Card head). That hairline is a second language next to padded Notes / DetailCard. Title→table air is gap-4, not a line.
Separator only between in-card groups. No border-b under the card title. Title→table air is gap-4.
Use a Separator | Do not |
|---|---|
| Between in-card groups (Contact then Fulfillment) | Under the card title (border-b on the toolbar) |
| — | Between title and Table/List — that air is gap-4 |
ASCII diagrams that draw a line under the title mean “next row is the body”, not a CSS hairline.
Descriptions Layout
Do not reopen the kit default. Cite CRUD Page Patterns — Descriptions Layout And Header KPIs: short scalars horizontal; Notes/addresses vertical; kit default stays vertical; header KPIs are Statistic.
| Body | layout |
|---|---|
| Short scalars (Contact, Fulfillment, status, dates, money codes) | horizontal |
| Long text (Notes, addresses, multi-line) | vertical (or wider span) |
| Kit default | vertical — do not flip |
| Page-header Amount / Quantity | Statistic, never Descriptions hero type |
Empty field values use EmptyValuePlaceholder — not region Empty.
Region Toolbar Controls
The problem is mixing SelectTrigger size="sm" / Button size="sm" (h-7) with default Buttons (h-8) on the same title row. Default height is h-8. Do not put SelectTrigger size="sm" or Button size="sm" on the title row (including Demo status teaching controls). Tools ghost/outline; at most one solid Add. See CRUD Region Table Toolbar and Button And Action Emphasis.
| Rule | Lock |
|---|---|
| Height | Default h-8. Button, SelectTrigger, outline Columns, solid Add — same height |
| Ban | SelectTrigger size="sm" and Button size="sm" in this row |
| Emphasis | Tools ghost/outline; at most one solid (region Add) |
| Layout | Horizontal cluster on the same row as the title; no floating vertical stack |
Popover footers inside Columns (Reset / Save) may stay size="sm" — those are overlay chrome, not the card toolbar.
Checklist
- Body cards use
SectionCard/RelatedListwith thetitleprop — no heading rendered as an outside sibling (Page Sections). - Object Page body cards follow this page — not a flush-table recipe.
- CRUD Page Patterns and Related List link here; Region Table Toolbar diagrams do not teach a
border-bunder the title. - /showcases/orders-detail-related: in-card groups muted; Attachments / Activity semibold; same
p-6+gap-4; Demo status / Columns / Add allh-8. - /showcases/orders-detail: Line items title matches Attachments weight; Notes/addresses vertical.
- /showcases/orders-detail-basic: one card, muted groups,
Separatorbetween groups,gap-4. - /showcases/approvals-detail: persona Alert is a body sibling; cards are direct
PageContainerchildren; noDetailCardgap-*override. - Related List stays on the padded shell;
bodyPadding="none"is escape hatch only. - No Object Page
Descriptions bordered; no title-rowsize="sm".
See Also
- Page Sections — cross-page shell, title ladder, and rhythm
- CRUD Page Patterns — Body Card Granularity, Descriptions layout, Region Table Toolbar
- Related List
- Sentence Case
- Button And Action Emphasis
- Page And Region Status
Page Sections
One rule set for every page body — when to wrap a region in a card, where its title lives, and how much air separates it from everything else.
Object Messaging And Table Chrome
Reusable decisions for Message Popover, validation surfaces, cell value state, and region table toolbars — distilled from enterprise patterns.