@k2b/ui
DetailPanel
Quiet contextual inspectors with compact identity, flat sections, and one scroll owner.
Full-width destinations keep navigation primary while a sibling overflow menu holds secondary commands. Disabled rows preserve unavailable resource context.
TSX
Copy<DetailPanel.Section title="Related resources" icon="ti ti-link"> <DetailPanel.Action href="/app/mail/inbox?conversation=Conv01" leading={<i class="ti ti-mail" aria-hidden="true" />} title="Release planning" description="Mail conversation" menuLabel="More actions for Release planning" menuItems={[{ label: "Unlink", icon: "ti ti-unlink", action: unlink }]} /> <DetailPanel.Action type="button" disabled leading={<i class="ti ti-file-off" aria-hidden="true" />} title="Archived brief" description="Resource unavailable or no longer accessible" /></DetailPanel.Section>Two production-shaped inspectors demonstrate the final contract: padded identity headers, one stable scroll gutter absorbed by the trailing host inset, related surfaces, compact data, and a panel-native comment thread.
Welcome!
Collaborative note
Contents
- H1
- H2
- H2
- H2
- H2
- H2
- H2
Comments
1- MK
Mara Klein The introduction is ready for review.
Online · 1
Info
- Created
- 07 Jul 2026
- Updated
- 07 Jul 2026
Versions
TSX
Copy<DetailPanel.Group label="Customer context"> <DetailPanel.Section title="Company" icon="ti ti-building" tone="accent"> <DescriptionList layout="rows" size="sm" items={companyItems} /> </DetailPanel.Section> <DetailPanel.Section title="Contact" icon="ti ti-user" tone="warning"> <DetailPanel.Action href="/contacts/ada" target="_blank" rel="noopener noreferrer" title="Ada Lovelace" menuLabel="More actions for Ada Lovelace" menuItems={[{ label: "Related requests", href: "/requests?contact=ada" }]} /> </DetailPanel.Section> <DetailPanel.Section title="Recent threads" icon="ti ti-history" tone="success">…</DetailPanel.Section></DetailPanel.Group> <Discussion label="Comments" icon="ti ti-messages" count={1}> <Discussion.List>…</Discussion.List> <Discussion.Composer label="Add comment" placeholder="Add a comment" submitLabel="Post comment" onSubmit={postComment} /></Discussion>DetailPanel gives contextual inspectors one quiet content structure without
owning their surrounding drawer, workspace region, dialog, or domain state.
Import
import { DescriptionList, DetailPanel } from "@k2b/ui";Use DetailPanel
Place it inside AppWorkspace.Detail for persistent selection context, or in
another host when the same inspector content is reused. The host owns opening,
closing, sizing, and persistence. The application owns data, permissions,
mutations, and which sections are present.
DetailPanel.Header keeps identity and actions in one compact region. Pass
icon for the standard accent-tinted identity tile or leading for an avatar
or another custom identity; they are mutually exclusive. Pass actions for
compact utilities such as more and close, and primaryActions for the small
set of prominent commands below the identity row. Optional metadata sits
beside the subtitle instead of competing with the title.
DetailPanel.Body is the single scrolling element and accepts a
scrollPreserveKey. Its stable scrollbar gutter prevents content from shifting
when expanding content first makes the panel overflow. Inside
AppWorkspace.Detail, that gutter occupies the host's existing trailing inset
instead of adding a second gap. Do not add a second full-height scroller inside
it.
Use DetailPanel.Summary once, directly below the header, when the selected
item has a primary set of facts or controls. Summary and grouped sections share
the same normal surface, while their structure still distinguishes the
primary overview from related context. Do not repeat the summary for every
group.
The panel uses --k2b-detail-panel-accent for restrained identity and action
accents, with the portable UI accent as its fallback. A host may map that hook
to its own theme token; DetailPanel does not know how the host derives it.
DetailPanel.Section is deliberately flat by default. It groups content
through spacing and a sentence-case title, not a card, divider, or decorative
background. Pass icon for a fixed section icon slot and tone to distinguish
portable accent, neutral, success, warning, or danger roles through
text color only. Pass description for short supporting context, meta for a
count or state, and actions for a normal section. A normal section may omit
its body to represent a compact, actionable empty group. Set collapsible for
secondary content; a collapsible section uses native details behavior and
therefore does not accept header actions.
Use DetailPanel.Group when one or more sections form one stable context, such
as a company and its contacts or a document and its derived metadata. Merge
adjacent sections when they belong to that same context. The group owns the
same normal surface as the summary and the one-pixel gaps between its sections;
sections outside a group stay flat. Pass label when the shared context benefits from an
accessible group name. Do not wrap every standalone section or manufacture
groups only for decoration.
Sections accept arbitrary content. Use DescriptionList layout="rows" for
compact properties, normal shared inputs for a full form inspector, and the
appropriate shared list, table, notice, preview, or editor for specialized
content. Use Discussion directly in the body when
notes or comments need their own labelled composer and author timeline. Do not
add domain variants such as record, mail, or workflow to DetailPanel.
Use DetailPanel.Action for a full-width destination or command such as a
related record, attachment, or in-panel jump. Pass href for native link
semantics; omit it for a native button. leading, title, optional
description, and trailing keep row geometry and interaction states
consistent. Action rows align their optional leading icon with the section
heading and keep their default label as quiet as supporting text. Hover and
keyboard focus change only the action text and icon to the panel's primary
color; the row and optional Dots menu trigger stay transparent. When a
destination has secondary actions, pass declarative
menuItems together with the required accessible menuLabel. The primary link
and the Dots trigger render as sibling controls, so the whole main row remains
a native destination without nesting a button inside the link. On fine-pointer
devices the Dots trigger appears on row hover, keyboard focus, or while its menu
is open; it remains visible on touch devices. Do not use it for static key-value
data, comments, history, or form fields.
Accessibility
The header title is an h2; normal section titles are labelled h3 headings.
Decorative header and section icons are hidden from assistive technology, so
their adjacent text remains the label and color is never the only signal.
Collapsible sections use a native summary with a visible focus indicator.
Every icon-only action and every control embedded in a description value still
needs its own accessible name. DetailPanel.Action keeps its visible title as
the accessible name and renders a real link or button.
Runtime
The composition is server-renderable. Collapsible sections work without client JavaScript. Interactive children keep their own hydration and state contracts.
Example
<AppWorkspace.Detail id="item" open={selectedId() !== null} width="md">
<DetailPanel>
<DetailPanel.Header
icon="ti ti-building-warehouse"
title="Studio shelf"
subtitle="Locations · version 2"
primaryActions={<Toolbar label="Location actions"><Button size="xs">Edit</Button></Toolbar>}
actions={<IconButton label="Close details"><i class="ti ti-x" aria-hidden="true" /></IconButton>}
/>
<DetailPanel.Body scrollPreserveKey="location-detail">
<DetailPanel.Summary title="Overview">
<DescriptionList
layout="rows"
size="sm"
actionVisibility="progressive"
items={[
{ term: "Room", description: "Studio" },
{ term: "Quantity", description: "18" },
]}
/>
</DetailPanel.Summary>
<DetailPanel.Section
title="Notes"
meta="0"
description="Keep decisions with this location"
actions={<Button variant="ghost" size="xs">Add note</Button>}
/>
<DetailPanel.Group label="Inventory context">
<DetailPanel.Section title="Related records" icon="ti ti-link" tone="accent">
<DetailPanel.Action
href="/app/grids/locations/records/st-02"
leading={<i class="ti ti-building-warehouse" aria-hidden="true" />}
title="Studio"
description="Room · ST-02"
trailing={<i class="ti ti-chevron-right" aria-hidden="true" />}
/>
</DetailPanel.Section>
<DetailPanel.Section title="Attachments" icon="ti ti-paperclip" tone="neutral" meta="2" />
</DetailPanel.Group>
<DetailPanel.Section title="History" collapsible>
…
</DetailPanel.Section>
</DetailPanel.Body>
</DetailPanel>
</AppWorkspace.Detail>