@gridui/disclosure
Core family. Public API: Disclosure, DisclosureProps, DisclosureSpace, DisclosureSpacing.
Import components from @gridui/disclosure and load @gridui/disclosure/styles.css after Theme.
Load styles for any composed dependency used by this family. Applications own routes, labels, state persistence, and branding.
See component ownership and release history.
Disclosure
Use summaryClassName to customize the summary and contentClassName to arrange
the body through public APIs. summaryProps forwards native summary attributes,
events, and refs for focus management or accessible relationships. label owns
its content; children, dangerouslySetInnerHTML, and className are excluded
from summaryProps. Summary classes continue to merge through summaryClassName.
Native toggle behavior and responsive padding remain unchanged.
Disclosure is the shared native details/summary control for block sections
and compact table-cell information. It forwards native details props and refs,
including open, name, and onToggle, without adding hydration or replacing
native keyboard behavior. Its label supplies the summary; put links and other
interactive content in the body, not inside the label.
The root and summary fill their containing content width. The summary has a
2.75rem minimum height and uses the local link color and shared focus treatment.
Only the summary toggles the disclosure; body links retain their own actions.
There are no outer margins, borders, fixed heights, or overflow clipping.
The body does not impose typography, color, a grid, or a separator. Callers
compose their own Stack, Grid, source text, or DataTable inside it. The root uses
type-body; use className="type-data" or type-supporting for compact contexts.
| Prop | Default | Responsibility |
|---|---|---|
summaryPaddingBlock | 24 | Clickable space above and below the label |
summaryPaddingInline | 24 | Clickable space on both inline sides |
contentPadding | 24 | Body inset; 0 allows edge-aligned tables and grids |
Each accepts the theme spacing scale 0 / 4 / 8 / 16 / 24 / 32 / 48 / 64, or
{ base, md?, lg? } at the shared 40rem and 64rem breakpoints. Omitted sizes
inherit the preceding breakpoint. Compact cells normally use block 8, inline 0,
and content 0 because the table already owns cell padding. The minimum summary
height still applies when padding is zero. A closed body contributes no height.
Keep Disclosure inside a direct GridItem for framed grids and fillLastRow.
Use GridItem span="full" to span all columns. Grid owns cell backgrounds and
seams; Disclosure owns only its internal spacing. Opening one item increases the
row height but does not stretch neighboring closed summaries or synchronize
open state. Native name can opt into mutually exclusive groups when needed.
A DataTable inside the body keeps its own horizontal scroll region. In a table
cell, Disclosure fills the cell's content box and long text wraps; expansion can
change the row height and the browser's automatic column allocation. It cannot
span sibling cells or render a whole-table detail row. Do not introduce invalid
details children directly under table or tr, or wrap a block disclosure in
phrasing-only elements such as span or small.
Shared heading rows and body layout
The default Disclosure remains a block. When a parent arrangement must place an
independent link or control beside the summary, flatten the details box with
className="contents". Style the body through contentClassName so it occupies
its own full-width row; no private descendant selector is needed:
<div className="grid grid-cols-[minmax(0,1fr)_auto] items-start gap-gridui-16">
<a href="/index/">Index</a>
<Disclosure
label="Sources"
className="contents"
summaryClassName="w-auto justify-self-end"
summaryProps={{ id: "sources-summary", "aria-controls": "sources-list" }}
contentClassName="col-span-full"
summaryPaddingBlock={8}
summaryPaddingInline={0}
contentPadding={0}
>
<ul id="sources-list">
<li><a href="/guide/">Guide</a></li>
</ul>
</Disclosure>
</div>
The native DOM remains details > summary + div; the public body is the parent
grid item and retains a valid list inside it. The component flattens the browser's
::details-content wrapper where supported so that it does not intercept the
body's grid placement. A separately imported Grid can use
its own asChild API around that ul, with GridItem composing each native li.
Disclosure does not require an asChild mode or replace its semantic root.
Keep independent header actions outside the summary so they retain their own
activation behavior.
Callers may also flatten the body with contentClassName="contents"; its children
then own their placement and spacing because a contents box has no padding area.
Closed bodies remain hidden even when the details or body uses contents or a later
stylesheet supplies a display utility. Native closed-content visibility also
preserves browser search; the component does not replace it with display: none.
The wrapper is flattened only while open, without JavaScript, effects, transition
timing, or motion-preference exceptions.
For application-owned focus restoration, pass a native summary ref through
summaryProps={{ ref: summaryRef }}. The ref targets the actual focusable summary;
Disclosure does not add focus policy or query an internal descendant for callers.
name and open remain native details props, and onToggle remains the native
details event.
Verification
The UI browser fixtures verify shared heading rows, flattened closed bodies,
native summary refs, Task composition, SSR without JavaScript, responsive
spacing, full summary width, multi-column
rows, span="full", fillLastRow, independent expansion, wide nested tables,
nested grids, table-cell wrapping, keyboard activation, focus, and clicks in the
summary's trailing whitespace across the viewport/appearance/motion matrix.