作った手順は次の通りです。
1. TailwindCSS をCDNから読み込む。
headタグ内に次のlinkタグを追加します。
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" />
2. Alpine.js をCDNから読み込む。
headタグ内に次のstyleタグを追加します。
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
3. ページのレイアウトを作る。
bodyタグ直下のdivタグの中に、header, main, footer の3つの要素を作成します。
親のdivのスタイルで display: grid とし、それぞれのエリアの高さが header 6rem, main 1fr, footer 4rem になるように指定します。また w-screen, h-screen で親のdivが画面一杯に広がるようにしています。
<div
class="m-0 p-0 w-screen h-screen grid"
style="grid-template-rows: 6rem 1fr 4rem;"
>
詳細はソースコードを参照してください。
下の画像のような表示になればOKです。
mainに overflow-y-auto クラスを指定しているので、コンテンツが画面に収まらない場合はフッターやヘッダーを固定にしたままmain部分だけを縦スクロールさせることが出来ます。
<main class="p-6 overflow-y-auto">
画面の中央にある「開く」ボタンのクラス指定は次のようになっています。(一部省略)
<button
class="py-2 px-4 max-h-12 bg-blue-600
hover:bg-blue-400 text-white border rounded-md shadow"
>
TailwindCSS のおかげでそれなりに見栄えの良いボタンになります。
4. モーダルダイアログのHTMLを追加。
親divの一番最後に下のHTMLを追加します。
<!-- モーダルダイアログのラッパー -->
<div>
<!-- 背景を暗くするための半透明部分 -->
<div></div>
<!-- 実際のモーダルダイアログ部分 -->
<div>
<header>
<h1>確認してください</h1>
</header>
<main>
<p>本当にこの操作を実行しますか?</p>
</main>
<footer>
<button>キャンセル</button>
<button>実行!</button>
</footer>
</div>
</div>
5. モーダルダイアログのスタイルを設定。
ラッパー用divに次のクラスを指定して、画面一杯をカバーするようにします。
<div class="absolute top-0 left-0 w-screen h-screen">
次に背景を暗くするためのdivのスタイルを設定します。
<div class="absolute w-full h-full bg-black opacity-80"></div>
ダイアログ本体部分のスタイル指定は下のようになります。
<div
class="relative w-5/6 max-w-xl h-1/2 m-auto grid bg-gray-300 border rounded-md shadow"
style="top: 20vh; grid-template-rows: 4rem 1fr 6rem;"
>
TailwindCSSで定義されていない値についてはstyle属性で独自に指定しています。
最終的にはこのようなダイアログになります。
6. 初期状態でモーダルダイアログを非表示にする。
ここから Alpine.js が活躍します。
まず、ページの一番親(body直下)のdivに x-data 属性を追加します。
<div
x-data="{ open : false }"
class="m-0 p-0 w-screen h-screen grid"
style="grid-template-rows: 6rem 1fr 4rem;"
>
次にモーダルダイアログのラッパーのdivに x-show 属性を追加します。
<!-- モーダルダイアログ -->
<div x-show="open" class="absolute top-0 left-0 w-screen h-screen">
これでモーダルダイアログが初期状態では表示されなくなります。
7. 「開く」ボタンでモーダルダイアログを表示する。
ページ中央の「開く」ボタンに @click="open = true" を追加します。
<button
@click="open = true"
class="py-2 px-4 max-h-12 bg-blue-600 ...
>開く</button>
これで「開く」ボタンをクリックするとモーダルダイアログが表示されるようになりました!
8. 「キャンセル」ボタンでモーダルダイアログを閉じる。
モーダルダイアログ内の「キャンセル」ボタンでは「開く」ボタンと反対に "open = false" とすることでダイアログを非表示にします。
<button
@click="open = false"
class="py-2 px-4 text-white bg-gray-600 ..."
>キャンセル</button>
Alpine.js シンプルで良いですね!
ちなみにx-showでの表示・非表示切替時にトランジションアニメーションを付けるには、x-transition 属性を付加するだけで大丈夫です。
x-transition ではデフォルトで250msの scale + opacityのアニメーションが実行されます。これをopacityのみに変更したい場合は、x-transition を x-transition.opacity とすればOKです。もちろん実行時間の指定も可能です。
9. 外側をクリックされたらモーダルダイアログを閉じる。
@click.outside という表記を使えば、その要素の外側がクリックされた時の処理を書くことが出来ます。
<!-- モーダルダイアログ -->
<div x-show="open" class="absolute top-0 left-0 ...">
<div class="absolute w-full h-full bg-black ..."></div>
<div
@click.outside="open = false"
class="relative w-5/6 max-w-xl h-1/2 m-auto ..."
style="top: 20vh; grid-template-rows: 4rem 1fr 6rem;"
>
10. 「実行!」ボタンが押されたらモーダルダイアログを閉じてカスタムイベントを発行する。
「実行!」ボタンの処理もキャンセルと同じように @click="..."の中に全て書いてしまっても良いのですが、ここでは Alpine.js のカスタムイベント発行の機能を使ってみます。
<button
@click="open = false; $dispatch('dialog-ok')"
class="py-2 px-4 text-white bg-red-600 ..."
>実行!</button>
11. カスタムイベントを受け取ったら何らかの処理を実行する。
$dispatch() で発行したカスタムイベントはDOMツリーの上位にある全ての要素で受け取ることが可能です。
ここでは body直下のdivにイベント処理を追加しました。
<div
x-data="{ open : false }"
@dialog-ok="setTimeout(() => alert('Hi'), 100)"
class="m-0 p-0 w-screen h-screen grid"
>
setTimeout()を使っているのは、これが無いとモーダルダイアログが閉じる前にアラートが表示されてしまっていたためです。
まとめ
TailwindCSSもAlpine.jsも、覚えないといけないことが少なくてすんなりと習得出来そうです。
TailwindCSSに慣れるとスタイルの設定にかかる時間が格段に短縮されます。Alpine.jsも必要最小限の使い方さえ覚えれば簡単な動作であればさくさくと実装出来て、とても便利です。
興味を持った方はぜひ試してみてください!
🍻