f-ui
Design

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 SectionCardp-6 padding, 16px semibold title in the shell's own title slot, default-height tools, table inset with gap-4.

Wrong

Attachments

FileUploaded
po.pdf2026-03-01

Right

Attachments

FileUploaded
po.pdf2026-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:

TokenValueWhat to do
Related List bodyPaddingdefaultShares card p-6. "none" (-mx-6 -mb-6) is escape hatch only — not the Table recipe.
Header summarycontent + extraContentDo not stuff a page Alert into the left content column beside Amount
Descriptions borderedNever inside a cardIt 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.

RoleWhereWhen
Card titleSectionCard / RelatedList title propAttachments, Activity, Line items
In-card groupSectionGroupHeading (muted)Contact, Fulfillment, Notes inside a shared card
Descriptions titleDo not pass on Object PageThe 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 SeparatorDo 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.

Bodylayout
Short scalars (Contact, Fulfillment, status, dates, money codes)horizontal
Long text (Notes, addresses, multi-line)vertical (or wider span)
Kit defaultvertical — do not flip
Page-header Amount / QuantityStatistic, 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.

RuleLock
HeightDefault h-8. Button, SelectTrigger, outline Columns, solid Add — same height
BanSelectTrigger size="sm" and Button size="sm" in this row
EmphasisTools ghost/outline; at most one solid (region Add)
LayoutHorizontal 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 / RelatedList with the title prop — 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-b under the title.
  • /showcases/orders-detail-related: in-card groups muted; Attachments / Activity semibold; same p-6 + gap-4; Demo status / Columns / Add all h-8.
  • /showcases/orders-detail: Line items title matches Attachments weight; Notes/addresses vertical.
  • /showcases/orders-detail-basic: one card, muted groups, Separator between groups, gap-4.
  • /showcases/approvals-detail: persona Alert is a body sibling; cards are direct PageContainer children; no DetailCard gap-* override.
  • Related List stays on the padded shell; bodyPadding="none" is escape hatch only.
  • No Object Page Descriptions bordered; no title-row size="sm".

See Also

On this page