Skip to contentSkip to navigationSkip to topbar
Figma
Star

Disclosure Primitive

Version 2.1.1GithubStorybook

An accessible disclosure primitive for controlling the visibility of a content section.


Component preview theme
function BasicExample() {
const disclosure = useDisclosurePrimitiveState({ visible: true });
return (
<>
<DisclosurePrimitive {...disclosure}>Toggle</DisclosurePrimitive>
<DisclosurePrimitiveContent {...disclosure}>Content</DisclosurePrimitiveContent>
</>
);
}

Guidelines

Guidelines page anchor

About the Disclosure Primitive

About the Disclosure Primitive page anchor

The Disclosure primitive is an unstyled, functional version of a Disclosure. It can be used to build a component following the WAI-ARIA Disclosure(link takes you to an external page) pattern. Our Disclosure component is built on top of this primitive.

These unstyled primitives are to be used when the styled Disclosure provided by Paste doesn’t meet the requirements needed to solve a unique customer problem. At that point, you are welcome to fallback to this functional primitive to roll your own styled disclosure while still providing a functional and accessible experience to your customers.

This primitive should be used to compose all custom Disclosures to ensure accessibility and upgrade paths.

(warning)

Warning

We strongly suggest that all components built on top of this primitive get reviewed by the Design Systems team and goes through the UX Review process to ensure an excellent experience for our customers.

This package is a wrapper around Reakit's Disclosure(link takes you to an external page). If you’re wondering why we wrapped that package into our own, we reasoned that it would be best for our consumers’ developer experience. For example:

  • We can control which APIs we expose and how to expose them. For example, in this package we rename and export only some of the source package's exports.
  • If we want to migrate the underlying nuts and bolts in the future, Twilio products that depend on this primitive would need to replace all occurrences of import … from ‘@reach/dialog’ to import … from ‘@some-new/package’. By wrapping it in @twilio-paste/disclosure-primitive, this refactor can be avoided. The only change would be a version bump in the package.json file.
  • We can more strictly enforce semver and backwards compatibility than some of our dependencies.
  • We can control when to provide an update and which versions we allow, to help reduce potential bugs our consumers may face.

This package is available individually or as part of @twilio-paste/core.

yarn add @twilio-paste/disclosure-primitive - or - yarn add @twilio-paste/core

Basic Example

Basic Example page anchor
Component preview theme
function BasicExample() {
const disclosure = useDisclosurePrimitiveState({ visible: true });
return (
<>
<DisclosurePrimitive {...disclosure}>Toggle</DisclosurePrimitive>
<DisclosurePrimitiveContent {...disclosure}>Content</DisclosurePrimitiveContent>
</>
);
}

Conditional Rendering Example

Conditional Rendering Example page anchor

You shouldn't conditionally render the DisclosurePrimitiveContent component as this will make some Reakit features not work. Instead, you can use render props and conditionally render the underneath element:

Component preview theme
function ConditionallyRenderingExample() {
const disclosure = useDisclosurePrimitiveState();
return (
<>
<DisclosurePrimitive {...disclosure}>Toggle</DisclosurePrimitive>
{/* instead of {disclosure.visible && <DisclosureContent {...disclosure}>Content</DisclosureContent>} */}
<DisclosurePrimitiveContent {...disclosure}>
{props => disclosure.visible && <div {...props}>Content</div>}
</DisclosurePrimitiveContent>
</>
);
};

Each DisclosurePrimitiveContent component should have its own useDisclosureState. This is also true for derivative modules like Dialog, Popover, Menu, Tooltip etc.

If you want to have only one DisclosurePrimitive element controling multiple DisclosurePrimitiveContent components, you can use render props to apply the same state to different Disclosures that will result in a single element.

Component preview theme
function MultipleExample() {
const disclosure1 = useDisclosurePrimitiveState();
const disclosure2 = useDisclosurePrimitiveState();
return (
<>
<DisclosurePrimitive {...disclosure1}>
{props => (
<DisclosurePrimitive {...props} {...disclosure2}>
Toggle All
</DisclosurePrimitive>
)}
</DisclosurePrimitive>
<DisclosurePrimitiveContent {...disclosure1}>Content 1</DisclosurePrimitiveContent>
<DisclosurePrimitiveContent {...disclosure2}>Content 2</DisclosurePrimitiveContent>
</>
);
};
(information)

Much of the following is copied directly from Reakit's docs. Because we may update at a different cadence, we're duplicating the docs here to prevent information fragmentation.

useDisclosurePrimitiveState Props

useDisclosurePrimitiveState Props page anchor

All the regular HTML attributes (role, aria-*, type, and so on) including the following custom props:

PropTypeDefault
baseId?string
visible?bool
animated?number or boolean
baseId prop

ID that will serve as a base for all the items IDs.

visible prop

Whether it's visible or not.

animated prop

If true, animating will be set to true when visible is updated. It'll wait for stopAnimation to be called or a CSS transition ends. If animated is set to a number, stopAnimation will be called only after the same number of milliseconds have passed.

DisclosurePrimitive Props

DisclosurePrimitive Props page anchor

All the regular HTML attributes (role, aria-*, type, and so on) including the following custom props:

PropTypeDefault
disabled?boolean
focusable?boolean
disabled prop

Same as the HTML attribute.

focusable prop

When an element is disabled, it may still be focusable. It works similarly to readOnly on form elements. In this case, only aria-disabled will be set.

(information)

State props

These props are returned by the state hook. You can spread them into this component (...state) or pass them separately. You can also provide these props from your own state logic.

PropTypeDefault
visibleboolean
baseIdboolean
toggle() => void
baseId prop

ID that will serve as a base for all the items IDs.

visible prop

Whether it's visible or not.

toggle prop

Toggles the visible state

DisclosurePrimitiveContent Props

DisclosurePrimitiveContent Props page anchor
(information)

State props

These props are returned by the state hook. You can spread them into this component (...state) or pass them separately. You can also provide these props from your own state logic.

PropTypeDefault
visibleboolean
baseIdboolean
animatednumber or boolean
animatingboolean
stopAnimation() => void
baseId prop

ID that will serve as a base for all the items IDs.

visible prop

Whether it's visible or not.

animated prop

If true, animating will be set to true when visible is updated. It'll wait for stopAnimation to be called or a CSS transition ends. If animated is set to a number, stopAnimation will be called only after the same number of milliseconds have passed.

animating prop

Whether it's animating or not.

stopAnimation prop

Stops animation. It's called automatically if there's a CSS transition.