スマートフォンアプリとタブレットアプリでは、求められるUI/UXが若干異なります。タブレットアプリでは、より多くの情報が一画面中に表示できるのが魅力です。スマートフォンがユーザー向けで簡単に入力・閲覧できるアプリが向いているのに対して、タブレットアプリでは管理画面などデータが多い表示に向いています。

そこで今回はタブレットアプリに向いたUI/UXを提供するために、Bootstrapを使ったUIを紹介します。

Bootstrapの導入

BootstrapはCSS、JavaScriptファイルを読み込めば利用できます。基本形は以下のようになります。

<!doctype html>
<html lang="ja">
 <head>
   <!-- 文字エンコーディングをUTF-8に設定 -->
   <meta charset="utf-8">
   <!-- ビューポートの設定。画面幅に合わせて表示を最適化 -->
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <!-- ページのタイトル -->
   <title>Bootstrap demo</title>
   <!-- Bootstrap CSSファイルの読み込み -->
   <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
 </head>
 <body>
   <!-- ページの見出し -->
   <h1>Hello, world!</h1>
   <!-- Bootstrap JavaScriptファイルの読み込み -->
   <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
 </body>
</html>

ここにMonacaアプリとして必要な情報を盛り込むと以下のようになります。

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: content: https://ssl.gstatic.com; style-src * "unsafe-inline"; script-src * "unsafe-inline" "unsafe-eval"">
    <script src="components/loader.js"></script>
    <link rel="stylesheet" href="components/loader.css">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
    <link href="css/styles.css" rel="stylesheet" />
  </head>
  <body>
    <h1>Hello, world!</h1>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
  </body>
</html>

サンプル

今回はSB Admin - Free Bootstrap Admin Template - Start Bootstrapをベースに進めています。

サイドメニュー

タブレットを横向きにすると幅広く使えるので、左側にメニューを表示することが多いです。Bootstrapを使えば、以下のようにしてサイドメニューを簡単に作成できます。

利用するコンポーネントや機能は以下の通りです。メニューの折り畳みにアコーディオンを利用しています。

また、アイコンはFontawesomeを利用しています。

<body class="sb-nav-fixed">
    <!-- ナビゲーションバー -->
    <nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
        <!-- ナビゲーションバーのブランド -->
        <a class="navbar-brand ps-3" href="index.html">Start Bootstrap</a>
        <!-- サイドバーのトグルボタン -->
        <button class="btn btn-link btn-sm order-1 order-lg-0 me-4 me-lg-0" id="sidebarToggle" href="#!">
            <i class="fas fa-bars"></i>
        </button>
    </nav>

    <!-- レイアウトのコンテナ -->
    <div id="layoutSidenav">
        <!-- サイドナビゲーション -->
        <div id="layoutSidenav_nav">
            <nav class="sb-sidenav accordion sb-sidenav-dark" id="sidenavAccordion">
                <!-- サイドナビゲーションのメニュー -->
                <div class="sb-sidenav-menu">
                    <div class="nav">
                        <!-- メニューのセクション: Core -->
                        <div class="sb-sidenav-menu-heading">Core</div>
                        <a class="nav-link" href="index.html">
                            <div class="sb-nav-link-icon"><i class="fas fa-tachometer-alt"></i></div>
                            Dashboard
                        </a>

                        <!-- メニューのセクション: Interface -->
                        <div class="sb-sidenav-menu-heading">Interface</div>
                        <!-- Layoutsのメニュー項目 -->
                        <a class="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#collapseLayouts" aria-expanded="false" aria-controls="collapseLayouts">
                            <div class="sb-nav-link-icon"><i class="fas fa-columns"></i></div>
                            Layouts
                            <div class="sb-sidenav-collapse-arrow"><i class="fas fa-angle-down"></i></div>
                        </a>
                        <!-- Layoutsのサブメニュー -->
                        <div class="collapse" id="collapseLayouts" aria-labelledby="headingOne" data-bs-parent="#sidenavAccordion">
                            <nav class="sb-sidenav-menu-nested nav">
                                <a class="nav-link" href="layout-static.html">Static Navigation</a>
                                <a class="nav-link" href="layout-sidenav-light.html">Light Sidenav</a>
                            </nav>
                        </div>

                        <!-- Pagesのメニュー項目 -->
                        <a class="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#collapsePages" aria-expanded="false" aria-controls="collapsePages">
                            <div class="sb-nav-link-icon"><i class="fas fa-book-open"></i></div>
                            Pages
                            <div class="sb-sidenav-collapse-arrow"><i class="fas fa-angle-down"></i></div>
                        </a>
                        <!-- Pagesのサブメニュー -->
                        <div class="collapse" id="collapsePages" aria-labelledby="headingTwo" data-bs-parent="#sidenavAccordion">
                            <nav class="sb-sidenav-menu-nested nav accordion" id="sidenavAccordionPages">
                                <!-- Authenticationのメニュー項目 -->
                                <a class="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#pagesCollapseAuth" aria-expanded="false" aria-controls="pagesCollapseAuth">
                                    Authentication
                                    <div class="sb-sidenav-collapse-arrow"><i class="fas fa-angle-down"></i></div>
                                </a>
                                <!-- Authenticationのサブメニュー -->
                                <div class="collapse" id="pagesCollapseAuth" aria-labelledby="headingOne" data-bs-parent="#sidenavAccordionPages">
                                    <nav class="sb-sidenav-menu-nested nav">
                                        <a class="nav-link" href="login.html">Login</a>
                                        <a class="nav-link" href="register.html">Register</a>
                                        <a class="nav-link" href="password.html">Forgot Password</a>
                                    </nav>
                                </div>

                                <!-- Errorのメニュー項目 -->
                                <a class="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#pagesCollapseError" aria-expanded="false" aria-controls="pagesCollapseError">
                                    Error
                                    <div class="sb-sidenav-collapse-arrow"><i class="fas fa-angle-down"></i></div>
                                </a>
                                <!-- Errorのサブメニュー -->
                                <div class="collapse" id="pagesCollapseError" aria-labelledby="headingOne" data-bs-parent="#sidenavAccordionPages">
                                    <nav class="sb-sidenav-menu-nested nav">
                                        <a class="nav-link" href="401.html">401 Page</a>
                                        <a class="nav-link" href="404.html">404 Page</a>
                                        <a class="nav-link" href="500.html">500 Page</a>
                                    </nav>
                                </div>
                            </nav>
                        </div>

                        <!-- メニューのセクション: Addons -->
                        <div class="sb-sidenav-menu-heading">Addons</div>
                        <a class="nav-link" href="charts.html">
                            <div class="sb-nav-link-icon"><i class="fas fa-chart-area"></i></div>
                            Charts
                        </a>
                        <a class="nav-link" href="tables.html">
                            <div class="sb-nav-link-icon"><i class="fas fa-table"></i></div>
                            Tables
                        </a>
                    </div>
                </div>

                <!-- サイドナビゲーションのフッター -->
                <div class="sb-sidenav-footer">
                    <div class="small">Logged in as:</div>
                    Start Bootstrap
                </div>
            </nav>
        </div>
        <!-- 省略 -->
    </div>
</body>

また、このままでは動的な部分は動かないので、JavaScriptを以下のように記述します。クラスを変えることで、アコーディオンの開閉を制御しています。また、localStorageを使って開閉状態を保存しています。

// Cordovaを使用している場合は"deviceready"イベント、そうでない場合は"DOMContentLoaded"イベントを待つ
const event = window.cordova ? "deviceready" : "DOMContentLoaded";

// 指定されたイベントが発生した時に実行される関数を登録
document.addEventListener(event, event => {
    // "sidebarToggle"というIDを持つ要素を取得
    const sidebarToggle = document.body.querySelector("#sidebarToggle");

    // サイドバーのトグルボタンが存在しない場合は終了
    if (!sidebarToggle) return;

    // サイドバーのトグルボタンにクリックイベントのリスナーを追加
    sidebarToggle.addEventListener("click", event => {
        // ボタンのデフォルトの動作をキャンセル
        event.preventDefault();

        // <body>要素のクラスリストに"sb-sidenav-toggled"クラスを切り替えて、サイドバーの表示/非表示を切り替える
        document.body.classList.toggle("sb-sidenav-toggled");

        // サイドバーの状態をローカルストレージに保存
        localStorage.setItem("sb|sidebar-toggle", document.body.classList.contains("sb-sidenav-toggled"));
    });
});

カード型のコンポーネント

管理画面のUIでは、グラフや一覧などをコンポーネントとして表示することが多いです。Bootstrapを使えば、以下のようにしてカード型のコンポーネントを簡単に作成できます。

4つのコンポーネント

KPIなど、ダッシュボード中に集計された数値を表示する際に便利な形です。

<!-- カードを含む行 -->
<div class="row">
  <!-- 最初の列(Primary Card) -->
  <div class="col-xl-3 col-md-6">
    <div class="card bg-primary text-white mb-4">
      <div class="card-body">Primary Card</div>
      <div class="card-footer d-flex align-items-center justify-content-between">
        <a class="small text-white stretched-link" href="#">View Details</a>
        <div class="small text-white"><i class="fas fa-angle-right"></i></div>
      </div>
    </div>
  </div>

  <!-- 2番目の列(Warning Card) -->
  <div class="col-xl-3 col-md-6">
    <div class="card bg-warning text-white mb-4">
      <div class="card-body">Warning Card</div>
      <div class="card-footer d-flex align-items-center justify-content-between">
        <a class="small text-white stretched-link" href="#">View Details</a>
        <div class="small text-white"><i class="fas fa-angle-right"></i></div>
      </div>
    </div>
  </div>

  <!-- 3番目の列(Success Card) -->
  <div class="col-xl-3 col-md-6">
    <div class="card bg-success text-white mb-4">
      <div class="card-body">Success Card</div>
      <div class="card-footer d-flex align-items-center justify-content-between">
        <a class="small text-white stretched-link" href="#">View Details</a>
        <div class="small text-white"><i class="fas fa-angle-right"></i></div>
      </div>
    </div>
  </div>

  <!-- 4番目の列(Danger Card) -->
  <div class="col-xl-3 col-md-6">
    <div class="card bg-danger text-white mb-4">
      <div class="card-body">Danger Card</div>
      <div class="card-footer d-flex align-items-center justify-content-between">
        <a class="small text-white stretched-link" href="#">View Details</a>
        <div class="small text-white"><i class="fas fa-angle-right"></i></div>
      </div>
    </div>
  </div>
</div>

グラフ

グラフを表示するカードです。なお、グラフはChart.jsを使っています。

<!-- グラフを表示するカードのコンテナ -->
<div class="row">
  <!-- 左側のカード: エリアチャート -->
  <div class="col-xl-6">
    <div class="card mb-4">
      <div class="card-header">
        <i class="fas fa-chart-area me-1"></i>
        Area Chart Example
      </div>
      <div class="card-body">
        <!-- エリアチャートのキャンバス -->
        <canvas id="myAreaChart" width="100%" height="40"></canvas>
      </div>
    </div>
  </div>

  <!-- 右側のカード: 棒グラフ -->
  <div class="col-xl-6">
    <div class="card mb-4">
      <div class="card-header">
        <i class="fas fa-chart-bar me-1"></i>
        Bar Chart Example
      </div>
      <div class="card-body">
        <!-- 棒グラフのキャンバス -->
        <canvas id="myBarChart" width="100%" height="40"></canvas>
      </div>
    </div>
  </div>
</div>

このコードでは、rowクラスを持つ<div>要素が2つのカードを含んでいます。それぞれのカードはcol-xl-6クラスを持つ<div>要素によって分割され、左側にエリアチャート、右側に棒グラフが表示されます。

各カードには、cardクラスを持つ<div>要素があり、その中にcard-headerとcard-bodyクラスを持つ<div>要素が含まれています。card-headerにはグラフのタイトルとアイコンが表示され、card-bodyにはグラフを描画するための<canvas>要素が含まれています。

グラフの描画には、Chart.jsライブラリを使用しています。<canvas>要素のid属性を使って、JavaScript側からグラフを操作します。

データ一覧系

データの一覧はtableタグを使います。Bootstrapであれば、table表示も綺麗にできます。

<!-- データ一覧を表示するカード -->
<div class="card mb-4">
  <!-- カードのヘッダー -->
  <div class="card-header">
    <!-- ヘッダーのアイコン -->
    <i class="fas fa-table me-1"></i>
    <!-- ヘッダーのタイトル -->
    DataTable Example
  </div>

  <!-- カードの本体 -->
  <div class="card-body">
    <!-- データ一覧を表示するテーブル -->
    <table id="datatablesSimple">
      <!-- テーブルのヘッダー -->
      <thead>
        <tr>
          <th>Name</th>
          <th>Position</th>
          <th>Office</th>
          <th>Age</th>
          <th>Start date</th>
          <th>Salary</th>
        </tr>
      </thead>

      <!-- テーブルの本体 -->
      <tbody>
        <!-- データの行 -->
        <tr>
          <td>Tiger Nixon</td>
          <td>System Architect</td>
          <td>Edinburgh</td>
          <td>61</td>
          <td>2011/04/25</td>
          <td>$320,800</td>
        </tr>
        <!-- 以下省略 -->
      </tbody>
    </table>
  </div>
</div>

まとめ

サイドメニューとカード型コンポーネントを組み合わせることで、管理UIとしての見栄えを良くできるでしょう。Bootstrapを使えば、簡単に実装できるので、ぜひ活用してみてください。

今回試したSB Adminには、ログインやダーク/ライトレイアウトなど参考になりそうなUIが多数用意されています。こうしたテンプレートをベースにすれば、アプリ開発の効率化にもつながるでしょう。

はじめに · Bootstrap v5.3