On this pageOverview
Disclosure
A toggle for showing and hiding content inline. Disclosure manages its own open/closed state and renders a button + panel pair. Use it for FAQs, accordions, and collapsible sections. For overlaying content in a floating panel, use Dialog or Popover instead.
For programmatic control in update functions, use Disclosure.toggle(model) and Disclosure.close(model) which return [Model, Commands] directly.
See it in an app
Check out how Disclosure is wired up in a real Foldkit app.
Pass buttonContent and panelContent directly — Disclosure handles the wrapper, ARIA linking, and toggle behavior. Style the button and panel with className or attributes props.
// Pseudocode walkthrough of the Foldkit integration points. Each labeled
// block below is an excerpt — fit them into your own Model, init, Message,
// update, and view definitions.
import { Effect } from 'effect'
import { Command, Ui } from 'foldkit'
import { m } from 'foldkit/message'
import { evo } from 'foldkit/struct'
import { p, span } from './html'
// Add a field to your Model for the Disclosure Submodel:
const Model = S.Struct({
disclosure: Ui.Disclosure.Model,
// ...your other fields
})
// In your init function, initialize the Disclosure Submodel with a unique id:
const init = () => [
{
disclosure: Ui.Disclosure.init({ id: 'faq-1' }),
// ...your other fields
},
[],
]
// Embed the Disclosure Message in your parent Message:
const GotDisclosureMessage = m('GotDisclosureMessage', {
message: Ui.Disclosure.Message,
})
// Inside your update function's M.tagsExhaustive({...}), delegate to Disclosure.update:
GotDisclosureMessage: ({ message }) => {
const [nextDisclosure, commands] = Ui.Disclosure.update(
model.disclosure,
message,
)
return [
// Merge the next state into your Model:
evo(model, { disclosure: () => nextDisclosure }),
// Forward the Submodel's Commands through your parent Message:
commands.map(
Command.mapEffect(
Effect.map(message => GotDisclosureMessage({ message })),
),
),
]
}
// Inside your view function, render the disclosure:
Ui.Disclosure.view({
model: model.disclosure,
toParentMessage: message => GotDisclosureMessage({ message }),
buttonContent: span([], ['What is Foldkit?']),
panelContent: p([], ['A functional UI framework built on Effect-TS.']),
buttonClassName:
'flex items-center justify-between w-full p-4 border rounded-lg data-[open]:rounded-b-none',
panelClassName: 'p-4 border-x border-b rounded-b-lg',
})Use the data-open attribute to style the button and panel differently when open. A common pattern is rotating a chevron icon and changing border radius: data-[open]:rounded-b-none on the button, rounded-b-lg on the panel.
| Attribute | Condition |
|---|---|
data-open | Present on both button and panel when the disclosure is open. |
data-disabled | Present on the button when isDisabled is true. |
| Key | Description |
|---|---|
| Enter | Toggles the disclosure. |
| Space | Toggles the disclosure. |
The toggle button receives aria-expanded and aria-controls linking to the panel. When the disclosure closes, focus is returned to the toggle button automatically.
Configuration object passed to Disclosure.init().
| Name | Type | Default | Description |
|---|---|---|---|
id | string | - | Unique ID for the disclosure instance. |
isOpen | boolean | false | Initial open/closed state. |
Configuration object passed to Disclosure.view().
| Name | Type | Default | Description |
|---|---|---|---|
model | Disclosure.Model | - | The disclosure state from your parent Model. |
toParentMessage | (childMessage: Disclosure.Message) => ParentMessage | - | Wraps Disclosure Messages in your parent Message type for Submodel delegation. |
buttonContent | Html | - | Content rendered inside the toggle button. |
panelContent | Html | - | Content rendered inside the disclosure panel. |
buttonClassName | string | - | CSS class for the toggle button. |
buttonAttributes | ReadonlyArray<Attribute<Message>> | - | Additional attributes for the toggle button. |
panelClassName | string | - | CSS class for the panel. |
panelAttributes | ReadonlyArray<Attribute<Message>> | - | Additional attributes for the panel. |
isDisabled | boolean | false | Whether the toggle button is disabled. |
persistPanel | boolean | false | When true, keeps the panel in the DOM when closed (with the hidden attribute) instead of removing it. |
onToggled | () => Message | - | Optional domain-event handler fired when toggled, as an alternative to Submodel delegation. |
buttonElement | TagName | 'button' | The HTML element to use for the toggle. |
panelElement | TagName | 'div' | The HTML element to use for the panel. |
className | string | - | CSS class for the wrapper element. |
attributes | ReadonlyArray<Attribute<Message>> | - | Additional attributes for the wrapper element. |