detailsタグで作るアコーディオン。CSSだけで実装可。アクセシビリティにも対応
当サイトのリンクには広告が含まれています。
よくある質問(FAQ)の実装で重宝するアコーディオン。クリックすると回答の詳細が開くUIです。
今までは開閉の切替をJavaScriptで行うのが主流でしたが、detailsタグならCSSだけでサクッと実装出来ます。しかも標準で「キーボード操作・スクリーンリーダーに対応」し、アクセシビリティ対応も優秀。デモを交えながら解説していきます。
今まではdlタグにJavaScriptを付与して実装
皆さん今までアコーディオンって何のタグで作ってきましたか?
自分の観測範囲だとdiv
もしくは定義リストのdl
タグで囲み、dt
をJavaScriptでクリック判定。隣接したdd
をtoggle機能でスライドアウトさせる……といった手法を多く見てきました。自分もそのうちの一人。
もしくはCSSだけで実装するテクニックとして、空のcheckbox
をopacity:0;
で隠しinput[type=checkbox]:checked + ~
で開閉を切り替えるといったトリッキーな手法もよく見ます。
アクセシビリティに配慮しつつ、制作が容易な形の模索
しかしこういった実装では、キーボード操作やスクリーンリーダーに対応せず、アクセシビリティの面で不利な事がわかりました。
これからのWeb制作はアクセシビリティへの配慮が標準で求められるので、何か新しいやり方を模索しなければなりません。
しかしどうするか。単純にdt
タグをJavaScriptでキー対応させる?もしくは見出し部分をbutton
タグに切り替え?うーんでもHTMLの文章構造としてどうなんだろう。
そんなこんなで「アクセシビリティに配慮」しつつ「制作も容易」「トリッキーな構造にしない」というコンセプトを検討したところ、HTML5の新要素にピッタリなものがある事がわかりました。それがdetails
タグです。
これからはdetailsタグの出番
details
タグはHTML5から追加された「詳細情報を示す」要素。
HTML要素だけで開閉機能を持ち、クリックすると詳細情報が表示されます。さらに子要素にsummary
タグを用いると、その内容が要約となり、ラベルとして利用できます。
デモ(初期状態)
See the Pen (デフォルト)detailsタグで作るアコーディオン by MEMORUKA (@memoruka) on CodePen.
キーボード操作にも対応
デモはCSS無しのデフォルトのHTML。これだけで開閉ギミックが機能します。JavaScriptいらず。そして開閉は標準でキーボード操作に対応。さらにスクリーンリーダーで開閉状態が読み上げられる。これを求めていた!
さらに良いと思った点が「非表示状態のテキストにもブラウザのキーワード検索がヒットする※」ところ。従来のtoggle方式では非表示の要素はdisplay:none;
の状態だっため、検索してもヒットしませんでした。FAQ(よくある質問)で重宝されるUIなのに、ユーザーには使い勝手が悪いですよね。その点も解消されるのがGOOD👍️
※Chrome、Edge
WordPress(Gutenberg)にブロックとして標準搭載しているって知ってた?
そしてWebサイト制作の現場で避けて通れないWordPress(Gutenberg)への対応。なんとdetails
タグは標準でブロックとして実装されています。え。今まで全然気が付かなかった。。GutenbergでFAQのデザインをどう再現するか。必死に複数のブロックをグループ化して作っていたのは何だったのか。
やっぱりアップデートの度に一度は全機能触らないとダメですね。。
CSSで装飾し、実際に作っていく
では先程の素のHTMLをCSSで装飾していきます。
デモ(CSSで装飾)
See the Pen (CSSで装飾)detailsタグで作るアコーディオン by MEMORUKA (@memoruka) on CodePen.
よくある形式に合わせ「QA」の表示と、開閉の状態を表す「矢印」を付けてみました。すべてCSSのみで制作しています。
解説:デフォルトの三角形アイコンを消す
summary
タグには開閉状態を表す三角形▶がデフォルトで表示されます。矢印はオリジナルのものを使いたいので、まずブロック要素に変換してこれを削除します。
summary {
/* display: list-item;以外を指定し、デフォルトの三角形アイコンを削除 */
display: block;
}
しかし早速トラブル発生。令和のIE6(偏見)ことSafariだけ独自の仕様で実装されているため、三角形が消えません。よってベンダープレフィックスで個別対応。
summary::-webkit-details-marker {
display: none;
}
くの字矢印をclip-pathで制作
オリジナルの矢印を疑似要素(after)で制作します。今回はこれもCSSだけで実装。clip-path
を使えば、通常の三角形だけではなく「くの字型」も制作出来ます。
details summary::after {
content: "";
width: 18px;
aspect-ratio: 1/.6;
background: #0A64BE;
clip-path: polygon(100% 13%, 50% 100%, 0 13%, 8% 0, 50% 73%, 92% 0);
/* ~ ※三角形以外のプロパティは省略 ~ */
}
勿論画像を用意してbackground
での指定も可。その辺はお好みで。
別案:borderを回転させて作る
details summary::after {
content: "";
display: block;
width: 26px;
aspect-ratio: 1/1;
border-right: 4px solid #0A64BE;
border-bottom: 4px solid #0A64BE;
transform: rotate(45deg);
/* ~ ※三角形以外のプロパティは省略 ~ */
}
他にポピュラーなやり方として、borderで装飾したブロックを45度回転させる手法があります。
現場ではこちらが主流な印象ですが、45度回転した状態を正とする仕様上、width
の指定値と実際の矢印のサイズが異なる点がネック。
また、くるりと回転させる際もrotate
の値が180度で無くなるといったように、頭の中で描いているアウトプットの形とCSSの設定値が微妙に一致しない点が、若干扱いづらいです。
CSSで作る際はaspect-ratio
を活用すれば、レスポンシブ時の可変が楽に
ちなみにheight
指定ではなくaspect-ratio
を使って縦横比を指定しているのは、サイズ変更があった時にラクだから。width
の値を変えるだけで高さも同じ比率を自動で維持します。
codepenのデモではそのwidth
の値も変数化し、値を変えれば関連する計算箇所も自動で反映するようにしています。
生のCSSでも工夫してモジュール化・自動化を意識すると保守管理の負担がだいぶ減ります。レスポンシブ対応で、いちいち全値打ち直し、再計算していたら大変ですよ。
まあSassを使う現場なら今更感があるでしょうが。
矢印の位置はabsoluteでは無く、grid-areaで済ませても良い。ただし……。
総合的な判断で、デモは矢印の位置にposition: absoluteを使用していますが、summary
にflex
やgrid
を指定する形でも良いです。むしろその方がスマートではある。
details summary {
display: grid;
grid-template-columns: 1fr auto;
justify-content: space-between;
align-items: center;
gap: 1em;
}
ただしその場合、summary
直下の要素数が想定外の数に増えないようにしておく必要があります。
CMSで顧客に納品している場合、例えば強調用のstrong
や、部分的な色変更でspan
を不意に挟まれ、grid-template-columns
の制御が意図しない形で表示される可能性があります。
div
等の包括要素で囲むなど、予め対策をしてください。
<summary>
<div>ここに質問が入ります。</div>
</summary>
解説:details[open]で矢印アイコンをrotate
details[open] summary::after {
transform: rotate(180deg);
}
展開の状態取得は[open]で判定可能。[open]状態だと矢印アイコンをtransform: rotate(180deg);
で180度回転させる事で開閉の状態を表現します。
その他、詳細部分のコンテナは自由に装飾可。デモではdiv.inner
の1つしか使っていませんが、.content
.inner
など入れ子構造にする事も可。デザインに応じて必要な数を用意してください。
<details>
<summary>
概要テキスト。
</summary>
<div class="content">
<div class="inner">
詳細テキスト。
</div>
</div>
</details>
以上で基本的な形は完成です。
仕上げとして矢印にCSSアニメーションを付与
基本形は先程のデモで完成ですが、もう一押し。
よくアコーディオンUIで指摘されるのが、矢印にアニメーションを付けてくれという要望。一瞬で上下が反転すると変化に気が付かないという事です。
矢印をアニメーションで回転させることで、その点を解消すると同時に「もう一度押したら畳めそう」とユーザーにアクションを予想させる効果もあります。
そこでtransform
にtransition
を設定してアニメーションを付与します。
デモ(矢印にCSSアニメーションを付与)
See the Pen (矢印にCSSアニメを付与)detailsタグで作るアコーディオン by MEMORUKA (@memoruka) on CodePen.
解説:矢印アイコンにtransition
を設定
details summary .ico {
transition: transform .3s;
/* ~ ※アニメーション以外のプロパティは省略 ~ */
}
details[open] summary .ico {
transform: rotate(180deg);
}
矢印アイコンにtransition: transform .3s;
を設定し300ミリ秒かけて回転させます。
アニメーションさせる場合、iOS対策で疑似要素(after)からHTMLタグに変更
ところで先程まで矢印はsummary
の疑似要素(after)で作っていたのに、今回はわざわざ矢印用のspan
を用意しているのがわかりますか。
本当はこういった装飾のためだけに用意する空タグが大嫌い(最近は割り切ってわりと使ってるけどw)なので、after要素で済ませたかったのですが、ここでまたしても令和のIE6ことSafariの登場😵。
疑似要素のtransform: rotate();
にtransition
を指定すると動かないというバグがあり、やむを得ず矢印用にspan
タグを用意して装飾しています。
まとめ
以上、details
タグで作るアコーディオンUIの解説でした。
サクッと実装できて、アクセシビリティも優秀なdetails
。昨年末にChromeが完全対応し、これからは大手を振って現場に投入できそうです。
今後は積極的に活用していきたいと思います。