Rails7.0+Devise4.9をBootstrap5でカッコよくする

# はじめに
[Rails 7.0.5、Bootstrap 5.2でDevise 4.9を動かす](https://qiita.com/NYC-Blue/items/847134d55047e7e9bc9e)の続きです。今回はBootstrap5で昔のRailsLayoutに相当する部分を作ります。

```ruby:環境
Rails 7.0.5
Ruby 3.1.2
Bootstrap 5.2.3
MySQL Ver 8.0.31 for macos13.0 on arm64 (Homebrew)
Mac OS Ventura 13.4
```

この記事が参考になって[VPSにデプロイ](https://qiita.com/NYC-Blue/items/f5ea17b76d024f4212f0)したいと思った方はぜひ以下のリンクからVultr VPSを申し込んでみて下さい。

激安VPSのVultrで[紹介者からのリンク](https://www.vultr.com/?ref=8478497)経由でアカウント登録すると＄100のクレジットをもらえるので、CentOS7, 8, MySQL 5.7, 8などの組み合わせをいろいろ試しました。このキャンペーンはいつ終わるか分からないので、興味のある方は以下のリンクからアカウント登録してください。あなたはクレジットをもらえるし、私にも多少のクレジットが入るらしいので、Win-Winです。

#### [このリンク](https://www.vultr.com/?ref=8478497)で＄100もらえます

![スクリーンショット 2023-06-11 午後12.24.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329289/d2f7138c-24e1-812e-9c27-cd1d31e9c6c3.png)

私もリファラー経由でアカウントを作ったので＄100もらえました。これで最初にあれやこれや試すのは無料でできます。
![VultrCredit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329289/41b4277c-2176-b2c6-3ed8-6207751e9b22.png)

関連記事
1. [Rails 7.0.5でBootstrap 5.2を動かす](https://qiita.com/NYC-Blue/items/3cd72f6e1e7cc19bc55a)
1. [Rails 7.0.5、Bootstrap 5.2でDevise 4.9を動かす](ttps://qiita.com/NYC-Blue/items/847134d55047e7e9bc9e)
1. Rails7.0+Devise4.9をBootstrap5でカッコよくする　（今回の記事）

# 1. Toastr
自分の趣味でトースターを入れてみた。

## (1) Toastrのインストール

```ruby:Gemfile
# Toastr
gem 'toastr_rails'
```

```ruby:rails_root
$ bundle install

Installing toastr_rails 2.1.3
Bundle complete! 24 Gemfile dependencies, 94 gems now installed.
Bundled gems are installed into `./vendor/bundle`
```

## (2) Toastrの構成

```ruby:app/assets/javascripts/application.js
//= require toastr_rails
```

```ruby:app/assets/stylesheets/application.scss
@import "toastr";
```

## (3) ViewにToasterを追加
`</body>`の上にスクリプトを追加。

```ruby:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Device490</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track' => 'reload' %>
  </head>

  <body>

    <%= yield %>

    <%# ------ Toastr ----- %>
    <% if flash.any? %>
    <script type="text/javascript">
        <% flash.each do |key, value| %>
        <% key = "success" if key == "notice" %>
        <% key = "error" if key == "alert" %>
        <% key = "error" if key == "warn" %>
        toastr['<%= key %>']('<%= value %>');
        <% end %>
    </script>
  <% end %>
  <%# ------ Toastr ----- %>

  </body>
</html>
```

## (4) 動作確認

動きました。

![スクリーンショット 2023-06-12 午前12.46.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329289/e65b3d70-bf9c-97cb-cf87-f2c79122bdd7.png)

# 2. Bootstrap5 Navbar
RailsLayoutがBootstrap5に対応してれば一発でとりあえずの雛形まで作成できるのだが、残念ながらRailsLayoutは開発が止まっているので、Bootstrap4対応のRailsLayoutで作成されたものをBootstrap5に書き換える。

## (1) Navbar
### application.html.erb
最上部にNavbarを出したいのでapplication.html.erbにNavbar用のERBファイルを呼び出すように`<%= render 'layouts/navigation' %>`を入れる。同時に`<body> </body>`の間にWrapperを入れておく。

```ruby:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
  <title>Device490</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <%= csrf_meta_tags %>
  <%= csp_meta_tag %>

  <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => 'reload' %>
</head>

<body>
<div id="wrapper"> <%# --- BEGIN of wrapper --- %>
  <header>
    <%= render 'layouts/navigation' %>
  </header>

  <%= yield %>

  <%# ------ Toastr ----- %>
  <% if flash.any? %>
    <script type="text/javascript">
        <% flash.each do |key, value| %>
        <% key = "success" if key == "notice" %>
        <% key = "error" if key == "alert" %>
        <% key = "error" if key == "warn" %>
        toastr['<%= key %>']('<%= value %>');
        <% end %>
    </script>
  <% end %>
  <%# ------ Toastr ----- %>

</div> <%# --- END of wrapper --- %>
</body>
</html>
```

### _navigation.html.erb

Navbarを作り出す部分がこのファイルとなる。細かい説明は省略するがBootstrap5公式サイト（あるいはその翻訳サイト）でNavbarのサンプルがあるので、ここではそれをほぼそのまま使っている。

```ruby:app/views/layouts/_navigation.html.erb
<nav class="navbar navbar-expand-lg bg-body-tertiary">
  <div class="container-fluid">
    <%= link_to 'MY COMPANY', root_path, class: 'navbar-brand' %>

    <button type="button" class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#Navber" aria-controls="Navber" aria-expanded="false" aria-label="ナビゲーションの切替">
      <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="Navber">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
        <li class="nav-item dropdown">
          <a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
            Menu
          </a>
          <ul class="dropdown-menu">
            <li><a class="dropdown-item" href="#">Item</a></li>
            <li><a class="dropdown-item" href="#">Item</a></li>
            <li><a class="dropdown-item" href="#">Item</a></li>
            <li><hr class="dropdown-divider"></li>
            <li><a class="dropdown-item" href="#">Item</a></li>
          </ul>
        </li>
        <li class="nav-item dropdown">
          <a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
            Menu
          </a>
          <ul class="dropdown-menu">
            <li><a class="dropdown-item" href="#">Item</a></li>
            <li><a class="dropdown-item" href="#">Item</a></li>
            <li><a class="dropdown-item" href="#">Item</a></li>
            <li><hr class="dropdown-divider"></li>
            <li><a class="dropdown-item" href="#">Item</a></li>
          </ul>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Menu</a>
        </li>
        <li class="nav-item">
          <a class="nav-link disabled">FAQ</a>
        </li>
      </ul>

      <%= render 'layouts/nav_links_for_auth' %>

    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>
```

### _nav_links_for_auth.html.erb

```ruby:app/views/layouts/_nav_links_for_auth.html.erb
<% if user_signed_in? %>
  <ul class="nav-item dropdown mb-2 mb-lg-0">
    <a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <%= t('nav_hello') %>
      <% if params[:locale] == 'ja' %>
        <%= current_user.LastName %><%= t('nav_honorific') %>
      <% else %>
        <%= current_user.FirstName %>
      <% end %>
    </a>
    <ul class="dropdown-menu">
      <li>
        <%= link_to root_path, class: 'dropdown-item' do %>
          <i class="fa-solid fa-tachograph-digital"></i> Menu
        <% end %>
      </li>
      <li>
        <%= link_to root_path, class: 'dropdown-item' do %>
          <i class="fa-solid fa-envelope"></i> Menu
        <% end %>
      </li>
      <li>
        <%= link_to destroy_user_session_path, data: { turbo_method: :delete }, class: 'dropdown-item' do %>
          <i class="fa-solid fa-right-from-bracket"></i>
          <span id="logout_link"><%= t('devise.shared.links.sign_out') %></span>
        <% end %>
      </li>
    </ul>
  </ul>
<% else %>
  <%= link_to t('devise.registrations.new.sign_up'), new_user_registration_path, class: 'btn btn-outline-primary' %>
  <ul class="nav-item dropdown mb-2 mb-lg-0">
    <a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <i class="fa-solid fa-user-large fa-lg"></i>
    </a>
    <ul class="dropdown-menu dropdown-menu-end">
      <li>
        <%= link_to new_user_session_path, class: 'dropdown-item' do %>
          <i class="fas fa-sign-in-alt ml-1"></i><span id="login_link"><%= t('devise.shared.links.sign_in') %></span>
        <% end %>
      </li>
    </ul>
  </ul>
<% end %>

```

### (2) font-awesome-sassをインストール
font-awesome-sassをインストールします。

```ruby:Gemfile
# Font Awesome
gem "font-awesome-sass", "~> 6.4.0"
```

```
$ bundle install

Installing font-awesome-sass 6.4.0
Bundle complete! 25 Gemfile dependencies, 95 gems now installed.
Bundled gems are installed into `./vendor/bundle`
```

```ruby:app/assets/stylesheets/application.scss
@import 'font-awesome';
```

ここまでの作業でTopページ画面はこんな感じです。

![スクリーンショット 2023-06-12 午前1.51.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329289/e6adf4d2-dc8e-4363-c81e-86b5573924bb.png)

### (3) Deviseの各Viewを中央寄せ
このままだとDeviseの各Viewが左寄せされてしまって見にくいので各Viewを`<div class="main-container mb-5">　〜</div>`で挟み込みます。このCSSも載せておきます。

例えばログイン画面は次の通りです。

```ruby:app/views/devise/sessions/new.html.erb
<div class="main-container mb-5">
  <div class="row">
    <div class="col-md-6">
      <h2 class="pb-4"><%= t('.sign_in') %></h2>

      <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
        <div class="mb-3">
          <%= f.label :email, class: "form-label" %>
          <%= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control' %>
        </div>

        <div class="mb-3">
          <%= f.label :password, class: "form-label" %>
          <%= f.password_field :password, autocomplete: "current-password", class: 'form-control' %>
        </div>

        <% if devise_mapping.rememberable? %>
          <div class="mb-3 form-check">
            <%= f.check_box :remember_me, class: 'form-check-input' %>
            <%= f.label :remember_me, class: 'form-check-label' %>
          </div>
        <% end %>

        <div class="mb-4 mt-4">
          <%= f.submit t('.sign_in'), class: "btn btn-primary" %>
        </div>
      <% end %>

      <%= render "devise/shared/links" %>
    </div>
  </div>
</div>
```

使っているSCSS
```ruby:app/assets/stylesheets/pages.scss
// Place all the styles related to the Pages controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/

.main-container-bg{
   padding-top: 28px; // 固定されたナビバーの高さの分だけ上にずれてしまうため、bodyのpadding-topで調整する。
   background:#ddd center center / cover no-repeat fixed;
   background-size:cover;
   width: 100vw;
   height: 100vh;
   position: relative;
   text-align: center;
}

.main-container {
  margin-top: 56px; // 固定されたナビバーの高さの分だけ上にずれてしまうため、bodyのpadding-topで調整する。
  margin-left: auto;
  margin-right: auto;
  width: 75%;
}

.sub-container{
  width: 100vw;
  text-align: center;
  position: absolute;
  top: 40%;
  -webkit-transform : translateY(-50%);
  transform : translateY(-50%);
  color: #FFFFFF;
  text-shadow:1px 1px 0 #000, -1px -1px 0 #000,
  -1px 1px 0 #000, 1px -1px 0 #000,
  0px 1px 0 #000,  0-1px 0 #000,
  -1px 0 0 #000, 1px 0 0 #000;
}

body,
#wrapper {
  //padding-top: 28px; // 固定されたナビバーの高さの分だけ上にずれてしまうため、bodyのpadding-topで調整する。
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background-color: white;
}

footer {
  margin-top: auto;
}

// For Devise Sign-in
.form-signin {
  width: auto;
  padding: 19px 29px 29px;
  margin: 40px auto 20px;
  background-color: #fff;
  border: 1px solid #e5e5e5;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
  -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
  -moz-box-shadow: 0 1px 2px rgba(0,0,0,.05);
  box-shadow: 0 1px 2px rgba(0,0,0,.05);
}

.form-signin .form-signin-heading,
.form-signin .checkbox {
  margin-bottom: 10px;
}

.form-signin input[type="email"],
.form-signin input[type="password"] {
  font-size: 16px;
  height: auto;
  margin-bottom: 15px;
  padding: 7px 9px;
}

// https://yukimasablog.com/rails-field-with-errors
.field_with_errors {
  display: contents;
  // もしくは diplay: inline;
}
```

上記CSSの読み込み
```ruby:app/assets/stylesheets/
@import 'pages';
```

ここまでの作業でログイン画面はこんな感じになりました。

![スクリーンショット 2023-06-12 午前2.11.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329289/60820fa8-64e1-b85d-a80d-1a31cb34224c.png)


# 3. 更なるカスタマイズ
ここから先は好みが分かれるでしょうし記事の趣旨であるRailsLayout相当の見た目のBootstrap5への実装としては上記までで終わりなのですが、Deviseのエラーメッセージの取り扱い等をもっとカッコよくするカスタマイズを次回の記事で書きたいと思います。