ドロワー
hc-drawer は、ネイティブの <dialog> 要素を、ビューポートの任意の
端からスライドインするパネルとしてスタイルします。ネイティブの
ダイアログがフォーカストラップ、Escape で閉じる、::backdrop
レイヤーを無料で処理します。HC は端への配置、スライドイン / アウトの
アニメーション、そして(installDrawer 経由で)スライドパネルに
ユーザーが期待するバックドロップクリックで閉じる操作を足します。
別名: サイドパネル、オフキャンバス、シート。
ブラウザのベースライン
Section titled “ブラウザのベースライン”| プリミティブ | 必要バージョン |
|---|---|
HTML <dialog> + showModal() | すべてのエバーグリーンブラウザ |
CSS @starting-style + transition-behavior: allow-discrete | Chrome 117+、Firefox 129+、Safari 17.5+ |
スライドアニメーションは穏やかにデグレードします —
@starting-style のない古いブラウザでは入場トランジションなしで
最終位置にスナップしますが、ドロワーは機能します。
基本の HTML
Section titled “基本の HTML”<button class="hc-button" type="button" onclick="document.getElementById('settings-drawer').showModal()"> Open settings</button>
<dialog class="hc-drawer" data-side="right" id="settings-drawer"> <header class="hc-drawer__header"> <h2 class="hc-drawer__title">Settings</h2> <form method="dialog"> <button class="hc-button" data-variant="ghost" data-size="sm" type="submit" aria-label="Close">×</button> </form> </header>
<div class="hc-drawer__body"> <p>Form fields, settings, anything.</p> </div>
<footer class="hc-drawer__footer"> <form method="dialog"> <button class="hc-button" type="submit">Cancel</button> </form> <button class="hc-button" data-variant="primary">Save</button> </footer></dialog>import { installDrawer } from '@hypermedia-components/core';installDrawer();installDrawer() は冪等で、アンインストーラを返します。ゼロ設定の
@hypermedia-components/core/behaviors エントリが自動インストールし、
htmx でスワップされた内容も自動で拾います。
data-side は right(デフォルト)、left、top、bottom を
受け付けます。右 / 左のドロワーはビューポートの高さを満たし、幅を
--hc-drawer-side-max-width で制限します。上 / 下のドロワーは
ビューポートの幅を満たし、高さを --hc-drawer-vert-max-height で
制限します。
<button class="hc-button" type="button" onclick="document.getElementById('left-drawer').showModal()"> Open left</button>
<dialog class="hc-drawer" data-side="left" id="left-drawer"> <header class="hc-drawer__header"> <h2 class="hc-drawer__title">Left drawer</h2> <form method="dialog"> <button class="hc-button" data-variant="ghost" data-size="sm" type="submit" aria-label="Close">×</button> </form> </header> <div class="hc-drawer__body"> <p>Slides in from the left edge.</p> </div></dialog>
<!-- same structure, other edges --><dialog class="hc-drawer" data-side="top" id="top-drawer">…</dialog><dialog class="hc-drawer" data-side="bottom" id="bottom-drawer">…</dialog>ドロワーを閉じる
Section titled “ドロワーを閉じる”推奨順に 3 つの慣用パターン:
<form method="dialog">(JS なし)
Section titled “<form method="dialog">(JS なし)”method="dialog" のフォーム内のボタンは、送信でダイアログを閉じ
ます。ネイティブ、アクセシブル、宣言的です。
<form method="dialog"> <button class="hc-button" type="submit">Cancel</button></form>バックドロップクリック(installDrawer が追加)
Section titled “バックドロップクリック(installDrawer が追加)”ドロワーパネルの外側(::backdrop 領域)のクリックがダイアログを
閉じます。ビヘイビアは event.target === dialog でこれを検知します。
ドラッグで解散(installDrawer が追加)
Section titled “ドラッグで解散(installDrawer が追加)”パネルをアンカーされた端の方向へドラッグすると解散します。軸は
data-side に従います(right / left → 水平、top / bottom → 垂直)。
パネルサイズの約 40% を超えるか、素早いフリックで、ドロワーは
スライドアウトして閉じます — そこに届かず放すとスナップバックします。
動くのは外向きだけです(内向きのドラッグはクランプされるため、
ラバーバンディングはなく、prefers-reduced-motion の特別扱いも
不要です)。
ジェスチャはパネルのクローム — __header / __footer — から
つかみます。スクロール可能な __body やインタラクティブな
コントロールからは決してつかまないため、コンテンツのスクロールと
ボタンは機能し続けます。Pointer Events ベースなので、マウス、タッチ、
ペンで機能します。
JS の .close()
Section titled “JS の .close()”document.getElementById('settings-drawer').close();htmx のレスポンスハンドラから使うのに便利です — htmx 成功時に
ダイアログを閉じるパターンには
installCloseDialogと
組み合わせてください。
htmx での利用
Section titled “htmx での利用”サーバ取得に応じてドロワーを開き、htmx にコンテンツをスワップさせ ます:
<button class="hc-button" data-hx-get="/users/42/edit" data-hx-target="#user-drawer .hc-drawer__body" data-hx-swap="innerHTML" onclick="document.getElementById('user-drawer').showModal()"> Edit user</button>
<dialog class="hc-drawer" id="user-drawer" data-side="right"> <header class="hc-drawer__header"> <h2 class="hc-drawer__title">Edit user</h2> <form method="dialog"> <button class="hc-button" data-variant="ghost" data-size="sm" type="submit" aria-label="Close">×</button> </form> </header>
<div class="hc-drawer__body"> <span class="hc-spinner" role="status" aria-label="Loading"></span> </div></dialog>フォーム保存後の成功時クローズには:
<form data-hx-post="/users/42" data-hx-target="this" data-hc-close-dialog-on-success> …</form>同梱の installCloseDialog ビヘイビアが、成功した htmx レスポンスで
内包する <dialog> を閉じます。hc-dialog と同じパターンです。
アクセシビリティ
Section titled “アクセシビリティ”- ネイティブの
<dialog>はすでに正しい role、フォーカストラップ、 Escape のセマンティクスを公開しています。role="dialog"やaria-modal="true"を足さないでください — 冗長で、一部の スクリーンリーダーを混乱させえます。 - フォーカストラップの着地先があるよう、ドロワー内には常に フォーカス可能な要素を含めてください。ヘッダーの閉じるボタンか フッターの主アクションで十分です。
- 見える閉じる操作部を用意してください。上の
<form method="dialog">パターンは、見える「×」ボタンに組み込みの キーボード起動経路(ボタン上の Enter / Space)を対にしています。 - スライドアニメーションは
prefers-reduced-motion: reduceを尊重 します — ユーザーがオプトアウトしていればトランジション時間は 0 ms に落ちます。
ドロワーは意図的に 1 つの見た目だけを提供します — 面は hc-dialog と
同じ規約に従うため、同じテーマトークン(ライト / ダーク / カラー)が
そのまま機能します。破壊的フロー向けの data-variant="error" の
ようなバリアントは、コンテナのクロームではなくコンテンツ(赤い
ヘッダー、先頭のエラーアラート)でモデル化してください。
テーマ用トークン
Section titled “テーマ用トークン”component トークン(component.tokens.json):
| トークンパス | 用途 |
|---|---|
drawer.bg / fg / border | 面の色。 |
drawer.side-max-width | 右 / 左ドロワーの幅の上限。 |
drawer.vert-max-height | 上 / 下ドロワーの高さの上限。 |
drawer.padding | ヘッダー / ボディ / フッターの内側パディング。 |
drawer.gap | ヘッダー / フッターのフレックスギャップ。 |
drawer.backdrop | ::backdrop の背景。 |
drawer.duration | スライドアニメーションの時間。 |
CSS 変数
Section titled “CSS 変数”生成される CSS 変数を表示
--hc-drawer-bg | -fg | -border--hc-drawer-side-max-width | -vert-max-height--hc-drawer-padding | -gap--hc-drawer-backdrop | -duration