13 - Section Anatomy
The inside of a template. 03-layout.md says what archetype a page is. 11-templates-catalog.md says which template within the archetype. This file says what’s inside the template — the named areas, the required elements per area, and the canonical row patterns for presenting different kinds of information within a section.
Scope rule. When a behavior is already covered in 04-components.md (per-primitive rules), 05-patterns.md (cross-cutting UI patterns), or 03-layout.md (page-level layout), this file points there. New anatomy lives only for the within-template structure.
Prereqs: 03-layout.md, 04-components.md, 11-templates-catalog.md.
How to read this doc
- Area = a named region within a template (page bar, toolbar, main content, action bar, right panel, sub-toolbar).
- Section = a labeled group inside an area (a
<FormSection>,<ContentSection>, or right-panel<CollapsibleSection>). - Row = a single item inside a section (one setting, one list entry, one property, one activity event).
Every template below specifies its areas in a table — name, purpose, required elements, optional elements. Then the second half of this file is a row-pattern catalog: every canonical way to present a single piece of information.
Per-template area tables
Type 1 — List page (default)
The most common page in any app — /agents, /tools, /review, /audit.
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Page bar | Identity, primary create action, scope of the list | <ListPageBar title> + entity-plural title | <SearchInput> in center slot · primary ”+ New …” action right slot · <ViewSwitcher> (only on task/flow screens) |
| Content toolbar | Filters, secondary search, bulk-action surface | None — omit if there’s nothing to filter | <FilterChips> · <SearchInput> · sort dropdown · “N selected” + clear (when bulk-active) |
| Main | The actual collection | One of: <DataTable> (default) · <CardGrid> (gallery) · <BoardView> (kanban) · calendar/timeline/map per template | Empty-state variant (zero rows) · loading-state skeleton |
| Footer | Pagination + footer aggregates | <TablePagination> when N > page size | Per-column footer aggregates (“Total: 1,247”, “Avg: 4.2”) — Attio pattern, not yet in <DataTable> (open issue) |
| Action bar | None on a List page | — | — |
Forbidden in List. No <ActionBar> (List page has no commit). No persistent right panel (use 3c Drawer if you need one).
A — Dashboard
Landing-page summary. KPI strip on top, charts in middle, detail table at bottom.
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Page bar | Title + scope (date range) | <ListPageBar title="Dashboard"> | <DateRangePicker> in center · “Export” outline button right slot |
| Metrics row | At-a-glance KPIs — the user reads this before anything else | <CardGrid columns={4}> of <MetricCard> (label / big number / trend) · 3–6 cards · equal heights | Trend arrow + delta % · sparkline |
| Charts row | Time-series and breakdowns | None — omit if dashboard is metrics-only | <CardGrid columns={2}> of chart cards · max 4 charts before splitting |
| Detail table | Most-recent activity / drilldown rows | <ContentSection> + <DataTable> if dashboard data has rows behind it | ”View all” link to a List page |
Required ordering. Metrics → Charts → Tables, top to bottom. Reordering breaks the “scan → analyze → drill” reading rhythm.
B — Detail 3a Split (record with activity)
Canonical record detail. Left = activity feed; right = persistent property panel. Used on /review/:id, future CRM contact, future agent run detail.
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Page bar | Entity identity, navigation, page-level actions | <DetailPageBar backTo title basePath tabs> · back button to parent list · entity name as title | subtitle (entity description, ≤80 char) · titleEditable / subtitleEditable for inline-edit · primary action right slot |
| Highlights row | Key stats / status pills above the activity feed | None — optional even on 3a Split | <CardGrid columns={3}> of <HighlightCard> (the ONLY cards on this page; max 6) |
| Main — activity feed | Reverse-chron timeline of events, comments, status changes | <ContentSection title="Activity"> · timeline rows (see Activity row patterns below) | Filter chips above the feed (event type) · pagination at bottom (older entries on demand) |
| Main — notes | Free-form notes attached to the record | None — optional | <ContentSection title="Notes" actions={<Button>+</Button>}> · note rows |
| Right panel — details | Static metadata — owner, status, dates, custom fields | <RightPanel> with <CollapsibleSection title="Record Details" defaultOpen> containing <PropertyList> of <PropertyRow> · “Show all values” toggle when ≥ 6 rows | Multiple sections (Details, Tags, Lists, Related) · per-section actions slot |
| Right panel — comments | Threaded comments tied to the record (not the same as activity feed) | None — optional second tab in the panel | Tab bar at top of <RightPanel> switching Details ↔ Comments |
| Action bar | None — 3a Split commits inline (per-property via <InlineEditField>) | — | — |
Hard rules.
- Right panel is the second allowed scroll zone (the only exception to “only
<PageContent>scrolls”). Each side scrolls independently. - Right panel content is flat — no
<Card>, no border wrapper. Use<PropertyList>inside<CollapsibleSection>. - Highlights are the only cards on this page.
3b — Detail Tabbed full-page
Used by /agents/:id/*. Each tab is a self-contained surface with its own template.
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Page bar | Entity identity + tab bar | <DetailPageBar tabs> with ≤ 8 tabs · text-only tab labels · 2px underline active state | titleEditable / subtitleEditable per parent layout doc · primary action right slot |
| Tab content (Outlet) | Fully owned by the active tab — different shape per tab | <Outlet /> rendering the per-tab template | Sub-toolbar at L4 if a tab needs to switch between equivalent views (per 03-layout.md § Exemptions § Intra-tab sub-navigation) |
| Action bar | Optional — per-tab choice | None at the layout level | Per-tab <ActionBar> for Form-shaped tabs (Settings, Billing) |
Hard rules.
- Tab order is frequency-first (daily-touched leftmost, monthly rightmost).
- Default tab path is
''(empty path) — don’t use/overviewor/details. - Layout itself owns no
<ActionBar>— each tab decides.
3c — Detail Drawer
The list stays primary; clicking a row opens a slide-over panel. Used for triage (notification inbox, audit log entries).
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Drawer header | Drawer title + ✕ | <SheetHeader> with <SheetTitle> · close button | Per-record actions in the header (mark read, archive) |
| Drawer body | Record content — read-mostly, dense | One of: read-only detail layout · single short form · property list | Scroll within the drawer |
| Drawer footer | Per-record commit actions | None — most drawers are read-only | Footer with action buttons |
Hard rule. Drawer content is ephemeral. URL is backed by ?id=<x> query param so closing the drawer clears it. If the content deserves its own URL and full viewport, promote to 3a Split or 3b Tabbed.
D — Settings (Form, single column)
The dominant template across nx-agent. The example the user called out — many distinct settings, each with its own presentation.
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Page bar | Title (for standalone settings) OR slim breadcrumb (for tab settings) | Standalone: <H1> (24px) inside content + slim <ListPageBar>. Tab: <DetailPageBar> already provides identity. | ”Save” button NEVER goes here — Save lives in the action bar |
| Page content (narrow) | The form itself — single column, max-w-2xl centered | <PageContent narrow> · space-y-10 between sections (40px gap, NOT 24px) | — |
| Section | Logical group of related settings | <FormSection title description> · description is one sentence per Attio convention | Section-level ”+” or “Reset” outline button (zone ②, never primary) |
| Row | One setting — see Row patterns catalog below | One of the 15 canonical row patterns (Toggle / Slider / Input / Select / etc.) | Inline <Alert variant="warning"> if a precondition is missing (e.g., “Read KB” toggled but no docs) |
| Advanced sub-section | Rarely-touched settings hidden by default | None — collapse only if the section grows past 6 rows | <CollapsibleSection title="Advanced" defaultOpen={false}> |
| Danger zone | Catastrophic actions, always last on the page | None — only present if the entity is deletable | Red-tinted <Card> (the one card-allowed exception per page) · destructive button + warning copy |
| Action bar | Save / Reset commit | <ActionBar> as sibling of <PageContent> (NOT inside) · <Button>Cancel</Button> left · <ActionButton>Save</ActionButton> right | Compound action button (“Save and reload agent”) to the left of Save |
Hard rules.
- Section gap is
space-y-10(40px). Spec violation ifspace-y-6(24px) per 03-layout.md § Template D. <Separator>between sections is secondary to whitespace — Attio prefers whitespace-only separation. Use<Separator>only when whitespace alone reads ambiguous.- Page-bar action variant:
outline size="sm"ONLY. Never primary. Save belongs in the action bar. - Danger zone is always last. One per page. Red-tinted card.
E — Split form (list-nav + per-item config)
Used when a tab has a list of items (tools, KB docs, channels) and the user picks one to configure.
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Sidebar (left, ~30%) | Selection only — list of items | <SplitContent sidebar> · row per item with hover state · selected row marked | Search / filter <ContentToolbar> above the list |
| Main (right, ~70%) | Configuration form for the selected item | <PageContent narrow> inside the main slot · same Form anatomy as Template D | Empty state if nothing selected (“Select an item to configure”) |
| Action bar | Per-item Save / Reset | <ActionBar> if form-mode (commit-together fields) | OMIT if every field is independently savable via <InlineEditField> |
Hard rules.
- Sidebar is selection only. No bulk actions in the sidebar; bulk goes in
<ContentToolbar>above. - Right side is flat — no nested cards, no right-panel-inside-right-side.
- Both sides scroll independently — exception to “only PageContent scrolls” is permitted because this is the sidebar-as-nav pattern.
F — Focus wizard
Multi-step linear flow. Sidebar stays visible, body takes over the main pane.
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Header row 1 | Close + breadcrumb (h-12) | ✕ close button (top-left) · breadcrumb showing “Parent · Step destination” | — |
| Header row 2 | Stepper (h-12, separate row) | <Stepper current total> · linear, numbered ① ② ③ ④ · future steps muted | — |
| Body | Step content, centered, max-width-constrained | <div className="mx-auto max-w-2xl py-12"> containing one or more <FormSection> · bg-background (no tint) | Help text · skip-this-step toggle for optional steps |
| Sticky bottom action bar | Step navigation | <Button variant="ghost">Cancel</Button> left · <Button variant="outline">Previous</Button> middle · <Button>Next</Button> (or “Finish” / outcome verb on last step) right | ”Skip” link for optional steps |
Hard rules.
- Mount inside
AppLayout(sidebar visible). Never overlay the shell. - Two-row header. Cramming stepper + close + breadcrumb into one row is forbidden.
- Body bg is
bg-background(same as the rest of the app). No tinted body — focus comes from structure, not color.
G7 — Bulk Import wizard (specialization of F)
Inherits all F areas + a fixed step sequence:
| Step | Body content | Required |
|---|---|---|
| Upload | File dropzone + format picker (CSV / JSON / Excel) | <FileDropZone> · accepted-format hint |
| Map | Two columns — source columns (left) → target fields (right) · drag or pick | List of source columns · target fields with type hints · “Required” marker on must-map fields |
| Preview | First 10 rows rendered as <DataTable> with validation highlights | Per-row error / warning indicator · “N rows have errors” counter |
| Confirm | Summary: “Import N rows into Y. Errors: Z (skipped). [Start import]" | "Start import” outcome verb on the action bar |
| Result | Outcome page — success count + warning drill-down + error drill-down | ”Close” or “View imported items” actions |
G8 — Permissions matrix (specialization of D)
Inherits all D areas + one canonical section content:
| Element | Purpose | Required |
|---|---|---|
| Matrix header | Roles across the top (sticky) | <TableHeader> with one <TableHead> per role |
| Matrix rows | One row per resource, cells = checkbox or scope dropdown | <TableRow> with resource name in first cell + <Checkbox> (or <Select> for scope) per role cell |
| Reset to default | Section-header outline action | <Button variant="outline" size="sm">Reset to default</Button> in <FormSection actions> |
| Action bar | Sticky Save | Inherits Template D action bar |
Mobile. Matrix degrades to per-role accordion below md — each role becomes an expandable section listing its resources. Side-by-side roles forbidden below md.
G9 — Audit Log (specialization of Type 1 List)
Inherits all Type 1 areas + dense-row override + 3c Drawer for detail:
| Element | Difference from default |
|---|---|
| Row height | 28px (dense), not 32-40px |
| Primary action | None in page bar — audit is read-only. “Export” lives in <ContentToolbar> |
| Row click | Opens 3c Drawer with full JSON payload pretty-printed in <pre className="font-mono text-xs"> |
| Empty state | ”No events match these filters” + “Clear filters” — never the blank-slate variant |
G10 — Node-Graph Canvas
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Toolbar (top) | Zoom · Undo / Redo · Run · Publish | h-12 shrink-0 toolbar with action buttons | ”Saved 2s ago” auto-save indicator |
| Left palette | Draggable node types | Vertical list of node tiles · grouped by category · w-56 | Search field · pinned recent nodes |
| Canvas (center) | Infinite pan/zoom canvas with nodes | react-flow / xyflow surface · node + edge rendering | ”Drag a node to begin” hint when empty (do NOT use <EmptyState> here) |
| Right inspector | Selected-node configuration | w-80 panel · only mounts when something is selected | Tab bar (Properties / Logs / History) |
| Minimap | Bird’s-eye view bottom-right | None — optional | Bottom-right corner overlay |
Hard rules.
- No
<PageContent>— canvas owns the full body below the toolbar. - Save model is C (auto-save 1–2 s debounce). Show “Saved Ns ago” in the toolbar.
- Read-only mobile fallback below
md.
G11 — Editor + Preview
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Page bar | Title + Run | <DetailPageBar> · “Run” primary action right slot | Save indicator (Model C auto-save) |
| Editor (left) | Authoring surface — code or prompt editor | <SplitContent sidebarWidth="50%"> left side · Monaco / PromptEditor | Variables panel above the editor |
| Preview (right) | Live result of the editor’s saved content | Right side of <SplitContent> · renders the latest saved state (not unsaved keystrokes) | Loading state during run |
Hard rule. Preview reflects the latest saved content, not unsaved keystrokes. Otherwise it flickers and the user can’t tell what’s deployed.
G12 — Auth (Standalone, exempt from AppLayout)
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Centered card | The whole page is one centered card | <Card className="w-[400px]"> · brand logo top · <H2> page title · form · footer | ”Continue with SSO” button + <Separator> divider |
| Form fields | Email, password, or 6-digit code | Stacked input rows · full-width primary submit button (the one card-allowed exception) | Show/hide password toggle |
| Footer | Legal / forgot password / SSO link | None | Small muted links |
Hard rule. Full-width buttons are allowed inside the auth card (one of the four card-allowed contexts). Outside auth, full-width buttons remain forbidden.
G13 — Command Palette (Overlay)
| Area | Purpose | Required | Optional |
|---|---|---|---|
| Modal shell | Centered modal, top-third of viewport | Portal-rendered overlay · max-w-2xl · backdrop dim | — |
| Search input | Auto-focused on open | Single input at top · placeholder “Search pages, records, actions…” | Clear button (X) when text typed |
| Result groups | Grouped results — Recents, Pages, Records, Actions | Each group has a small muted heading · keyboard-selectable rows | Per-group result limits (“Show all 12 records →“) |
| Footer hints | Keyboard legend | ”↑↓ navigate · ↵ select · esc close" | "⌘K to close” hint |
Hard rules.
- Keyboard-only flow. Mouse works but is slower; never add per-row icons.
- No destructive actions in the palette (no Delete, no Archive). Open the record first.
Section types and their inner anatomy
Sections are the labeled groups inside areas. Each section type has its own internal anatomy.
<FormSection> (D, E, F)
┌────────────────────────────────────────┐│ Section title (H4 14px, semibold 600) │ ← required│ Section description (12px muted) │ ← required for Attio Settings│ │ ← 24px gap to first row│ [row 1] │ ← row patterns below│ [row 2] ││ … │└────────────────────────────────────────┘- Title. Required. One concept per section (“Voice”, not “Voice & generation”).
- Description. Required on Settings (Attio convention). One sentence stating what the section governs. NOT optional — every Settings FormSection has one.
- Field gap.
space-y-4(16px) between rows. - Section gap.
space-y-10(40px) between FormSections in the parent stack. NOTspace-y-6. - Forbidden.
<Card>wrapping a<FormSection>. Multiple titles per section.
<ContentSection> (List, Detail, Dashboard)
┌────────────────────────────────────────┐│ Section title (H3 16px, semibold) │ Section actions → │ ← required│ │ ││ [section content — table, grid, etc] │└────────────────────────────────────────┘- Title. Required.
- Actions. Optional.
outline size="sm"only — never primary (primary belongs in page bar). - Use cases. Wrapping a
<DataTable>with a section title; wrapping a<CardGrid>with “Recent activity” header; wrapping the activity feed in 3a Split.
<CollapsibleSection> (Settings advanced, RightPanel groups)
┌────────────────────────────────────────┐│ ▾ Section title [actions →] │ ← chevron-clickable├────────────────────────────────────────┤ ← only when open│ [content] │└────────────────────────────────────────┘- State persists in localStorage per
(pathname, sectionId). - Use cases. “Advanced” settings collapsed by default. RightPanel property groups. Variables sub-section in AgentTab.
- Default open.
defaultOpen={true}on first visit if the section is core;defaultOpen={false}for Advanced / rarely-touched.
<RightPanel> content
[Tabs: Details | Comments] ← optional tab bar┌────────────────────────────────────────┐│ ▾ Record Details │ CollapsibleSection│ icon · label │ value │ PropertyRow│ icon · label │ value ││ icon · label │ [chip] [chip] ││ ▸ Show all values │ ShowAllValuesToggle when ≥6 rows├────────────────────────────────────────┤│ ▾ Tags │ CollapsibleSection│ [chip] [chip] [chip] │└────────────────────────────────────────┘- Flat throughout — no
<Card>wrappers anywhere in the panel. - Tab bar at top is optional; shown when the panel has multiple distinct content modes.
Row patterns catalog
The canonical ways to present a single piece of information within a section. The user’s specific question — “in Attio settings page each setting has a dedicated way to present” — is answered by the Settings rows subsection below.
Settings rows (used in D, E, F-step bodies)
S1 — Toggle row
For boolean settings. Most common Settings row.
Codified primitive: <ToggleRow>. Use the primitive — don’t hand-roll the markup below.
┌────────────────────────────────────────┐│ Read knowledge base [⚪]│ ← Label left, Switch right└────────────────────────────────────────┘<div className="flex items-center justify-between"> <Label htmlFor="kb-toggle">Read knowledge base</Label> <Switch id="kb-toggle" checked={…} onCheckedChange={…} /></div>When. Boolean, instant-apply (Save Model B). Description below label. Optional — add when the consequence isn’t obvious from the label.
S2 — Toggle row with sub-disclosure
Toggle + when on, reveal nested settings that only matter if the parent is on.
┌────────────────────────────────────────┐│ Email notifications [🟢]││ ┌─ when on ───────────────────────┐ ││ │ Frequency [Select ▾] │ ││ │ Include digest [⚪]│ ││ └────────────────────────────────┘ │└────────────────────────────────────────┘<> <div className="flex items-center justify-between"> <Label htmlFor="notif">Email notifications</Label> <Switch id="notif" checked={enabled} onCheckedChange={setEnabled} /> </div> {enabled && <div className="mt-4 ml-6 space-y-4 border-l pl-4">{/* nested rows */}</div>}</>When. The nested settings are meaningless if the parent is off. Indent. 24px left margin + left border for visual coupling.
S3 — Stacked input row
Free-text single-line value.
┌────────────────────────────────────────┐│ Display name │ ← Label│ [_________________________________] │ ← Input│ Shown on the agent card. │ ← Help text (optional)└────────────────────────────────────────┘<div className="space-y-1.5"> <Label htmlFor="name">Display name</Label> <Input id="name" value={…} onChange={…} /> <p className="text-muted-foreground text-xs">Shown on the agent card.</p></div>When. Free-text single-line value. Email, URL, name. Help text. Optional. When present, sits below the input. Never use placeholder as label.
S4 — Stacked textarea row
Same as S3 but multi-line.
<div className="space-y-1.5"> <Label htmlFor="bio">Bio</Label> <Textarea id="bio" rows={4} value={…} onChange={…} /></div>S5 — Select row (1-of-N, N ≥ 4)
Codified primitive: <SelectRow>. Use the primitive — don’t hand-roll. The primitive handles the Attio quiet-label convention by default.
┌────────────────────────────────────────┐│ Language ││ [Vietnamese (vi-VN) ▾] │└────────────────────────────────────────┘<div className="space-y-1.5"> <Label className="text-muted-foreground text-xs">Language</Label> <Select value={…} onValueChange={…}> <SelectTrigger><SelectValue placeholder="Choose a language" /></SelectTrigger> <SelectContent>{/* options */}</SelectContent> </Select></div>When. 1-of-N selection where N ≥ 4 OR the option list is dynamic.
Placeholder copy. “Choose a …” not ”— select —”.
Label class is text-muted-foreground text-xs when stacked above a Select (matches Attio Settings convention).
S6 — Radio row (1-of-N, N ≤ 4)
┌────────────────────────────────────────┐│ Theme ││ ◉ Light ││ ○ Dark ││ ○ System │└────────────────────────────────────────┘When. 1-of-N where N ≤ 4 AND the user benefits from seeing all options at once.
S7 — Slider row
For continuous numeric values.
Codified primitive: <SliderRow>. The primitive sets aria-valuetext automatically from formatValue + ariaUnit — required a11y comes for free.
┌────────────────────────────────────────┐│ Speaking rate 1.20× │ ← Label + value display│ ────●─────────────────────── │ ← Slider full-width└────────────────────────────────────────┘<div className="space-y-2"> <div className="flex justify-between text-sm"> <Label>Speaking rate</Label> <span className="font-medium tabular-nums">{value.toFixed(2)}×</span> </div> <Slider min={0.25} max={4} step={0.05} value={[value]} onValueChange={…} aria-valuetext={`${value.toFixed(2)}× speed`} /></div>When. Continuous numeric (volume, temperature, threshold).
Value display. Right-aligned, font-medium tabular-nums, with unit suffix if applicable (“dB”, ”×”, ”%”).
Required a11y. aria-valuetext so screen readers hear “1.20× speed”, not “1.2”.
S8 — Number input row
For discrete numeric (integer counts).
Codified primitive: <NumberInputRow>. Optional suffix prop for units (tokens, ms, MB).
┌────────────────────────────────────────┐│ Top-K results ││ [10 ] │└────────────────────────────────────────┘<div className="space-y-1"> <Label htmlFor="top-k">Top-K results</Label> <Input id="top-k" type="number" value={…} onChange={…} /></div>When. Discrete numeric. Counts, IDs. No slider. Sliders are for continuous; counts use Input type=number.
S9 — Avatar / upload row
Profile picture, agent icon, branded asset.
┌────────────────────────────────────────┐│ ⚪ Avatar ││ ⚪ [Upload] PNG, JPG · max 2MB │└────────────────────────────────────────┘<div className="flex items-center gap-4"> <Avatar className="size-16"><AvatarImage src={…} /><AvatarFallback>{…}</AvatarFallback></Avatar> <div className="space-y-1"> <Button variant="outline" size="sm" onClick={…}>Upload</Button> <p className="text-muted-foreground text-xs">PNG, JPG · max 2MB</p> </div></div>When. Image / avatar setting. Layout. Side-by-side: preview left, upload + constraints right.
S10 — Action row
A “setting” that’s actually a button — performs an operation, doesn’t store a value.
Codified primitive: <ActionRow>. Optional status slot for <MutationStatus> rendered below.
┌────────────────────────────────────────┐│ Reset to defaults ││ Restores Voice and Model to factory…│ [Reset]│ │└────────────────────────────────────────┘<div className="flex items-start justify-between gap-4"> <div className="min-w-0 flex-1"> <Label>Reset to defaults</Label> <p className="text-muted-foreground text-xs mt-0.5"> Restores Voice and Model to factory values. </p> </div> <Button variant="outline" size="sm" onClick={…}>Reset</Button></div>When. The “setting” is an operation, not a stored value. “Regenerate API key”, “Reload runtime”, “Resync”.
Button variant. outline size="sm". Destructive variant + <ConfirmPopover> if irreversible.
S11 — Info / read-only row
Display a value the user can’t edit, often with a copy button.
┌────────────────────────────────────────┐│ Agent ID ││ agt_8a3f2c1d [📋 Copy] │└────────────────────────────────────────┘<div className="space-y-1"> <Label>Agent ID</Label> <div className="flex items-center gap-2"> <code className="text-sm font-mono">{id}</code> <Button variant="ghost" size="icon-xs" tooltip="Copy" onClick={…}> <Copy /> </Button> </div></div>When. System-set values, generated tokens, IDs.
Code style. font-mono text-sm.
S12 — Date / date-time row
┌────────────────────────────────────────┐│ Expires on ││ [Mar 15, 2027 📅] │└────────────────────────────────────────┘When. Scheduled tasks, expiration, planned events.
S13 — Color / swatch row
┌────────────────────────────────────────┐│ Brand color ││ [⚪][🔴][🟠][🟡][🟢][🔵][🟣][⚫] │ ← swatches as RadioGroup└────────────────────────────────────────┘When. Theme color, brand color, status accent. Use a RadioGroup of color swatches, NOT a free-form color picker (use a picker only when the brand requires arbitrary hex).
S14 — Secret / API key row
A read-only row with masking and regenerate.
┌────────────────────────────────────────┐│ API key ││ nxa_•••••••••••• [👁][📋][↻] │ ← reveal, copy, regenerate└────────────────────────────────────────┘When. Generated secret (API key, webhook signing key).
Default state. Masked. Reveal toggles via eye button. Regenerate is destructive — wrap in <ConfirmDialog>.
S15 — Danger row (in Danger Zone)
┌── Danger Zone (red-tinted card) ──────┐│ Delete agent ││ Permanently deletes this agent and … │ [Delete agent] ← destructive└────────────────────────────────────────┘<Card className="border-destructive/40"> <CardContent className="space-y-4"> <div className="flex items-start justify-between gap-4"> <div> <Label>Delete agent</Label> <p className="text-muted-foreground text-xs">Permanently deletes …</p> </div> <ConfirmDialog trigger={<Button variant="destructive" size="sm">Delete agent</Button>} title="Delete this agent?" description="…" confirmLabel="Delete" variant="destructive" onConfirm={…} /> </div> </CardContent></Card>When. Catastrophic actions. Always last on the page. One Danger Zone per page max.
List rows (Type 1, G1, G2 etc.)
L1 — Identity row (DataTable)
☐ [avatar] Name owner@… 2d ago [⋯]- Leading checkbox if bulk-selectable.
- Row hover:
hover:bg-muted/50. - Whole row clickable to open detail.
- Trailing actions: max 3 visible icon-xs ghost buttons, rest in
<DropdownMenu>.
L2 — Status row
Same as L1 + a status pill column.
☐ [avatar] Name [Active] owner@… 2d ago [⋯]- Status uses
<StatusBadge>or<Badge>with semantic variant.
L3 — Card row (Gallery G1)
┌────────────────┐│ [thumb] ││ Name ││ meta · meta │└────────────────┘- Card 240–320px wide. Hover reveals quick actions overlay.
RightPanel rows (B 3a Split)
R1 — Property row (read-only)
icon · Owner alice@example.com<PropertyRow icon={User} label="Owner"> alice@example.com</PropertyRow>R2 — Property row with InlineEditField
icon · Domain google.com ← click-to-edit<PropertyRow icon={Globe} label="Domain"> <InlineEditField value={domain} onCommit={save} type="url" /></PropertyRow>R3 — Property row with chips/tags
icon · Tags [Design] [System] [+ 2]R4 — Show-all-values toggle
▸ Show all values ← bottom of the PropertyList when ≥ 6 rows totalToolbar rows (above tables / lists)
T1 — Filter chip row
[+ Filter] [Status: Active ✕] [Owner: me ✕] … [Clear all]T2 — Search + filter + sort
[🔍 Search…] [Filter ▾] [Sort: Recent ▾]T3 — Bulk action toolbar
3 selected · [Clear] [Tag…] [Archive] [⋯ More]- Renders in place of T1/T2 when selection ≥ 1.
- Sticky on scroll.
Activity feed entries (B 3a Split main)
A1 — Status change
[avatar] alice changed Status to "Active" 2h agoA2 — Comment / note
[avatar] alice 3h ago Lorem ipsum dolor sit amet… [Reply] [Edit] [Delete]A3 — Email entry
[avatar] alice → bob · "Subject line" Yesterday Body preview… [Expand]A4 — System event
[icon] Tools updated 2d ago 5 tools added, 2 removed.Required-elements global rules
A summary of what every area MUST have, regardless of template:
- Page bar — title (entity-plural for List, entity-name for Detail, fixed string for Form/Dashboard). Max 1 primary action.
- Every section — one title (H3 or H4). On Settings, also one description sentence.
- Every interactive element —
<Label>(oraria-label) + accessible name. Icon-only buttons MUST havetooltiporaria-label. - Every list — empty state, loading skeleton, and error state. Three states; never fewer.
- Every form —
<ActionBar>as sibling of<PageContent>(NOT inside) + dirty-nav guard if there are unsaved changes worth protecting. - Every multi-step flow — confirm-on-discard on EVERY exit path (✕, Cancel, Esc, sidebar click). Silent no-ops are forbidden (06-flows.md § Exit-path close behavior).
- Every destructive action —
<ConfirmPopover>(routine) or<ConfirmDialog>(catastrophic). Neverwindow.confirm. - Every mutation — feedback per 05-patterns.md § Feedback. Inline
<ActionButton>for click-triggered, toast for background events. Never both.
If any of these are missing, the screen is wrong — even if it “looks fine”.
Composition example — AgentTab read against this doc
To make this concrete: the Settings tab at /agents/:id is Template D. Map it to row patterns:
| Section | Row | Pattern | Why |
|---|---|---|---|
| Voice | Language | S5 Select | 1-of-2 today, will grow → Select |
| Voice | Voice (specific) | S5 Select | 1-of-N dynamic |
| Voice | Speaking rate | S7 Slider | continuous 0.25–4× |
| Voice | Pitch | S7 Slider | continuous −20 to +20 |
| Voice | Volume | S7 Slider | continuous −10 to +10 dB |
| Voice | Test voice | S10 Action row | operation, not a stored value (collapsed into the section without label/desc shape since it’s the section’s outcome — minor variant) |
| Knowledge & tools | Read knowledge base | S1 Toggle | boolean |
| Knowledge & tools | Call external tools | S1 Toggle | boolean |
| Knowledge & tools | Top-K results | S8 Number input | discrete count |
| Knowledge & tools | Similarity threshold | S7 Slider | continuous 0–1 |
| Knowledge & tools | Reranking | S1 Toggle | boolean |
| Model | LLM model | S5 Select | 1-of-many |
| Model | Embedding model | S5 Select | 1-of-many |
| Model | Temperature | S7 Slider | continuous 0–1 |
| Model | Max tokens | S8 Number input | discrete count |
| Variables (collapsible) | each variable | L1 Identity row | listed in a sub-DataTable |
Each row has a defined pattern. No improvisation. That’s the discipline this doc codifies.
Next: jump back to 11-templates-catalog.md for “which template do I pick”, or 04-components.md for primitive APIs.