【Astro】Alpine.jsでアコーディオンのコンポーネントを作る
目次
執筆時の作業環境は以下のとおりです。
- MacOS: Sonoma 14.2.1
- Node.js: v21.6.1
- npm: v10.2.4
- Astro: v4.4.0
- Alpine.js: v3.13.5
はじめに
Alpine.jsは、HTMLの要素に直接処理を記述することでDOM操作やインタラクティブなUIを実装することができるJavaScriptフレームワークです。
公式HP
Alpine.js
A rugged, minimal framework for composing behavior directly in your markup.
今回はアコーディオンにHTML標準の<details>
タグを使うのでそのままでもアコーディオンとして機能しますが、開閉時のアニメーションを簡単に実装するためにAlpine.jsも併用します。
完成形はこちらです。
クリックしてください
クリックしてください
開きました!
<!-- コード全体 -->
<details x-data="{showDetails: false}">
<summary @click="showDetails = !showDetails" class="block">
<slot name="summary" />
</summary>
<div x-show="showDetails" x-collapse x-cloak>
<slot name="details" />
</div>
</details>
<style lang="scss">
summary {
position: relative;
padding-right: 1rem;
&::before,
&::after {
content: '';
position: absolute;
top: 50%;
right: 0.5rem;
background-color: var(--stone-800);
translate: 50% -50%;
}
&::before {
width: 1rem;
height: 2px;
}
&::after {
width: 2px;
height: 1rem;
transition: rotate 0.3s ease;
}
}
details[open] {
summary::after {
rotate: 90deg;
}
}
</style>
Alpine.jsをプロジェクトに追加する
Astroでは、Alpine.jsの公式のインテグレーションが用意されているので、以下のコマンドをターミナルで入力すると自動的にプロジェクトにAlpine.jsが追加されます。
npx astro add alpinejs
collapse
プラグイン
アコーディオン開閉時に伸び縮みするようなアニメーションを追加するためにAlpine.jsのcollapse
プラグインを導入します。
ターミナルで以下のコマンドを入力して、プラグインと型ファイルをインストールします。
npm install @alpinejs/collapse
npm install --save-dev @types/alpinejs__collapse
astro.config.mjs
のAlpine.jsインテグレーションの部分にentrypoint
を指定します。
import { defineConfig } from 'astro/config'
import alpine from '@astrojs/alpinejs'
export default defineConfig({
integrations: [alpine({ entrypoint: '/src/entrypoint' })],
})
src/entrypoint.ts
を作成し以下のコードを記述します。
import type { Alpine } from 'alpinejs'
import collapse from '@alpinejs/collapse'
export default (Alpine: Alpine) => {
Alpine.plugin(collapse)
}
変数名と初期値を設定する
まずはx-data
ディレクティブを使って変数名と初期値を設定します。
x-data
の値にオブジェクトの形で{変数名: 初期値}
と書くことで設定できます。
<details x-data="{showDetails: false}">・・・</details>
let showDetails = false
と宣言するのと同じようなイメージです。 スコープ
この変数のスコープは、x-data
ディレクティブを書いた要素をその子要素内に限定されます。先祖要素や兄弟要素などでは使うことができません。
逆に言うと、同じアコーディオンコンポーネントがページ内に複数あっても、お互いの変数の値が影響してしまうことはありません。
クリック時に変数を反転させる
アコーディオンを開閉するトリガーになる要素(今回はsummary
要素)に@click
ディレクティブを指定し、値をshowDetails = !showDetails
とします。
<details x-data="{showDetails: false}">
<!-- ↓いまここ -->
<summary @click="showDetails = !showDetails" class="block"> ・・・ </summary>
・・・
</details>
これにより、showDetails
がtrue
ならfalse
に、false
ならtrue
に反転します。
class="block"
は、summary
要素にデフォルトで存在する ”▶”
というマーカーを非表示にしています。
Tailwind CSS を使用しているので、それ以外の方法でスタイリングしている場合は合わせて記述を変えてください。
変数に応じて表示⇔非表示を切り替える
表示⇔非表示を切り替えたい要素にx-show
ディレクティブを記述し、値をshowDetails
にします。これにより、その変数がtrue
なら表示され、false
なら非表示になります。
そのままだと、パッパッと機械的に切り替わるので、x-collapse
ディレクティブをつけて開閉時の伸び縮みのアニメーションを付けます。
さらに、ページ表示時に開いた状態が一瞬見えてしまうことがあるので、x-cloak
ディレクティブをつけ、CSSで[x-cloak]: {display: none}
というスタイルを指定することで回避します。
<details x-data="{showDetails: false}">
<summary @click="showDetails = !showDetails" class="block"> ・・・ </summary>
<!-- ↓いまここ -->
<div x-show="showDetails" x-collapse x-cloak>・・・</div>
</details>
名前付きスロットで子要素の位置を指定する
アコーディオンの中身は、Astroの名前付きスロットで挿入します。
クリックする部分のスロットにname="summary"
、開閉する部分のスロットにname="details"
と設定します。
<details x-data="{showDetails: false}">
<summary @click="showDetails = !showDetails" class="block">
<!-- ↓いまここ 1 -->
<slot name="summary" />
</summary>
<div x-show="showDetails" x-transition x-cloak>
<!-- ↓いまここ 2 -->
<slot name="details" />
</div>
</details>
ページ内でアコーディオンコンポーネントを使用する
Astroのページのコードフェンス内でAccordionコンポーネントをインポートし、<Accordion>
タグの子要素にslot="summary"
とslot="details"
を持つ要素をいれることでアコーディオンとして機能します。
---
import Accordion from 'path/to/file/Accordion.astro'
---
<Accordion>
<p slot="summary">詳しく見る</p>
<p slot="details">詳細なテキスト・・・</p>
</Accordion>
スタイリング
<summary>
タグの右端に、開閉時に変化する+/-マークを付けるため、<style>
タグで以下のCSSを書いています(SCSSを使用しています)
<style lang="scss">
summary {
position: relative;
padding-right: 1rem;
&::before,
&::after {
content: '';
position: absolute;
top: 50%;
right: 0.5rem;
background-color: var(--stone-800);
translate: 50% -50%;
}
&::before {
width: 1rem;
height: 2px;
}
&::after {
width: 2px;
height: 1rem;
transition: rotate 0.3s ease;
}
}
details[open] {
summary::after {
rotate: 90deg;
}
}
</style>
その他は機能だけを提供してスタイルを持たせていないので、使う側で自由にスタイリングできます。
このアコーディオンのスタイリング例を見る
※ Tailwind CSS を使っています。
<div class="py-2 px-4 bg-stone-200 ">
<Accordion>
<p slot="summary" class="flex justify-between rounded-md">このアコーディオンのスタイリング例を見る</p>
<div slot="details" class="grid gap-8 mt-4">
・・・
</div>
</Accordion>
</div>
おわり
Alpine.jsを利用したアコーディオンコンポーネントの作り方でした。 名前付きスロットの活用やスタイルレスな設計で、汎用的に使えるコンポーネントにすることができました。
参考
@astrojs/alpinejs
Learn how to use the @astrojs/alpinejs framework integration to extend component support in your Astro project.
コンポーネント
Astroコンポーネント構文の紹介です。
Collapse — Alpine.js
Collapse and expand elements with robust animations