Show · Collapsible

Collapsible

The collapsible component is a container component used to put long sections of information under a block that can be expanded or collapsed.

When to use

  • Collapsibleis a behavior component with animation effect by default. It is used in various components in Semi Components, including: Navigation, Collapse, Tree, TreeSelect, and Typography.
  • When the above components do not meet requirements or customized collapsed behavior, you can use Collapsible to put in contents that need to be expanded or folded.

Demos

How to import

<script lang="ts">
    import { Collapsible } from '@fibrae/semi-ui-svelte';
</script>

Basic Usage

Use isOpen to control the expansion or folding of the content.

Code
<script lang="ts">
    import { Button, Collapsible } from '@fibrae/semi-ui-svelte';

    let isOpen = $state(false);
</script>

<div>
    <Button aria-controls="collapsible-basic" aria-expanded={isOpen} onclick={() => (isOpen = !isOpen)}>Toggle</Button>
    <Collapsible id="collapsible-basic" {isOpen}>
        <ul>
            <li>
                <p>Semi Design 以内容优先进行设计。</p>
            </li>
            <li>
                <p>更容易地自定义主题。</p>
            </li>
            <li>
                <p>适用国际化场景。</p>
            </li>
            <li>
                <p>效率场景加入人性化关怀。</p>
            </li>
        </ul>
    </Collapsible>
</div>

Custom Animation Duration

You can use duration to set animation duration or turn off animation by setting motion to false.

设置动画时间:

Code
<script lang="ts">
    import { Button, Collapsible, InputNumber } from '@fibrae/semi-ui-svelte';

    let isOpen = $state(false);
    let duration = $state(250);
</script>

<div>
    <span id="collapsible-duration-label">设置动画时间:</span>
    <InputNumber aria-labelledby="collapsible-duration-label" min={0} onChange={(value) => (duration = Number(value))} step={10} style="width: 120px" value={250} />
    <br />
    <Button aria-controls="collapsible-duration" aria-expanded={isOpen} onclick={() => (isOpen = !isOpen)}>Toggle</Button>
    <Collapsible {duration} id="collapsible-duration" {isOpen}>
        <ul>
            <li>
                <p>Semi Design 以内容优先进行设计。</p>
            </li>
            <li>
                <p>更容易地自定义主题。</p>
            </li>
            <li>
                <p>适用国际化场景。</p>
            </li>
            <li>
                <p>效率场景加入人性化关怀。</p>
            </li>
        </ul>
    </Collapsible>
</div>

Nested use


Code
<script lang="ts">
    import { Button, Collapsible } from '@fibrae/semi-ui-svelte';

    let isOpen = $state(false);
    let isChildOpen = $state(false);
</script>

<div>
    <Button aria-controls="collapsible-nested" aria-expanded={isOpen} onclick={() => (isOpen = !isOpen)}>Toggle</Button>
    <br />
    <Collapsible id="collapsible-nested" {isOpen}>
        <div>
            <span>Semi Design的设计原则包括:</span>
            <Button aria-controls="collapsible-nested-list" aria-expanded={isChildOpen} onclick={() => (isChildOpen = !isChildOpen)}>Toggle List</Button>
        </div>
        <Collapsible id="collapsible-nested-list" isOpen={isChildOpen}>
            <ul>
                <li>
                    <p>Semi Design 以内容优先进行设计。</p>
                </li>
                <li>
                    <p>更容易地自定义主题。</p>
                </li>
                <li>
                    <p>适用国际化场景。</p>
                </li>
                <li>
                    <p>效率场景加入人性化关怀。</p>
                </li>
            </ul>
        </Collapsible>
    </Collapsible>
</div>

Custom CollapseHeight

You could use collapseHeight to customize collapsed height.

  • Semi Design 以内容优先进行设计。

  • 更容易地自定义主题。

  • 适用国际化场景。

  • 效率场景加入人性化关怀。

Code
<script lang="ts">
    import { Button, Collapsible } from '@fibrae/semi-ui-svelte';

    let isOpen = $state(false);

    const maskStyle = $derived(isOpen ? undefined : '-webkit-mask-image: linear-gradient(to bottom, black 0%, rgba(0, 0, 0, 1) 60%, rgba(0, 0, 0, 0.2) 80%, transparent 100%)');
    const linkStyle = 'position: absolute; left: 0; right: 0; width: 100%; padding: 0; border: none; background: none; font-size: inherit; font-family: inherit; line-height: inherit; text-align: center; bottom: -10px; font-weight: 700; cursor: pointer; color: var(--semi-color-link)';
</script>

<Button aria-controls="collapsible-height" aria-expanded={isOpen} onclick={() => (isOpen = !isOpen)}>Toggle</Button>
<div style="position: relative">
    <Collapsible collapseHeight={60} id="collapsible-height" {isOpen} style={maskStyle}>
        <ul>
            <li>
                <p>Semi Design 以内容优先进行设计。</p>
            </li>
            <li>
                <p>更容易地自定义主题。</p>
            </li>
            <li>
                <p>适用国际化场景。</p>
            </li>
            <li>
                <p>效率场景加入人性化关怀。</p>
            </li>
        </ul>
    </Collapsible>
    {#if !isOpen}
        <button aria-controls="collapsible-height" aria-expanded={isOpen} style={linkStyle} type="button" onclick={() => (isOpen = !isOpen)}>+ Show More</button>
    {/if}
</div>

API reference

Props

PropertyDescriptionTypeDefault
childrenSnippet for the content to expand or collapse.Snippet-
collapseHeightHeight in pixels when collapsed.number0
collapseHeightAdaptiveWhether the collapsed height follows the content when it is shorter than collapseHeight.booleanfalse
durationAnimation duration in milliseconds.number250
fadeWhether to fade in and out.booleanfalse
idid of the inner content element, for the trigger's aria-controls; it does not read the global default.string-
isOpenWhether the content area is expanded.booleanfalse
keepDOMWhether to keep the DOM tree of the hidden content; it is destroyed by default.booleanfalse
lazyRenderUsed with keepDOM; when true, the content is not rendered on mount but after the first expansion.booleanfalse
motionWhether to enable the animation.booleantrue
onMotionEndCalled when the expand or collapse animation ends.Type() => void-

class, style and other native attributes and events land on the outer wrapper; id is a declared prop and lands on the inner content element, and it does not read the global default. An ontransitionend from the caller runs together with the component's own handler.

Accessibility

ARIA

  • Collapsible has id props, the value passed in will be set as the id of the inner content element (not the outer wrapper). The trigger button points at it with aria-controls and reports whether it is open with aria-expanded, see the usage example below.
<script lang="ts">
    import { Button, Collapsible } from '@fibrae/semi-ui-svelte';

    const collapseId = 'myCollapsible';
    let visible = $state(false);
</script>

<Button aria-controls={collapseId} aria-expanded={visible} onclick={() => (visible = !visible)}>
    {visible ? 'hide' : 'show'}
</Button>
<Collapsible id={collapseId} isOpen={visible}>
    <div>hide content</div>
</Collapsible>

Focus

  • With collapseHeight of 0, the collapsed content is inert: screen readers skip it and Tab cannot reach it.
  • With collapseHeight above 0, the visible preview and the clipped part are the same element, and the clipped part cannot be hidden on its own, so the whole content stays readable and focusable and screen reader users get the full text. Do not put links, buttons or other focusable elements in the clipped part, or keyboard focus lands where it cannot be seen.

FAQ

  • Why Collapsible does not expand as expected?
    The expanded height of Collapsible is computed by CSS and does not depend on measuring the node, so toggling isOpen under a parent with display: none still gives the correct expanded height. First check that isOpen follows your state; with collapseHeight above 0, part of the content is visible while collapsed by design.