初めてでもわかるTailwindcss入門(2) ナビゲーションバー

本文書ではTailwindcssを利用してナビゲーションバーを作成していきます。ナビゲーションの作成を通してTailwindのutility classを利用したFlexbox、ブレイクポイント、レスポンシブデザインの設定方法について理解することができます。
Tailwindcssの経験がない人は本書に進む前に下記の文書を読んでおくことをおすすめします。
レスポンシブなナビゲーションバーの作成
Tailwindcssのutility classによる実践的なナビゲーションバーの作成を通して、Tailwindcssの理解を深めていきます。
Live Serverのインストール
Tailwindを利用した場合はhtmlファイルのclass属性にTailwindのutility classを追加していくため基本的にはhtmlファイル以外の更新はありません。htmlファイルの更新後にブラウザ画面の更新が自動で行われるようにLive Serverをインストールします。

$ npm install live-server
インストール完了後は下記のコマンドを実行するとブラウザが自動で起動します。ここで指定したpublicディレクトリにはindex.htmlファイルが保存されています。環境に依存するのでindex.htmlが保存されているパスを指定してください。
$ npx live-server public
Serving "public" at http://127.0.0.1:8080
Ready for changes
モバイルファースト
モバイルの画面を作成し、その後にPCの画面を作成していきます。ここからclassに設定しているクラスはすべてTailwindのユーティリティクラスです。
作業に入る前にcdnを利用して、Tailwindとvue.jsのタグをindex.htmlファイルに追加します。

下記のindex.htmlファイルから開始します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Navigation Menu</title>
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<header>
</header>
</div>
</div>
<script>
cost app = new Vue({
el: "#app",
data: {
}
});
</script>
</body>
</html>
サイト名とハンバーメニューの追加
サイト名はHRとしてハンバガーメニューのsvgを追加します。
<header>
<h1>HR</h1>
<div>
<button>
<svg class="h-6 w-6 fill-current" viewBox="0 0 24 24">
<path d="M24 6h-24v-4h24v4zm0 4h-24v4h24v-4zm0 8h-24v4h24v-4z"/>
</svg>
</button>
</div>
</header>

ブラウザで確認すると下記のように表示されます。ブラウザの表示画面を広げてもHRとハンバーガーメニューは左側の端に張り付いたままなのでコンテンツの中央寄せを行います。

containerというutility classが準備されており、mx-auto(margin:0 auto)と組み合わせると先ほどとは異なりコンテンツが中央寄せになることが確認できます。わかりやすいように背景色をつけています。containerでは最大の幅max-widthは1280pxに設定されているので、どんなにブラウザを横に広げても1280px以上にグレーの領域が増えることはありません。
<header class="container mx-auto">

Flexboxの設定
HRとハンバーガーメニューを左右両端に配置します。配置変更は、Flexboxを利用すれば簡単に設定することができます。
Flexboxを設定したい場合はutility classのflexを利用します。flexを設定する前はHRはh1タグ、ハンバーガーメニューはdivタグで囲まれているためBlock要素で縦に並んでいます。flexを設定すると縦に並んでいた要素が横並びに変わります。

Flexboxについてはこちらが参考になります。
flexを設定して要素は横並びになりました。横並びから左右の端にそれぞれの要素を配置したい場合は、Flexboxのjustify-contentのspace betweenを設定することになります。Tailwindはjustify-betweenというutility classがそれに対応します。
<header class="container mx-auto flex justify-between bg-gray-300">
justify-betweenの設定の結果、HRとハンバーガーメニューが左右に配置されました。

HRの文字の大きさを大きくして太さも変えます。
<h1 class="text-4xl font-semibold">HR</h1>

HRとハンバーガーメニューの中心が揃っていない場合はitems-center(align-items:center;)で揃えることができます。
<div class="bg-gray-500">
<header class="container mx-auto flex justify-between items-center text-white">

ここまで設定ができたので、背景色をmax-widthではなく幅全体に広げ、フォントも文字色も変更します。
<div class="bg-gray-500">
<header class="container mx-auto flex justify-between items-center text-white">
<h1 class="text-4xl font-semibold">HR</h1>

メニューを追加
メニューをハンバガーメニューのdivタグの後ろに追加します。
<header class="container mx-auto flex justify-between items-center text-white">
<h1 class="text-4xl font-semibold">HR</h1>
<div>
<button>
<svg class="h-6 w-6 fill-current" viewBox="0 0 24 24">
<path d="M24 6h-24v-4h24v4zm0 4h-24v4h24v-4zm0 8h-24v4h24v-4z"/>
</svg>
</button>
</div>
<ul>
<li><a href="#">HRとは</a></li>
<li><a href="#">機能一覧</a></li>
<li><a href="#">料金プラン</a></li>
<li><a href="#">お知らせ</a></li>
<li><a href="#">お問い合わせ</a></li>
</ul>
</header>
追加したulタグの親タグheaderにはflexが設定されているので、下記のように表示されます。

メニューはHRとハンバーガーメニューの下に表示させたいので、HRとハンバーメニュー、メニューを別のdivタグで囲い設定を変更します。
<header class="container mx-auto text-white">
<div class="flex justify-between items-center">
<h1 class="text-4xl font-semibold">HR</h1>
<div>
<button>
<svg class="h-6 w-6 fill-current" viewBox="0 0 24 24">
<path d="M24 6h-24v-4h24v4zm0 4h-24v4h24v-4zm0 8h-24v4h24v-4z"/>
</svg>
</button>
</div>
</div>
<div>
<ul>
<li><a href="#">HRとは</a></li>
<li><a href="#">機能一覧</a></li>
<li><a href="#">料金プラン</a></li>
<li><a href="#">お知らせ</a></li>
<li><a href="#">お問い合わせ</a></li>
</ul>
</div>
</header>
設定変更の結果、メニューが下に移動します。

メニューの距離が狭いのでpadding, marginの設定を行います。またulのliはインライン要素なのでブロック要素に変更します。
<ul>
<li><a href="#" class="block px-8 py-2">HRとは</a></li>
<li><a href="#" class="block px-8 py-2">機能一覧</a></li>
<li><a href="#" class="block px-8 py-2">料金プラン</a></li>
<li><a href="#" class="block px-8 py-2">お知らせ</a></li>
<li><a href="#" class="block px-8 py-2">お問い合わせ</a></li>
</ul>
px-8は、padding-left, padding-rightを2rem, py-2はpadding-top, padding-bottomに0.5remの設定です。

hoverの設定
メニューにカーソルが乗った時に色が変わるように擬似クラスhoverを設定します。
<ul>
<li><a href="#" class="block px-8 py-2 hover:bg-gray-600">HRとは</a></li>
<li><a href="#" class="block px-8 py-2 hover:bg-gray-600">機能一覧</a></li>
<li><a href="#" class="block px-8 py-2 hover:bg-gray-600">料金プラン</a></li>
<li><a href="#" class="block px-8 py-2 hover:bg-gray-600">お知らせ</a></li>
<li><a href="#" class="block px-8 py-2 hover:bg-gray-600">お問い合わせ</a></li>
</ul>
最後のメニューにカーソルが乗るとli要素の背景色が変わります。

hoverの背景色とheaderの背景色との間に空間がないのでmarginを使って領域を取りますがmarginが親要素からはみ出して表示されないので、親要素のheaderタグにpb-1(padding-bottom)を設定しています。overflow-hidden-yでも対応可能です。
<ul>
<li><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">HRとは</a></li>
<li><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">機能一覧</a></li>
<li><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">料金プラン</a></li>
<li><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">お知らせ</a></li>
<li><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">お問い合わせ</a></li>
</ul>
roundedでhoverの背景色の四角の角に丸みをつけています。

メニューの間にborderをつける
メニューの項目がはっきりわかるようにboderをつけます。boderの下線のみをつけるのでli要素にborder-bを設定します。最後のli要素には設定していません。

<ul>
<li class="border-b"><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">HRとは</a></li>
<li class="border-b"><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">機能一覧</a></li>
<li class="border-b"><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">料金プラン</a></li>
<li class="border-b"><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">お知らせ</a></li>
<li><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">お問い合わせ</a></li>
</ul>

ここまでで一度モバイル向けからPC向け画面の作成に進みます。
ブレイクポイント
アクセスするブラウザのサイズによってスタイルの変更ができるようにTailwindでは4つのブレイクポイントが準備されています。これらの4つのブレイクポイントをutility classの前にsm:text-bold, lg:flexのように付与することで設定を行うことができます。
- sm: 640px
- md: 768px
- lg: 1024px
- xl: 1280px
例えば、h1タグのHRの文字を768px以上で小さな文字に変更するためmd:text-xlを設定して動作を確認してみましょう。1つのclassの中にtext-4xlとmd:text-xlを追加します。
<h1 class="text-4xl font-semibold md:text-xl">HR</h1>
768px以上のサイズにブラウザの幅を変更するとHRの文字のサイズが小さくなります。

さらに動作確認するためにブレイクポイントのlgを設定し、1024px以上で背景色を変更してみます。
<div class="bg-gray-500 lg:bg-blue-500">
ブラウザのサイズが1024px以上になると背景色がグレーからブルーに変わります。またサイズを元に戻すとグレーに戻ります。

ブレイクポイントmdであれば、768px以上のサイズにはすべて適用されます。もし、lgの1024px以上でmdで設定した値を変更したい場合はlgで設定を行う必要があります。mdで設定すれば768px以上1024px未満のサイズに適用されるわけではありません。
プレイクポイントを設定していないutility classについてはすべてのサイズで適用されます。
PC画面の作成
ブレイクポイントがわかったので、今回はmdである768px以上をPC用画面として設定を行っていきます。
ハンバーメニューを非表示に
PCの画面ではハンバーメニューは必要ないので非表示にします。非表示にしたい要素にはhiddenを設定します。md以降のみに設定を追加したいためutility classの前には必ずmd:をつけます。
<button>
<svg class="h-6 w-6 fill-current" viewBox="0 0 24 24">
<path d="M24 6h-24v-4h24v4zm0 4h-24v4h24v-4zm0 8h-24v4h24v-4z"/>
</svg>
</button>
768px以上のサイズに変更すると右側からハンバーガメニューが非表示になります。768pxより小さいサイズではハンバーガーメニューは表示されます。

メニューを横並びに
モバイル画面ではメニューを縦に表示していましたが、PCの画面ではFlexboxを利用して横表示に変更します。
flexでの横並びと同時にメニューのborderも非表示にします。borderを無効にする場合はborder-noneを設定します。

<ul class="md:flex">
<li class="border-b md:border-none"><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">HRとは</a></li>
<li class="border-b md:border-none"><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">機能一覧</a></li>
<li class="border-b md:border-none"><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">料金プラン</a></li>
<li class="border-b md:border-none"><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">お知らせ</a></li>
<li><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">お問い合わせ</a></li>
</ul>
ブラウザで確認するとメニューが横並びになっていることが確認できます。

メニューの並び方の位置についてはFlexboxの機能を使って変更することが可能です。
中央に表示させたい場合は、justily-centerを使います。
<ul class="md:flex md:justify-center">

間隔を均等にして表示させたい場合はjustify-aroundを使います。
<ul class="md:flex md:justify-around">

メニューを右端に表示させたい場合は、justify-endを使います。
<ul class="md:flex md:justify-end">

お問い合わせメニューをボタンへ
お問い合わせメニューのみ目立つようにボタンに変更します。ボタンを追加するとボタンに設定したpaddingによってメニューの中央揃いがずれてしまうのでli要素が中央揃えになるようにflexを設定したul要素にitems-centerも追加します。
<ul class="md:flex md:justify-end md:items-center">
・
・
<li>
<div class="my-4">
<a href="#" class="px-6 py-2 bg-orange-500 hover:bg-orange-400 rounded-full">お問い合わせ</a>
</div>
</li>
右側にお問い合わせボタンが表示されます。カーソルをボタンの上に乗せるとhoverにより色がかわります。

PCの画面にボタンを追加したのでモバイル画面も確認しておきます。追加したお問合ボタンが窮屈に感じられるので表示場所と要素同士の間隔を変更します。

お問い合わせボタンはインライン要素なので親要素にtext-centerを設定し真ん中に表示させます。marginが少ないのでmarginも追加します。
<li>
<div class="my-8 text-center md:my-4">
<a href="#" class="px-6 py-2 bg-orange-500 hover:bg-orange-400 rounded-full">お問い合わせ</a>
</div>
</li>

vue.jsを使ってメニューの開閉
メニューの開閉設定
モバイルの画面ではメニューが開いたままの状態になっているので、開閉を行うためにvue.jsを利用します。
vue.jsにデータプロパティisOpenを追加し、isOpenの真偽によりメニューの開閉を行うように設定していきます。
vueにisOpenを追加し、デフォルト値はfalseとします。
<script>
const app = new Vue({
el : '#app',
data: {
isOpen: false,
}
})
</script>
クラス属性のバインドを利用して、isOpenがtrueの時はutility classのblockを適用、falseの時はhiddenを適用します。メニュー要素ulの親要素のdivに設定します。
<div :class="isOpen ? 'block' : 'hidden'" ">
<ul class="md:flex md:justify-end md:items-center">
<li class="border-b md:border-none"><a href="#" class="block px-8 py-2 my-4 hover:bg-gray-600 rounded">HRとは</a></li>
ハンバーガーメニューを押した時に開閉が行えるようにクリックイベント設定してます。
<button @click="isOpen = !isOpen">
<svg class="h-6 w-6 fill-current" viewBox="0 0 24 24">
<path d="M24 6h-24v-4h24v4zm0 4h-24v4h24v-4zm0 8h-24v4h24v-4z"/>
</svg>
</button>
動作確認を行ってみましょう。
ページを開いた直後は閉じた状態です。ハンバガーメニューをクリックするとメニューが表示されます。

開いた状態から再度ハンバーガーメニューをクリックすると閉じることを確認してください。

ハンバーガーメニューの画像の切り替え
開いた時にハンバーガーメニューをXにするように新たにXのsvgを追加します。ハンバーガーメニューの下に追加し、v-showディレクティブを利用して表示・非表示を切り替えます。
<button @click="isOpen = !isOpen">
<svg class="h-6 w-6 fill-current" viewBox="0 0 24 24">
<path v-show="!isOpen" d="M24 6h-24v-4h24v4zm0 4h-24v4h24v-4zm0 8h-24v4h24v-4z"/>
<path v-show="isOpen" d="M24 20.188l-8.315-8.209 8.2-8.282-3.697-3.697-8.212 8.318-8.31-8.203-3.666 3.666 8.321 8.24-8.206 8.313 3.666 3.666 8.237-8.318 8.285 8.203z"/>
</svg>
</button>
上記の設定を行うと開いた時には画像がXに切り替わります。

クリックした際にハンバーガーメニューに四角枠が表示されるので、擬似クラスfoucsを設定して表示されないようにします。
<button @click="isOpen = !isOpen" class="focus:outline-none">

PCの画面ではメニューは常時表示させる必要があるので、md:blockを追加します。
<div :class="isOpen ? 'block' : 'hidden'" class="md:block">
ここまでの設定でTailwindを学びながらナビゲーションバーを作成することができました。
コンテンツ領域追加の設定
コンテンツ領域の追加
ナビゲーションバーの作成の次はその下にコンテンツを入れていきます。<div id=”app”>の直下にコンテンツ領域を追加します。追加した要素にのcontainerとmx-autoを設定します。
</header>
</div>
<div class="container mx-auto">
<p>コンテンツ領域</p>
</div>

このままコンテンツを下に追加することもできますが、メニューをオープンするとオープンしたメニューの下にコンテンツの領域が移動します。

コンテンツの内容をメニューの真下(コンテンツの内容がある階層がメニューの階層の下になる)になるようにスタイルを変更していきます。
fixedの設定
コンテンツ領域とメニューの領域を重ねるためにfixedを利用します。メニューの領域にfixedを追加します。
<div :class="isOpen ? 'block' : 'hidden'" class="fixed md:block">
TailwindだけではなくCSSのpositionの理解も必要になります。
fixedを設定するとメニューの領域に色の設定が行われていなため透明になります。しかしその下にコンテンツ領域が入っていることを確認できます。

メニューに背景色を設定します。
<div :class="isOpen ? 'block' : 'hidden'" class="fixed bg-gray-500 md:block">

fixedを設定すると幅が文字列の長さになってしますため、widthを100%に設定する必要があります。widthを100%にするためにw-fullを追加します。
<div :class="isOpen ? 'block' : 'hidden'" class="fixed w-full bg-gray-500 md:block">
左側に空白がありますが、コンテンツ領域はメニューの下にあることはわかります。

左端の空白を埋めるため、left-0を設定します。
<div :class="isOpen ? 'block' : 'hidden'" class="fixed left-0 w-full bg-gray-500 md:block">

きれいに下のコンテンツを隠すことができました。
ここで一度PC画面に戻りましょう。先程までの両端にスペースがありましたが、どこまでサイズを広げてもお問い合わせボタンは右にくっついたまま、またコンテンツ領域も消えています。

これはfixedが設定されたままになっているためです。mdではfixedからデフォルトのstaticを設定します。
<div :class="isOpen ? 'block' : 'hidden'" class="fixed w-full left-0 bg-gray-500 md:block md:static">
元のように右側にも領域があり、コンテンツ領域が表示されました。

コンテンツ領域をもう少し拡大して動作確認を行います。pタグで20行のダミー業を追加します。メニューの下からダミー行が表示されているので正常に動作しているように思えます。

ブラウザの縦の大きさを少し小さめにしてスクロールを行ってください。スクロールを行うと上部にコンテンツの中身が表示されます。メニューはfixedで固定されていますが、ロゴとハンバーガメニューの領域は固定されていないため、スクロールを行うとブラウザの画面上から消えるためです。

これを避けるためにはHRのロゴ領域も固定させる必要があります。
先程でfixedの方法は学んでいるのでfixed w-full left-0 bg-gray-500の設定をまず行います。
<div class="flex justify-between items-center fixed w-full left-0 bg-gray-500">
設定を追加した結果、ロゴ領域がfixedとして通常の場所から外れたことで下にあったメニューが上部に来て、ロゴ領域に重なっているためHRの文字列を確認することができません。

もし重なっていることを確認したいのであれば、メニューの背景色を削除してください。背景色がなくなると下の層に存在するロゴ領域が確認できます。

メニュー領域にmt-16(margin-top)を設定することでロゴ領域が見えるようになるので設定を行います。背景色も元に戻しておきます。
<div :class="isOpen ? 'block' : 'hidden'" class="fixed left-0 w-full mt-16 bg-gray-500 md:block md:static">
ブラウザで確認するとロゴ領域が表示されているのがわかります。

mt-16に設定しているので、ロゴ領域の高さをh-16に設定します。
<div class="flex justify-between items-center fixed w-full left-0 h-16 bg-gray-500">
h-16を追加すると2つの領域間の空間はなくなります。

コンテンツ領域の上にもmt-16を設定しておきます。設定しないとコンテンツの上部がロゴ領域の下に隠れていまいます。
<div class="container mx-auto mt-16">
この状態だとスクロールしても問題なく動作します。HRとハンバーガーメニューが両端に隣接しているのpx-2を追加しておきます。
<div class="flex justify-between items-center fixed w-full left-0 h-16 bg-gray-500 px-2">
PC画面に戻すとデザインが崩れています。

崩れたデザインはfixedやmarginなどを解除すると直すことができます。
ロゴ領域のfixedをmdでstaticへ。
<div class="flex justify-between items-center fixed w-full left-0 h-16 bg-gray-500 px-2 md:static">
メニュー領域のmt-16をmdでmt-0へ。
<div :class="isOpen ? 'block' : 'hidden'" class="fixed left-0 w-full mt-16 bg-gray-500 md:block md:static md:mt-0">
コンテンツ領域のmt-16をmdでmt-0へ。
<div class="container mx-auto mt-16 md:mt-0">
mdブレイクポイントでの設定を反映すると元の状態に戻ります。

marignやpaddingについては調整を行ってください。
メニューにスクロールバーを表示
メニューがブラウザの画面に収まりないような下記のような場合に右側にスクロールバーを表示させる設定を行います。
スクロールバーを表示させるためには、hight:100%、overflow-y-scrollを設定します。また、mdでは解除できるようにmd:overflow-visibleを設定しておきます。
<div :class="isOpen ? 'block' : 'hidden'" class="fixed left-0 w-full mt-16 bg-gray-500 h-full overflow-y-scroll md:overflow-visible md:block md:static md:mt-0">
下記のようにお問い合わせボタンまでスクロールできるように親要素のulにpb-24(padding-bottom)を追加しておきます。mdブレイクポイントではそのpaddingを解除しておきます。
<ul class="md:flex md:justify-end md:items-center pb-24 md:pb-0">
