📝
TailwindCSSでのダイアログの実装方法を調べる
TailwindCSSでのダイアログの実装方法について調べる。
TailwindCSSでダイアログでの実装方法がわからなかったので、shadcn-railsで実装がされており参考にしながらダイアログをHTML/js実装をした。
調査
- ドキュメント
- shadcn-railsのダイアログ関連の実装コード
基本HTML
shadcn-railsのダイアログ関連コードから基本HTMLを作成した。
<body>
<div>
<div>SheetTrigger</div>
<div>Backdrop</div>
<div
role="dialog"
data-state="open"
class="data-[state=closed]:hidden data-[state=open]:block fixed z-50 gap-4 bg-backgroud p-6 shadow-lg
transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300
data-[state=closed]:slide-out-to-right
data-[state=open]:slide-in-from-right"
tabindex="-1"
style="pointer-events: auto">
<div>
<h2 class="mt-10 scroll-m-20 border-b pb-2 text-3xl
font-semibold tracking-tight transition-colors first:mt-0">
The King's Plan
</h2>
<p class="leading-7 [&:not(:first-child)]:mt-6">
The king thought long and hard, and finally came up with
<a href="#" class="font-medium text-primary underline underline-offset-4">
a brilliant plan
</a>:
he would tax the jokes in the kingdom.
</p>
<blockquote class="mt-6 border-l-2 pl-6 italic">
"After all," he said, "everyone enjoys a good joke,
so it's only fair that they should pay for the privilege."
</blockquote>
</div>
<button
type="button"
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-4 w-4">
<line
x1="18"
x2="6"
y1="6"
y2="18"></line>
<line
x1="6"
x2="18"
y1="6"
y2="18"></line>
</svg>
<span class="sr-only">Close</span>
</button>
</div>
</div>
</body>
backdropを追加する
ダイアログの後ろに表示するbackdropを追加する。
ダイアログでは開閉の状態管理には、data-state="closed"
で扱う。
<div>
<div
id="modal"
data-ui--dialog-target="modal"
data-ui--sheet-target="modal"
data-state="closed"
class="hidden fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
style="pointer-events: auto"
data-aria-hidden="true"
aria-hidden="true"></div>
</div>
TailwinCSS v3.1では、<dialog>
タグを使えば、::backdrop
要素を指定することが可能になっている。
ダイアログの起動ボタンの配置とjs制御
ダイアログの起動ボタンを配置して、jsでダイアログを開く。
<div>
<button
id="opensidebar"
type="button"
data-state="closed"
>
OpenSidebar
</button>
</div>
shadcn-railsではStimulusが利用されているため、data-targetを起点にして、要素を取得する。
jsでhidden
クラスを削除し、data-sate=open
にする。
// open sidebar
document.querySelector('#opensidebar').addEventListener('click', (event) => {
const sheetTarget = document.querySelector('#sidebarsheet')
const dialogTarget = sheetTarget.querySelector("[data-ui--sheet-target='dialog']")
const modalTarget = sheetTarget.querySelector("[data-ui--sheet-target='modal']")
const contentTarget = sheetTarget.querySelector("[data-ui--sheet-target='content']")
document.body.classList.add("overflow-hidden");
contentTarget.classList.add("overflow-y-scroll", "h-full");
dialogTarget.classList.remove("hidden");
dialogTarget.dataset.state = "open";
modalTarget.classList.remove("hidden");
modalTarget.dataset.state = "open";
});
overflow-hidden
ではみだしたコンテンツを非表示にする。
body要素のコンテンツフローした分を非表示にするため、スクロールなしとなる。
overflow-y-scroll
で、はみだしたコンテンツがある場合にスクロールする。
ダイアログコンテンツがフローした分のスクロールを制御する。
閉じるボタンの追加
閉じるボタン押下時のダイアログの非表示を行う。
jsでhidden
クラスを追加し、data-sate=closed
にする。
// close sidebar
document.querySelector('#closesidebar').addEventListener('click', (event) => {
const sheetTarget = document.querySelector('#sidebarsheet')
const dialogTarget = sheetTarget.querySelector("[data-ui--sheet-target='dialog']")
const modalTarget = sheetTarget.querySelector("[data-ui--sheet-target='modal']")
const contentTarget = sheetTarget.querySelector("[data-ui--sheet-target='content']")
document.body.classList.remove("overflow-hidden");
contentTarget.classList.remove("overflow-y-scroll", "h-full");
dialogTarget.classList.add("hidden");
dialogTarget.dataset.state = "closed";
modalTarget.classList.add("hidden");
modalTarget.dataset.state = "closed";
});
// close sidebar modal
document.querySelector('#modal').addEventListener('click', (event) => {
const sheetTarget = document.querySelector('#sidebarsheet')
const dialogTarget = sheetTarget.querySelector("[data-ui--sheet-target='dialog']")
const modalTarget = sheetTarget.querySelector("[data-ui--sheet-target='modal']")
const contentTarget = sheetTarget.querySelector("[data-ui--sheet-target='content']")
document.body.classList.remove("overflow-hidden");
contentTarget.classList.remove("overflow-y-scroll", "h-full");
dialogTarget.classList.add("hidden");
dialogTarget.dataset.state = "closed";
modalTarget.classList.add("hidden");
modalTarget.dataset.state = "closed";
});
animation
ダイアログのアニメーション対応は、tailwindcss-animate
を利用しているため、インストールする。
ラップアップ
-
date-state
を利用してHTMLの状態を制御している。 -
data-*
属性を使ってjsの要素を特定し制御している。 - ダイアログのアニメーション表示は、tailwind-animateを利用している。
他に、shadcn/uiでは、sidebarまで実装されているためこちらも実装方法を確認したい。
Discussion