Accordion

Accordionは、クリックするとコンテンツが展開・折りたためる見出しのリストです。
必要な情報をコンパクトに整理し、ユーザーが見たい内容だけを表示できます。

プロパティ

Accordionは、以下のプロパティをサポートしています。

名前 デフォルト値 説明
title string 見出しの文言を設定できます。

インストールの手順

以下のコンポーネントのコードを、使いたいプロジェクトにコピー&ペーストします。
パスは実際のプロジェクトの構成にあわせて更新します。

atoms/Accordion.svelte
        <!--
@component
## 概要
- detailsタグ・summaryタグのように、クリックなどのユーザー操作で開閉に対応する汎用的なアコーディオンコンポーネントです

## 機能
- detailsタグ・summaryタグと同様に、クリックやフォーカスなどのインタラクションに対応します

## Props
- title: 見出しの文言を設定できます

## Usage
```svelte
<Accordion title='項目'>ここにコンテンツ内容が入ります</Accordion>
```
-->

<script module lang="ts">
  import type { Snippet } from 'svelte';
  import type { ClassValue } from 'svelte/elements';
  import { cva, type VariantProps } from 'class-variance-authority';

  export let accordionVariants = cva('w-full border-b-1 border-base-stroke-default');

  export let accordionTitleVariants = cva('relative py-4 pr-7.5 pl-2 text-base text-base-foreground-default list-none outline-primary transition cursor-pointer hover:bg-base-container-accent/90 focus-visible:outline-[0.125rem] focus-visible:outline-offset-[0.125rem] focus-visible:outline-primary focus-visible:!rounded-sm');

  export let accordionDescriptionVariants = cva('px-2 pb-4 text-base-foreground-default text-sm');

  export type AccordionVariants = VariantProps<typeof accordionVariants>;

  export interface AccordionProps extends AccordionVariants {
    /** タイトル */
    title: string;
    /** クラス */
    class?: ClassValue;
    children: Snippet<[]>;
  }
</script>

<script lang="ts">
  import { ChevronDown } from '@lucide/svelte';

  let { title, class: className, children } = $props();

  const animTiming = {
    duration: 250,
    easing: 'ease-out',
  };

  let detailsElement;
  let summaryElement;
  let descriptionContainer;

  let isOpen = $state(false);

  let accordionVariantClass = $derived(accordionVariants({ class: className }));

  function closeAnimKeyframes() {
    return [
      {
        height: descriptionContainer.offsetHeight + 'px', // height: "auto"だとうまく計算されないため要素の高さを指定する
        opacity: 1,
      },
      {
        height: 0,
        opacity: 0,
      },
    ];
  }

  function openAnimKeyframes() {
    return [
      {
        height: 0,
        opacity: 0,
      },
      {
        height: descriptionContainer.offsetHeight + 'px',
        opacity: 1,
      },
    ];
  }

  function onToggleSummaryAnim(e) {
    e.preventDefault();
    if (detailsElement.open) {
      const close_anim = descriptionContainer.animate(closeAnimKeyframes(), animTiming);

      isOpen = false;

      close_anim.onfinish = () => {
        // アニメーションの完了後にopen属性を取り除く
        detailsElement.removeAttribute('open');
      };
    }
    else {
      detailsElement.setAttribute('open', 'true');

      isOpen = true;

      descriptionContainer.animate(openAnimKeyframes(), animTiming);
    }
  }
</script>

<details class={accordionVariantClass} bind:this={detailsElement}>
  <summary class={accordionTitleVariants()} bind:this={summaryElement} onclick={onToggleSummaryAnim}>
    {title}
    <ChevronDown class={['absolute inset-y-0 right-2 transition duration-300 my-auto', { 'rotate-180': isOpen }]} size="1rem" />
  </summary>
  <div class="overflow-hidden" bind:this={descriptionContainer}>
    <div class={accordionDescriptionVariants()}>
      {@render children()}
    </div>
  </div>
</details>

      

使い方


サンプル

Default

特に操作が行われていない、デフォルトの状態です。