CSSのみで滑らかなアコーディオンを実現

他でも良く見かけるネタなので、単なる備忘録としてのエントリーですが・・・。
実装するとこんな感じです↓

元々はいくつか管理しているwordpressサイトで、長ったらしいQ&Aを折りたたみたいというニーズから使用したものです。 便利なjQuery系でも色々と綺麗なものがあるんですが、色々とプラグインを入れていたり、Q&A自体をプラグインで実現していたりする都合上あまり面倒なものは使いたくなかったのです。 装飾の関係でCSSは若干長くなっていますが、このまま使ってもらってもいいと思います。

ラベル部分にはこれもまたCSSのみでグラデーション背景を適用しています。
また僕がさっそく躓いたのは、エントリーの中で実装しようとした時に変な隙間ができてしまったこと。→wordpressが自動整形機能で余計なbrやpタグを突っ込んでくれていました。
function.phpに記述してもいいみたいですが、プラグインで解決しちゃいました。
プラグイン万歳

<section class="ac-container">
	<div>
		<input id="ac-1" name="xxxx" type="checkbox" />
		<label for="ac-1">タイトル1</label>
		<article class="acheight100"><p>本文1</p></article>
	</div>
	<div>
		<input id="ac-2" name="xxxx" type="checkbox" />
		<label for="ac-2">タイトル1</label>
		<article class="acheight100"><p>本文1</p></article>
	</div>
</section>

※コードエリアをダブルクリックでコード全体を選択できます
ac-xのx部分にアコーディオンのユニークナンバーを入れてあげるといいでしょう。
inputタグ内のidとlabelタグ内のforは同じ名前にしてください。またこれらはユニークなものにしないと、挙動がおかしくなるので注意します。
またarticleタグのclass属性ですが、いくつかの種類の高さ別にスタイルシートを用意しています。
ここの高さをきちんと指定しないとスムースに動いてくれないので、内容ごとに適切な高さを選んであげるのがいいでしょう。このあたりはスマートなやり方ではないのですが、若干地味な作業を加えることによってシンプルな仕組みで綺麗な動作を確保しています。

.ac-container{
	width: 90%;
	margin: 10px auto; 30px auto;
	text-align: left;
}
.ac-container label{
	margin: 0px;
	padding: 5px 20px;
	position: relative;
	z-index: 20;
	display: block;
	min-height: 30px;
	cursor: pointer;
	color: #777;
	text-shadow: 1px 1px 1px rgba(255,255,255,0.8);
	line-height: 33px;
	font-size: 19px;
	background: #ffffff;
	background: -moz-linear-gradient(top, #ffffff 1%, #eaeaea 100%);
	background: -webkit-gradient(linear, left top, left bottom, color-stop(1%,#ffffff), color-stop(100%,#eaeaea));
	background: -webkit-linear-gradient(top, #ffffff 1%,#eaeaea 100%);
	background: -o-linear-gradient(top, #ffffff 1%,#eaeaea 100%);
	background: -ms-linear-gradient(top, #ffffff 1%,#eaeaea 100%);
	background: linear-gradient(top, #ffffff 1%,#eaeaea 100%);
	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eaeaea',GradientType=0 );
	box-shadow: 
		0px 0px 0px 1px rgba(155,155,155,0.3), 
		1px 0px 0px 0px rgba(255,255,255,0.9) inset, 
		0px 2px 2px rgba(0,0,0,0.1);
}
.ac-container label:hover{
	background: #fff;
}
.ac-container input:checked + label,
.ac-container input:checked + label:hover{
	background: #c6e1ec;
	background: -moz-linear-gradient(top,  #c6e1ec 0%, #99b9db 100%);
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#c6e1ec), color-stop(100%,#99b9db));
	background: -webkit-linear-gradient(top,  #c6e1ec 0%,#99b9db 100%);
	background: -o-linear-gradient(top,  #c6e1ec 0%,#99b9db 100%);
	background: -ms-linear-gradient(top,  #c6e1ec 0%,#99b9db 100%);
	background: linear-gradient(to bottom,  #c6e1ec 0%,#99b9db 100%);
	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#c6e1ec', endColorstr='#99b9db',GradientType=0 );
	color: #3d7489;
	text-shadow: 0px 1px 1px rgba(255,255,255, 0.6);
	box-shadow: 
		0px 0px 0px 1px rgba(155,155,155,0.3), 
		0px 2px 2px rgba(0,0,0,0.1);
}
.ac-container label:hover:after,
.ac-container input:checked + label:hover:after{
	content: '';
	position: absolute;
	width: 20px;
	height: 24px;
	right: 13px;
	top: 10px;
	background: transparent url(images/arrow_down_s.png) no-repeat center center;	
}
.ac-container input:checked + label:hover:after{
	background-image: url(images/arrow_up_s.png);
}
.ac-container input{
	display: none;
}
.ac-container article{
	margin-top: -1px;
	overflow-y: auto;
	height: 0px;
	position: relative;
	z-index: 10;
	-webkit-transition: height 0.3s ease-in-out, box-shadow 0.6s linear;
	-moz-transition: height 0.3s ease-in-out, box-shadow 0.6s linear;
	-o-transition: height 0.3s ease-in-out, box-shadow 0.6s linear;
	-ms-transition: height 0.3s ease-in-out, box-shadow 0.6s linear;
	transition: height 0.3s ease-in-out, box-shadow 0.6s linear;
	background: #ffffff;
	background: -moz-linear-gradient(top,  #ffffff 0%, #efefef 100%);
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#efefef));
	background: -webkit-linear-gradient(top,  #ffffff 0%,#efefef 100%);
	background: -o-linear-gradient(top,  #ffffff 0%,#efefef 100%);
	background: -ms-linear-gradient(top,  #ffffff 0%,#efefef 100%);
	background: linear-gradient(to bottom,  #ffffff 0%,#efefef 100%);
	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#efefef',GradientType=0 );
}
.ac-container input:checked ~ article{
	-webkit-transition: height 0.3s ease-in-out, box-shadow 0.1s linear;
	-moz-transition: height 0.3s ease-in-out, box-shadow 0.1s linear;
	-o-transition: height 0.3s ease-in-out, box-shadow 0.1s linear;
	-ms-transition: height 0.3s ease-in-out, box-shadow 0.1s linear;
	transition: height 0.3s ease-in-out, box-shadow 0.1s linear;
    box-shadow: 0px 0px 0px 1px rgba(155,155,155,0.3);
}
.ac-container article p{
    color: #777;
    line-height: 18px;
    font-size: 12px;
    padding: 20px;
    text-shadow: 1px 1px 1px rgba(255,255,255,0.8);
}
.ac-container input:checked ~ article.acheight100{height: 100px;}
.ac-container input:checked ~ article.acheight150{height: 150px;}
.ac-container input:checked ~ article.acheight200{height: 200px;}
.ac-container input:checked ~ article.acheight250{height: 250px;}
.ac-container input:checked ~ article.acheight300{height: 300px;}
.ac-container input:checked ~ article.acheight350{height: 350px;}
.ac-container input:checked ~ article.acheight400{height: 400px;}
.ac-container input:checked ~ article.acheight450{height: 450px;}
.ac-container input:checked ~ article.acheight500{height: 500px;}
.ac-container input:checked ~ article.acheight600{height: 600px;}
.ac-container input:checked ~ article.acheight700{height: 700px;}
.ac-container input:checked ~ article.acheight800{height: 800px;}
.ac-container input:checked ~ article.acheight900{height: 900px;}
.ac-container input:checked ~ article.acheight1000{height: 1000px;}
.ac-container input:checked ~ article.acheight1100{height: 1100px;}
.ac-container input:checked ~ article.acheight1200{height: 1200px;}
.ac-container input:checked ~ article.acheight1300{height: 1300px;}
.ac-container input:checked ~ article.acheight1400{height: 1400px;}
.ac-container input:checked ~ article.acheight1500{height: 1500px;}
.ac-container input:checked ~ article.acheight1600{height: 1600px;}
.ac-container input:checked ~ article.acheight1700{height: 1700px;}
.ac-container input:checked ~ article.acheight1800{height: 1800px;}
.ac-container input:checked ~ article.acheight1900{height: 1900px;}
.ac-container input:checked ~ article.acheight2000{height: 2000px;}
.accordion input:checked ~ article.acheightauto{height: auto;}

非常にきれいではないコードで恐縮ですが、このまますぐに使えるように実際のものを貼り付けています。
一番行数を占めているのがブラウザごとの指定ですね。
一つは背景のグラデーション、もう一つは開閉のイージング処理です。

ラベル右端の矢印は^を画像として使っています。
下向き矢印上向き矢印上向き矢印オリジナル色 よろしければダウンロードしてお使いください。
画像は透過PNGとなっています。 
大したことありませんがクラウンクラウンオリジナル画像なので、気にせず使ってください。
白の上向き矢印はわかりやすく背景を黒にしていますが、黒い部分は透過しています。

coliss | [CSS]この発想はなかった!inputとlabelを使って実装する美しいデザインのアコーディオン
ColorZilla. | Ultimate CSS Gradient Generator  
今回は実をいうとCSSだけで実装しているということも意味がありますが、コリスさんのタイトルにあるように、inputとlabelでこの動作を実現しているというのがキモですね。
またアコーディオンごとにスタイルを変えたければ、sectionタグのclass属性を変えてしまえばいいですね。
もしくはlabelやarticleに背景等を指定するクラスを割り当ててあげてもいいと思います。
ここまでコンパクトに畳めると、「続きを読む」を設定しなくてもいいかもしれませんね。

シンプルな仕組みなのでそれほど問題はないだろうと思っていたのですが・・・。記事を投稿してから気づいた問題点をいくつかまとめます。
1.iPadで反応しない!
これは読んで字のごとく、僕は新iPad(第3世代)を利用していますが、iPadではSafariでもChromeでもアコーディオンが開きません。なんとかホバー状態にはなりますが、タップしてもダメ・・・・。
ちなみにGALAXY-S3では標準ブラウザでもChromeでもちゃんと開きました。
2.レスポンシブデザインの時に内容が表示しきれなくなる
すっかり忘れていましたが、このサイトもこのエントリーを書いている現在はレスポンシブデザインになっています。
つまりブラウザの大きさや端末によって幅が変わってしまうのです。
すると高さをピクセル指定しているものですから、改行が多くなると表示しきれなくなってしまうということが起きています。

iPadでうまく動作しなかったのは、classのネーミングに原因があったようです。sectionのclass名を最初はaccordionにしていたのですが、どこかでその名前が使われていたのでしょうか。また次にcss-ac-containerとするとPC上と、iPadのSafariでは問題なかったのですが、iPadのChromeでは動作しないどころかスタイルが全て適用されませんでした。いろいろ試していますが、Chromeのバグの様です。回避する確実な方法を探しましたが、なかなか見つからないのであきらめました。
また表示しきれなくなる問題対策に

    overflow-y: auto;
と入力してありますが、スクロールバーを表示したくなければ
    overflow: hidden;
に変更しましょう。

  • Pingback: Bookmarks for 4月 13th through 4月 19th()

  • 参考にさせていただき、エントリーにではなくブログのサイドバーに使用させていただきました。大変助かりました。ありがとうございます^^
    そして、勝手ながらその方法を記事にさせていただきました(勿論こちらの名前とリンク付き)。
    ご迷惑でしたら削除しますので、言って下さい。

    • わざわざのご報告ありがとうございます。
      個人的備忘録的なものでしたがお役にたてたなら何よりです。
      お使いになってみて問題点などありましたらフィードバックいただけると助かります。