Disclosure
Detail that is usually not wanted.
Why it exists
Built on native <details> and <summary> for the same reason Dialog is built on <dialog> — the browser already does the hard parts: toggling, the aria-expanded state, Enter and Space, and the one a hand-rolled version always loses. Ctrl+F finds text inside a closed <details> and opens it. A useState and a conditional render is not on the page at all as far as the browser's own search is concerned.
Raw payload4 fields
{
"id": "evt_9f2c4a7e",
"type": "message.delivered",
"recipient": "ops@example.co.id",
"provider": {
"name": "postmark",
"latencyMs": 412
}
}A stack, not an accordion
Deliberately no “only one open at a time”. Opening the thing you want would close the thing you were comparing it to, and comparing two payloads is most of why these are on the page.
Attempt 1failed · 4m ago
Connection reset by peer after 30s.
Attempt 2failed · 2m ago
550 5.1.1 recipient unknown.
Attempt 3delivered · just now
Accepted in 412ms.
Bare, inside something that already has a border
A table cell, a list row, a card that is already doing the framing.
Run failed at step 3.
Stack trace
at Object.dispatch (/app/src/lib/queue.ts:88:11)
In both themes
light
Closed by defaulthover the row
The body.
dark
Closed by defaulthover the row
The body.
Guidance
Use it when
- A payload, a stack trace, an audit diff — detail a minority of readers want and all of them need occasionally.
- open on the item that is the reason the page was opened.
- meta for the fact that decides whether to expand: a count, a status, a time.
Reach for something else when
- Content most people need. A disclosure is a bet that most readers will not open it, and hiding something essential to save vertical space loses that bet on every page load.
- Navigation. A summary that goes somewhere when clicked is a link pretending to be a toggle.
- Nesting more than one level. The second chevron is where people stop following.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| summary | ReactNode | — | The always-visible line. |
| meta | ReactNode | — | A count, a badge, a timestamp — right-aligned in the summary row. |
| bare | boolean | false | Drops the border and padding, for a disclosure inside something that already has them. |
| open | boolean | — | Native. Open on first render. |
Accessibility
Everything except the marker is the browser's. <summary> is a real tab stop with aria-expanded maintained for you — but removing the default list marker also removes the UA focus outline in some browsers, which is how a keyboard-only disclosure ends up invisible. The focus ring here is explicit for that reason, and the marker is replaced rather than merely hidden because Safari's default triangle is filled and Chrome's is not.