Collapsible
A collapsible shows or hides one block of content when its trigger is clicked.
Standard shipping arrives in 3 to 5 business days.
<tk-collapsible> <span slot="trigger">Show delivery details</span> <p>Standard shipping arrives in 3 to 5 business days.</p> </tk-collapsible>
Anatomy
| Part | What it is |
|---|---|
tk-collapsible |
the element |
trigger slot |
the trigger's label, required; it is the accessible name |
| default slot | the content, shown when open |
trigger part |
the trigger <button>, with a chevron after the label; one Tab stop |
panel part |
the content |
The trigger is only as wide as its label. The content sits under it, aligned with the element's edge; you lay out the content yourself.
States
Proxy and cache settings.
Sign-ins and exports.
<tk-collapsible open><span slot="trigger">Advanced options</span><p>Proxy and cache settings.</p></tk-collapsible> <tk-collapsible disabled><span slot="trigger">Audit log</span><p>Sign-ins and exports.</p></tk-collapsible>
Hover and press shade the trigger. Keyboard focus shows a ring around it.
open: starts open. The collapsible toggles it from then on; read or set theopenproperty from code.disabled: can't be opened or closed, and the trigger leaves the Tab order.
Controlling it yourself
To load content first or guard a toggle, listen for tk-toggle and call
preventDefault(), then set open when you're ready. event.detail.open is the state it
is moving to. The content is always in the page, so use this to fill heavy content on
first open.
<tk-collapsible id="report"><span slot="trigger">Full report</span><div id="report-body"></div></tk-collapsible>
<script>
const el = document.getElementById('report');
el.addEventListener('tk-toggle', async (e) => {
if (!e.detail.open) return;
e.preventDefault();
await loadReport();
el.open = true;
});
</script>Show more, show less
The label is yours. To swap it, change it on tk-toggle:
<tk-collapsible id="more"><span slot="trigger">Show more</span><p>The rest of the description.</p></tk-collapsible>
<script>
const el = document.getElementById('more');
el.addEventListener('tk-toggle', (e) => {
el.querySelector('[slot="trigger"]').textContent = e.detail.open ? 'Show less' : 'Show more';
});
</script>When to use
- Use a collapsible for one block that can start hidden: advanced options, a long description, delivery details.
- Use an
Accordion when the trigger should be a heading, such as a stack of titled sections. A one-section accordion is fine. Wrapping a collapsible in an<h3>puts its content inside the heading too. - Use a
Tree for nested data. - Use a
Tab bar to switch between views of one area. - Don't hide what people must see, such as key instructions, an error, or a required field.
Content
- Say what opens: "Show delivery details", not "More".
- Keep a label pair matched, such as "Show more" and "Show less", and keep it in step with the state.
- Use sentence case, with no end punctuation.
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
| Use it for one show or hide | Stack several to fake an accordion |
| Say what opens: "Show delivery details" | Write "More" with no object |
| Use an accordion when the trigger is a section title | Wrap tk-collapsible in a heading |
| Switch to "Show less" while it's open | Leave "Show more" on open content |
Load content with tk-toggle, then open |
Disable the trigger while content loads |
Related
Accordion : titled sections with heading triggers.Sidebar : uses collapsibles for its navigation groups.Tab bar : switch between views of one area.
API
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
open |
boolean | false |
the content is shown; the property is live |
disabled |
boolean | false |
can't be toggled; the trigger leaves the Tab order |
Slots
| Slot | Content |
|---|---|
trigger |
the trigger's label, required |
| default | the content |
Events
tk-toggle fires when the user opens or closes it, before the state changes.
detail.open is the new state. It bubbles. Call preventDefault() to stop the change and
set open yourself.
Methods
focus() moves focus to the trigger.
CSS
::part(trigger): the trigger<button>.::part(panel): the content.
Tokens
| Element | Token |
|---|---|
| trigger text | text/default; disabled text/disabled |
| chevron | icon/secondary, icon-size-16; disabled text/disabled |
| hover, press | state/hover, state/press, with radius-8 |
| focus ring | border/accent/focus, outside the trigger |
| content text | text/default |
The trigger uses Label Large (16/24, medium), with space-4 block padding, space-8
inline padding, and space-8 before the chevron. Open content starts space-8 below the
trigger. Values per theme:
Accessibility
Built in:
- The trigger is a native
<button>that reports whether the content is expanded. - Hidden content is hidden from screen readers and can't be focused.
- The chevron is hidden from screen readers; the state is never shown by the icon alone.
- A disabled collapsible is announced as disabled.
- With reduced motion, it opens and closes without animation.
Your part:
- Write a label that says what opens; it names the button.
- If the content is a titled section of the page, use an accordion: its trigger is a real heading, and many screen reader users move by headings.
| Key | Action |
|---|---|
Tab |
moves to the trigger, then into open content |
Enter / Space |
opens or closes it |