🌌 Astra - Rails Hotwire モーダル再考

Engineering Note

🌌 Astra - Rails Hotwire モーダル再考

· · 5分で読めます

Advertisement

Hotwire Astra UI は、 Hotwire(Turbo + Stimulus)Importmap を用いて、 モーダルをどこまで サーバーレンダリングだけで設計できるか を探る実践的な試みです。

モーダルは、一見すると単純なUI要素です。

しかし実際には、すぐに設計上の圧力点になります。

  • 画面遷移せずに送信したいフォーム
  • 別アクションの中にネストされる確認フロー
  • フォーカス管理やアクセシビリティ
  • 背景クリックやESCキーの扱い
  • ページ本体と独立して更新されるコンテンツ

多くのRailsアプリケーションでは、モーダル周りに次のようなものが蓄積されていきます。

  • 場当たり的なJavaScript
  • 条件分岐だらけのテンプレート
  • 「今どのモーダルが開いているか」を追うクライアント状態

これ自体が「間違い」というわけではありません。
ただし多くの場合、モーダルをクライアント側の問題として扱っている ことが原因です。

Hotwire は、この前提を静かに崩します。


🧭 Hotwireがもたらした静かな転換:UIはナビゲーションである

Turbo Frame を使うと、ブラウザは次の性質を取り戻します。

UIの状態は、ナビゲーションとして表現できる

モーダルは、JavaScriptで「開く」必要はありません。
サーバーから描画されればよい のです。

これまでの問いは、こうでした。

「このモーダルは、今どんなJavaScript状態であるべきか?」

Hotwireでは、問いが変わります。

「今このフレームに、サーバーは何を描画すべきか?」

小さな転換ですが、設計への影響は大きいものです。


🪟 コアアイデア:常設されたモーダル用 Turbo Frame

Astra UI の中心にあるのは、非常にシンプルな考えです。

モーダルとは、画面の上に浮かんでいる Turbo Frame にすぎない

実装上は、次の1行から始まります。

<%= turbo_frame_tag "astra_modal" %>

このフレームは、レイアウトに常に存在します。

astra_modal を target にしたリンクやフォームは、 その内容を モーダルとして 描画します。 ページ本体は一切影響を受けません。

グローバルなJavaScriptは不要。
クライアントルーティングも不要。
テンプレートの二重管理もありません。


📐 なぜ、この構成はスケールするのか

最初は、あまりにも単純に見えるかもしれません。

しかし、この単純さは積み重なります。

  • 常にサーバーが唯一の真実となる
  • ブラウザ履歴が直感的に振る舞う
  • 戻る/進む操作が自然に動く
  • Turboのキャッシュ戦略と衝突しない
  • エラー表示も通常のRailsビューと同じ

何より重要なのは、
デバッグすべき「見えないクライアント状態」が存在しない ことです。


🪜 状態マシンなしのスタック型モーダル

モーダル設計で、早い段階で問題になるのが「重ね合わせ」です。

  • サインアップ内の「ログインはこちら」
  • 編集画面内の「削除確認」
  • 複数ステップの操作フロー

よくある解決策は次のようなものです。

  • モーダルマネージャ
  • z-index の調整地獄
  • 状態スタックの管理

Hotwire では、これらは必須ではありません。

Astra UI では、スタックを ネストされたナビゲーション として扱います。

astra_modal
└── modal A
    └── astra_modal_next
        └── modal B
            └── astra_modal_next
                └── modal C

各モーダル層は、

  • それぞれ独立した Turbo Frame を持ち
  • 個別に閉じることができ
  • 他の層と干渉しません

グローバル管理は不要。
モーダルマネージャも不要。
必要なのは、明示的な target だけです。


♿ 構造としてのアクセシビリティ

アクセシビリティは、後付けのチェックリストになりがちです。

しかしサーバーレンダリング中心の設計では、 多くの課題が 構造の問題 になります。

  • 描画時にフォーカスを置ける
  • ARIA属性がテンプレートの一部になる
  • ESCや背景クリックが局所的に管理できる
  • キーボード操作が状態管理に依存しない

Astra UI では、アクセシビリティを 機能ではなく、設計の帰結 として扱います。


🧠 なぜJavaScriptがほとんど存在しないのか

Hotwire Astra UI における JavaScript の役割は限定的です。

  • 背景クリックの処理
  • ESCキーの処理
  • フォーカス管理

次のような状態は、クライアントには存在しません。

  • どのモーダルが開いているか
  • 今どのステップか
  • 次に何を閉じるべきか

それらはすべて、

  • どのフレームを target にするか
  • サーバーが何を描画するか
  • リクエストが成功したか

という形で表現されます。

これにより、Rails本来の思考モデルと一致した設計になります。


📦 実践からライブラリへ

Astra UI は、最初からライブラリとして作られたものではありません。

繰り返し浮かんだ問いは、これでした。

「サーバーレンダリングUIは、どこまで実用的なのか?」

現時点での答えは、思っていたより、ずっと遠くまで です。

ライブラリとして存在している理由は、

  • 有効だったパターンを固定化するため
  • 繰り返しを減らすため
  • 安全なデフォルトを提供するため

ただし、意図的に小さく保たれています。

Hotwireを理解していれば、Astra UIはすでに理解しているはずです。


🎯 結びに

Hotwire は、UIの捉え方を変えます。

クライアント状態ではなく、描画される状態 としてUIを見る

モーダルは、その考え方を試すのにちょうど良い題材です。
小さく、目に見え、そして過剰設計されやすいからです。

Hotwire Astra UI は、 その考え方を真面目に突き詰めた結果にすぎません。

Advertisement
#hotwire#rails#モダル#turboframe#importmap#modal
Advertisement