[CSS] `grid-template-rows`に`transition`が効くのでJS使わずにアコーディオン作ってみた件
背景
JavaScript を利用せずに HTML と CSS だけでアコーディオン UI を実装する場合、折りたたみ部分の開閉アニメーションをどうするのかが肝になる。
最近では JavaScript を利用しないでアコーディオン UI を実現するために <details>
と <summary>
を利用するケースもある。ただ、この場合は折りたたみ部分の開閉アニメーションは利用できない。
よくある実装方法
HTML と CSS だけで実装する場合、以下のように開閉時にmax-height
を変えてtransition
させることでアニメーションを実現する方法がある。
.collapse_body {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out;
}
.collapse_control:checked ~ .collapse_body {
max-height: 100vh;
}
実装方法
max-height
ではなく、grid
のgrid-template-rows
をアニメーションさせる。
説明用で簡素にしているが、実装は以下の通り。
<div class="Accordion">
<label class="Accordion-header">
<input type="checkbox" hidden />見出し
</label>
<div class="Accordion-content">
<div class="Accordion-content__inner">中身</div>
</div>
</div>
/* 必要なものだけ抜粋 */
.Accordion {
&:has(.Accordion-header input:checked) {
.Accordion-content {
grid-template-rows: 1fr;
}
}
}
.Accordion-content {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease-in-out;
}
.Accordion-content__inner {
overflow: hidden;
}
大まかに要素の高さを変えてアニメーションさせる手順は下記の通り。
.Accordion-content
:- 折りたたみ対象の要素に
display: grid
とgrid-template-rows
を0fr
で指定するtransition
はgrid-template-rows
に対して指定する
- 開閉状態時、
grid-template-rows
を1fr
にする
- 折りたたみ対象の要素に
.Accordion-content__inner
:- 折りたたみ対象の要素の中身がはみ出してしまうため、
overflow: hidden;
を指定しておく
- 折りたたみ対象の要素の中身がはみ出してしまうため、
:has()
が使えない場合
JavaScript を利用しないため、開閉状態のフラグをinput[type=checkbox]
にもたせている。それを CSS で判定する必要があるが、それは:has(.Accordion-header input:checked)
というセレクタで実現している。:has()
を利用できない環境の場合は、隣接セレクタや子孫セレクタなどを利用する必要がある。
<div class="Accordion">
<label class="Accordion-header" for="AccordionCheck"> 見出し </label>
<input type="checkbox" id="AccordionCheck" class="Accordion-check" hidden />
<div class="Accordion-content">
<div class="Accordion-content__inner">中身</div>
</div>
</div>
.Accordion-check:checked ~ .Accordion-content {
grid-template-rows: 1fr;
}