ドロワーメニュー

ページの隅っこにあるにほんせんさんぼんせんのアイコンをタップするとメニューがスライドしてくるの、よく見かけますね。引き出しみたいに出し入れできるメニューだからドロワーメニューとかって言います。ここではそんなドロワーメニューをCSSで作ってみます。

開閉する仕組み

メニューが開閉する仕組みは、これまでのサンプル同様<input><label>を使った方法で、今回はラジオボタンじゃなくて、チェックボックスを使います:)

ページ全体のHTMLの構造は以下の通り。

<body>
	<input type="checkbox" id="drawer">
	<label for="drawer" class="open"></label>
	<label for="drawer" class="close"></label>
	<nav class="menu">
		⋮
	</nav>
	<div class="contents">
		<header>
			⋮
		</header>
		<article>
			⋮
		</article>
		<footer>
			⋮
		</footer>
	</div>
</body>

メニューを開閉するためのボタンとなるlabel要素は2つ用意しておき、ひとつは開く・閉じるの両用、もうひとつを閉じる用としておきます。
両用の<label for="drawer" class="open">は、常に見えているボタンの形をしたもの。閉じる用の<label for="drawer" class="close">は、全面を覆うように固定配置しておきます。

そして、スイッチの役割となるチェックボックスは、ページの先頭に設置。

ドロワーメニューとページ全体の構造

これが:checkedになった時に、それ以降の兄弟要素<label><nav>のスタイルを変える仕組みです。

ドロワーメニューが左にスライドして、全面を覆う閉じるボタンがフェードイン

全面を覆っている閉じるボタンは、メニューが閉じている時はpointer-events: noneでポインターでの操作を無効にしておき、メニューが開いた時にはautoにして、クリックできるようにします。スライドショーの矢印ボタンの時と同じ方法ですね;)

.close {
	z-index: 1;
	inset: 0;
	pointer-events: none;
	transition: background-color .6s;
}
#drawer:checked ~ .close {
	pointer-events: auto;
	background-color: rgba(0,0,0,.3);
}

pointer-events: noneでポインターイベントが無効になるので、前面を覆っていても、その下のコンテンツには触ることができるってわけ:D

ドロワーメニューが開いたらスクロールをロックする

ドロワーメニューが開いている時には、下のコンテンツのスクロールを止めたいって時もあるでしょう。そんな時には、以下のように、ページ全体をひとつのdiv要素で括って、その要素内でスクロールするようにします。

<body>
	<div class="container">
		<input type="checkbox" id="drawer">
		<label for="drawer" class="open"></label>
		<label for="drawer" class="close"></label>
		<nav class="menu">
			⋮
		</nav>
		<div class="contents">
			⋮
		</div>
	</div>
</body>

body要素の内側のdiv要素.containerに、以下のようにスタイルを指定すれば、html要素ではスクロールしなくなり、.container内でスクロールするようになります。

.container {
	position: absolute;
	inset: 0;
	overflow: auto;
}

そうすると、ドロワーメニューが開いた時に、閉じるボタン用のlabel要素がページ全体を覆っているゆえ、下のコンテンツに触れなくなって、スクロールもできなくなります:o

閉じるボタンがコンテンツの上にかぶさって、コンテンツに触れない

ウィンドウの幅に合わせてメニューを使い分ける

ドロワーメニューは、画面が狭い時だけ使えればよくて、画面が広い時にはコンテンツ内にメニューを表示させときたいって時もあるでしょう。
そんな時はMedia Queriesメディアクエリという構文を使います。メディアクエリとは、メディアの特性に合わせてスタイルを出し分けるための方法。詳しくは後述
例えば、以下のように指定すれば、画面の横幅が520px以上の時にはドロワーメニューのアイコンが非表示になり、画面の横幅が519px以下の時にはコンテンツ内のメニューが非表示になります。

@media (min-width: 520px) {
	.open {
		display: none;
	}
}
@media (max-width: 519px) {
	header nav {
		display: none;
	}
}

上のサンプルでは、メディアクエリを使って、ブラウザの横幅に合わせて、普通なメニューを表示するスタイルと、ドロワーメニューを表示するスタイルを出し分けてるということになります。
ブラウザの横幅を狭くしていくと、コンテンツ内のメニューが消えて、ドロワーメニューのアイコンが現れます。

パッと切り替わるより、もうちょっと自然な感じに切り替わるようにしてみたいですね。CSSの指定を以下のように変えてみます。

.open {
	transform: translate(0, -100%);
}
#drawer:checked + .open {
	transform: translate(250px, 0);
}
@media (max-width: 480px) {
	.open {
		transform: translate(0, 0);
	}
}

display: noneで急に消すんじゃなくて、要素の透明度や位置を調整して隠すようにしてみました。それぞれtransitionプロパティを指定してゆっくり隠れるようにしています。