Ruby Ruby on Rails Web開発 プログラミング

Rails 8.1 で作るナビゲーションバー(ログイン・管理者対応・プルダウン付き)

今回の例では、Tailwind CSSTurbo + Stimulus を使い、以下の要素を持つナビゲーションバーを作りました

  • ロゴ
  • PC/モバイル対応のメニュー
  • ログイン状況表示
  • スーパーユーザー専用「新規登録」リンク
  • ログアウト(確認ダイアログ付き)
  • PCでもモバイルでも使えるプルダウンメニュー

この記事を書いてる人

082p
082p

ご訪問、ありがとうございます!

システムエンジニア兼動画クリエーターとして活動しています。
主にRubyを得意とし、Ruby on Railsを中心にWebアプリケーション開発を行っています。
業務ではSQLやJavaScriptに触れることも多く、バックエンドからデータベースまで幅広く対応しています。
このブログでは、Ruby on RailsやPython(Django)などのWebアプリケーション開発やデータベース関連の技術記事を中心に、時々C#やWPFなどの開発内容についても発信しています。

ビュー構成 (app/views/shared/_navbar.html.erb)

<header class="bg-[#3E2723]">
  <nav class="mx-auto flex max-w-7xl items-center justify-between p-6 lg:px-8">
    <!-- ロゴ -->
    <div class="flex lg:flex-1 items-center gap-2">
      <%= link_to root_path, class: "flex items-center gap-2" do %>
        <%= image_tag "SKILL_LIFE.png", alt: "SKILL LIFE", class: "h-16 w-auto" %>
      <% end %>
    </div>

    <!-- モバイル用バーガーメニュー -->
    <div class="flex lg:hidden">
      <button id="mobile-menu-button" class="text-[#FFF8E1] focus:outline-none">
        <svg class="h-6 w-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/>
        </svg>
      </button>
    </div>

    <!-- PC用リンク -->
    <div class="hidden lg:flex lg:flex-1 lg:justify-end gap-2 relative">
      <% if user_signed_in? %>
        <div class="relative">
          <!-- プルダウンボタン -->
          <button id="user-menu-button" class="flex items-center gap-2 text-[#FFF8E1] font-semibold px-4 py-2 rounded-md hover:bg-[#5D4037] transition-colors duration-200 focus:outline-none">
            <% if current_user&.super_admin? %>
              <span class="bg-yellow-500 text-white px-2 py-0.5 rounded-full text-xs">スーパーユーザー</span>
            <% end %>
            <%= current_user.login_id %>でログイン中
            <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/>
            </svg>
          </button>

          <!-- プルダウンメニュー -->
          <div id="user-menu" class="absolute right-0 mt-2 hidden w-48 bg-[#3E2723] rounded-md shadow-lg overflow-hidden ring-1 ring-black ring-opacity-20 z-50">
            <% if current_user&.super_admin? %>
              <%= link_to "新規登録 >", admin_new_user_path,
                  class: "px-4 py-2 text-[#FFF8E1] font-semibold rounded hover:bg-[#5D4037] transition-colors duration-150" %>
              <div class="border-t border-[#5D4037]"></div>
            <% end %>

            <%= link_to "ログアウト >", destroy_user_session_path,
                class: "px-4 py-2 text-[#FFF8E1] font-semibold rounded hover:bg-[#795548] transition-colors duration-150",
                data: { turbo_method: :delete, turbo_confirm: "ログアウトしてよろしいですか?" } %>
          </div>
        </div>
      <% else %>
        <%= link_to "ログイン", new_user_session_path, class: "bg-[#795548] hover:bg-[#8D6E63] text-[#FFF8E1] px-3 py-2 rounded" %>
      <% end %>
    </div>
  </nav>

  <!-- モバイルメニュー -->
  <div id="mobile-menu" class="hidden lg:hidden bg-[#3E2723] p-4">
    <% if user_signed_in? %>
      <span class="text-[#FFF8E1] font-semibold">
        <% if current_user&.super_admin? %>
          スーパーユーザー
        <% end %>
        <%= current_user.login_id %>でログイン中
      </span>
      <%= link_to "ログアウト >", destroy_user_session_path,
          class: "block px-4 py-2 text-[#FFF8E1] font-semibold rounded hover:bg-[#8D6E63]",
          data: { turbo_method: :delete, turbo_confirm: "ログアウトしてよろしいですか?" } %>
      <% if current_user&.super_admin? %>
        <%= link_to "新規登録 >", admin_new_user_path,
            class: "block px-4 py-2 text-[#FFF8E1] font-semibold rounded hover:bg-[#5D4037]" %>
      <% end %>
    <% else %>
      <%= link_to "ログイン", new_user_session_path, class: "block px-4 py-2 text-[#FFF8E1] font-semibold rounded hover:bg-[#8D6E63]" %>
    <% end %>
  </div>

  <!-- スクリプト -->
  <script>
    document.addEventListener("turbo:load", () => {
      const btn = document.getElementById("mobile-menu-button");
      const menu = document.getElementById("mobile-menu");
      const userBtn = document.getElementById("user-menu-button");
      const userMenu = document.getElementById("user-menu");

      // モバイルメニュー切り替え
      if (btn && menu) {
        if (!btn.dataset.bound) {
          btn.addEventListener("click", () => menu.classList.toggle("hidden"));
          btn.dataset.bound = "true";
        }
      }

      // PCプルダウン切り替え
      if (userBtn && userMenu) {
        if (!userBtn.dataset.bound) {
          userBtn.addEventListener("click", () => userMenu.classList.toggle("hidden"));
          userBtn.dataset.bound = "true";
        }
      }
    });
  </script>
</header>

ポイント解説

1. ログイン状況の表示

  • current_user.login_id を表示
  • スーパーユーザーなら バッジ 付き (<span class="bg-yellow-500 text-white ...">)

2. プルダウンメニュー

  • PCではユーザー名クリックで展開
  • モバイルではバーガーメニュー内に表示
  • Tailwindで影・角丸・ホバー効果を付与

3. ログアウト

  • link_to に data: { turbo_method: :delete, turbo_confirm: "..." } を指定
  • Turbo環境でも確認ダイアログが出て、DELETEリクエストでログアウト

4. イベント管理

  • turbo:load イベントで DOM を初期化
  • 多重バインド防止のため、dataset.bound フラグを使用
  • モバイルとPCプルダウンを別々に管理

デザイン改善ポイント

  • ring-1 ring-black ring-opacity-20 で プルダウンを浮かせる
  • 区切り線で メニュー項目を視覚的に分離
  • ホバー時の色を変えて ボタン感を出す
  • PC・モバイルで同じデザイン言語を使用

まとめ

このナビゲーションバーは、Rails 8.1 + Tailwind + Turbo環境で、スーパーユーザー判定・ログイン状況・プルダウン・モバイル対応を統合しています。

ポイントは:

  • Turbo 対応のイベント管理
  • プルダウンのスタイル整備
  • ユーザー権限による表示制御
  • 確認ダイアログ付きログアウト

この実装をベースに、さらにアイコンやアニメーションを追加すると、より洗練されたUIになります。

-Ruby, Ruby on Rails, Web開発, プログラミング