One disclosure that shows and hides its content behind a labelled native button.
<tp-collapsible>
Collapsible
Default density
Compact density
Restart from the initial state
Show the copyable source
Order #10482 details
Shipped October 6 via UPS Ground. Tracking 1Z 84A 7F2 03 9281 4410.
Delivery to 221 Mission St, San Francisco, CA 94105.
Order #10482 details
Shipped October 6 via UPS Ground. Tracking 1Z 84A 7F2 03 9281 4410.
Delivery to 221 Mission St, San Francisco, CA 94105.
<tp-collapsible default-open style="inline-size:100%;max-inline-size:28rem;margin-inline:auto">
<span slot="label">Order #10482 details</span>
<p style="margin:0">Shipped October 6 via UPS Ground. Tracking 1Z 84A 7F2 03 9281 4410.</p>
<p style="margin:0">Delivery to 221 Mission St, San Francisco, CA 94105.</p>
</tp-collapsible>
Usage
The label slot is the trigger text and the default slot is the content. The trigger is a native button wired with aria-expanded and aria-controls, so Enter, Space and pointer all work without extra code. Use Collapsible for one independent disclosure, and Accordion when several need to coordinate.
Indicator and alignment
indicator-position puts the chevron on the leading or trailing side. content-alignment="label" lines the content up with the label instead of the outer edge, which reads better when there is leading content.
Label alignment follows writing direction and the actual Leading layout, so arbitrary icons, badges, text, or multiple nodes do not require a hard-coded indent.
Leading indicator
Default density
Compact density
Restart from the initial state
Show the copyable source
Advanced settings
Webhook retries, request signing and IP allowlists. Most teams never need to change these.
Advanced settings
Webhook retries, request signing and IP allowlists. Most teams never need to change these.
<tp-collapsible indicator-position="leading" content-alignment="label" default-open style="inline-size:100%;max-inline-size:28rem;margin-inline:auto">
<span slot="label">Advanced settings</span>
<p style="margin:0">Webhook retries, request signing and IP allowlists. Most teams never need to change these.</p>
</tp-collapsible>
Leading and trailing content
The leading and trailing slots hold non-interactive extras such as a step number or status. They sit inside the trigger button, so they become part of its accessible name; mark purely decorative marks aria-hidden. Filling the indicator’s side replaces the default chevron.
Assigning any content to the selected position suppresses the fallback; the assigned nodes retain their own semantics and never acquire disclosure-indicator state or motion automatically. Positional content is inside the native button Trigger, so it must not contain links, buttons, inputs, or other interactive descendants.
Positional slots
Default density
Compact density
Restart from the initial state
Show the copyable source
01Connect your repository
Install the GitHub app and choose which repositories Tweakpad can read.
02Configure build settingsOptional
Set the install command, output directory and environment variables.
01Connect your repository
Install the GitHub app and choose which repositories Tweakpad can read.
02Configure build settingsOptional
Set the install command, output directory and environment variables.
<div style="display:grid;gap:var(--tp-space-2);inline-size:100%;max-inline-size:28rem;margin-inline:auto">
<tp-collapsible content-alignment="label">
<span slot="leading" aria-hidden="true" style="font-variant-numeric:tabular-nums;color:var(--tp-muted-foreground)">01</span>
<span slot="label">Connect your repository</span>
<p style="margin:0">Install the GitHub app and choose which repositories Tweakpad can read.</p>
</tp-collapsible>
<tp-collapsible content-alignment="label">
<span slot="leading" aria-hidden="true" style="font-variant-numeric:tabular-nums;color:var(--tp-muted-foreground)">02</span>
<span slot="label">Configure build settings</span>
<span slot="trailing" style="font-size:var(--tp-text-sm);color:var(--tp-muted-foreground)">Optional</span>
<p style="margin:0">Set the install command, output directory and environment variables.</p>
</tp-collapsible>
</div>
Headings
heading-level wraps the trigger in a heading of that level, so screen reader users can jump between disclosures from the page outline. Leave it at 0 when the Collapsible is not a section of the page.
As a heading
Default density
Compact density
Restart from the initial state
Show the copyable source
What happens when my trial ends?
Your workspace moves to the Free plan. Projects stay intact; members beyond the first three become read-only.
What happens when my trial ends?
Your workspace moves to the Free plan. Projects stay intact; members beyond the first three become read-only.
<tp-collapsible heading-level="3" style="inline-size:100%;max-inline-size:28rem;margin-inline:auto">
<span slot="label">What happens when my trial ends?</span>
<p style="margin:0">Your workspace moves to the Free plan. Projects stay intact; members beyond the first three become read-only.</p>
</tp-collapsible>
Find in page
hidden-until-found keeps closed content in the page as hidden="until-found", so the browser’s find-in-page can search it and opens the disclosure on a match. keep-mounted retains closed content without making it searchable.
Searchable
Default density
Compact density
Restart from the initial state
Show the copyable source
Refund policy
Annual plans can be refunded in full within 30 days of purchase. Monthly plans are not prorated.
Refund policy
Annual plans can be refunded in full within 30 days of purchase. Monthly plans are not prorated.
<tp-collapsible hidden-until-found style="inline-size:100%;max-inline-size:28rem;margin-inline:auto">
<span slot="label">Refund policy</span>
<p style="margin:0">Annual plans can be refunded in full within 30 days of purchase. Monthly plans are not prorated.</p>
</tp-collapsible>
Events
Each toggle dispatches a cancelable tp-open-change with the proposed value before open changes; call preventDefault() to keep the current state.
Default density
Compact density
Restart from the initial state
Show the copyable source
Billing history (owners only)
Invoices and payment methods.
Billing history (owners only)
Invoices and payment methods.
<tp-collapsible disabled style="inline-size:100%;max-inline-size:28rem;margin-inline:auto">
<span slot="label">Billing history (owners only)</span>
<p style="margin:0">Invoices and payment methods.</p>
</tp-collapsible>
disabled blocks the trigger but keeps the current open state, so a disclosure can be locked open or closed.
Content motion
Line-by-line reveal
Default density
Compact density
Restart from the initial state
Show the copyable source
What's new in 2.4
Single sign-on with SAML and OIDC on every plan
An audit log that keeps permission changes for 400 days
CSV exports that finish up to five times sooner
Scheduled reports, emailed every Monday
What's new in 2.4
Single sign-on with SAML and OIDC on every plan
An audit log that keeps permission changes for 400 days
CSV exports that finish up to five times sooner
Scheduled reports, emailed every Monday
<tp-collapsible id="collapsible-motion" style="inline-size:100%;max-inline-size:28rem;margin-inline:auto">
<span slot="label">What's new in 2.4</span>
<ul style="display:grid;gap:var(--tp-space-2);margin:0;padding-inline-start:var(--tp-space-5)">
<li>Single sign-on with SAML and OIDC on every plan</li>
<li>An audit log that keeps permission changes for 400 days</li>
<li>CSV exports that finish up to five times sooner</li>
<li>Scheduled reports, emailed every Monday</li>
</ul>
</tp-collapsible>
<script type="module">
const collapsible = document.getElementById('collapsible-motion');
collapsible.addEventListener('tp-motion-request', (event) => {
if (event.request.role !== 'content') return;
event.respondWith({
play(request) {
const entering = request.phase === 'enter';
const items = [...request.owner.querySelectorAll('li')];
if (!entering) items.reverse();
const animations = items.map((item, index) =>
item.animate(
entering
? [{ opacity: 0, transform: 'translateX(-8px)' }, { opacity: 1, transform: 'none' }]
: [{ opacity: 1 }, { opacity: 0 }],
{ duration: entering ? 240 : 120, delay: index * 60, easing: 'ease-out', fill: 'both' },
),
);
const finished = Promise.all(animations.map((animation) => animation.finished)).then(() =>
animations.forEach((animation) => animation.cancel()),
);
return { finished, cancel: () => animations.forEach((animation) => animation.cancel()) };
},
});
});
</script>
The Collapsible asks for motion through a bubbling tp-motion-request for three roles: disclosure animates the panel’s height, indicator turns the chevron and content targets the panel body, which has no motion by default. Claiming content lets you stagger the list items while the Collapsible still measures the panel, keeps it in the page during the exit and hides it once your animation finishes.
Claim a role synchronously from tp-motion-request to replace only that role’s CSS presentation.
Styling
The measured panel exposes --collapsible-panel-height and --collapsible-panel-width. Its default Trigger, Label, positional content, focus ring, and ContentBody inset are token-based baseline presentation shared by standalone Collapsible and Accordion Item. Override those public parts when a product needs a different treatment. Put content padding on ContentBody, not Content, so padding is included in the measured animated extent.
All eight parts accept the inherited partContracts interface, including content,
host properties, render delegates and element references. A delegate must retain
its supplied bind and content. The default Trigger is a native button; it may
also delegate to the existing tp-button. Collapsible resolves that component’s
registered native action for disabled state, focus and Trigger–Panel relationships.
Consumer trigger event hooks run before disclosure handling and may call
preventComponentHandling(); the cancellable tp-open-change proposal remains
available independently.
Changing a delegate releases the former trigger’s listeners and composed control
policy. Content measurement follows the current ContentBody. Disconnecting removes
trigger listeners and measurement observation; reconnecting restores current state.
Layout hooks may rearrange regions, but cannot make a normally hidden closed Panel
visible by overriding its display. hidden-until-found retains its native reveal
semantics.
API Reference
Every element also accepts motion-policy.
Attributes
AttributePropertyTypeDefaultDescriptionopenopenbooleanfalseWhether the content is shown; accepted trigger presses update it.default-opendefaultOpenbooleanfalseInitial state when open is not set; later changes do not reset it.keep-mountedkeepMountedbooleanfalseKeeps closed content in the page instead of removing it.hidden-until-foundhiddenUntilFoundbooleanfalseKeeps closed content searchable by the browser's find in page, which opens the disclosure on a match.indicator-positionindicatorPositionCollapsibleIndicatorPosition'trailing'The side of the trigger that draws the chevron, in writing direction.content-alignmentcontentAlignmentCollapsibleContentAlignment'edge'Starts the content at the outer edge, or lined up with the label, which reads better after leading content.heading-levelheadingLevelnumber0Wraps the trigger in a heading of this level, 1 to 6, so the disclosure joins the page outline; 0 adds no heading.
Properties
PropertyTypeDefaultDescriptiononOpenChange((event: TpOpenChangeEvent) => void) | undefined—Called after an open or close proposal is accepted.
Events
EventTypeDescriptiontp-open-changeTpOpenChangeEventCancelable proposal to open or close, before open changes; detail.value is the proposed state.tp-open-change-completeCustomEvent<{ open: boolean; }>After the content finishes opening or closing; detail.open.
Methods
MethodReturnsDescriptionsetMotionScope(owner, context)voidSupplies composition-owned event scope without transferring disclosure state ownership.refreshPositions()voidRe-evaluates forwarded positional slots after their outer assignments change.
Slots
SlotDescriptiondefaultThe content shown when open.labelThe trigger text.leadingNon-interactive content before the label, such as a step number; it replaces the chevron when the indicator sits on this side.trailingNon-interactive content after the label, such as a status; it replaces the chevron when the indicator sits on this side.
CSS parts
PartDescription::part(collapsible)The root around the trigger and the content.::part(collapsible-content)The region shown when open, whose block size animates.::part(collapsible-content-body)The inner box of the content; put padding here so it is measured with the content.::part(collapsible-heading)The wrapper of the trigger, a heading when heading-level is set.::part(collapsible-label)The trigger text.::part(collapsible-leading)The region before the label: the leading slot or the chevron.::part(collapsible-trailing)The region after the label: the trailing slot or the chevron.::part(collapsible-trigger)The native button that opens and closes the content.
Related
Components, widgets and patterns that use this one.