details要素を閉じる際にもCSSアニメーションを有効にする方法
概要
HTMLのdetails
要素はネイティブに開閉可能な要素だが、開閉時にアニメーションをつける方法としては、JavaScriptを利用するケースがまだまだ一般的である。しかし、昨今ではCSSだけでも実装が可能になってきている。
例えば、以下のようなCSSで開閉のアニメーションが可能である。
:root {
interpolate-size: allow-keywords;
}
details::details-content {
height: 0;
overflow: clip;
opacity: 0;
transition: height 0.4s ease;
}
details[open]::details-content {
height: auto;
opacity: 1;
}
しかし、この方法では「閉じる」際のアニメーションがうまく効かない。
opacity
を付与することで多少アニメーション時の違和感が減るが、デザインの要件によっては付与が難しいケースもあり、一律でアニメーションが自然に付くことが好ましい。
なぜ閉じる際にアニメーションが効かないのか?
閉じる際にアニメーションが動作しない理由は、ブラウザが内部的にdetails
要素を閉じる際にcontent-visibility
を「hidden」相当に変更するためである。
content-visibility
はブラウザが要素を描画対象から除外することでレンダリングコストを下げるためのプロパティである。閉じた瞬間に即座にコンテンツが描画ツリーから完全に外されてしまうため、高さのアニメーションが動作しない。
allow-discrete
の活用
解決方法:解決するには、以下のようにtransition
にcontent-visibility
とallow-discrete
キーワードを追加する必要がある。
:root {
interpolate-size: allow-keywords;
}
details::details-content {
height: 0;
overflow: clip;
opacity: 0;
transition: height 0.4s ease, opacity 0.4s ease,
content-visibility 0.4s ease allow-discrete;
}
details[open]::details-content {
height: auto; /* for unsupported browser */
height: calc-size(auto, size);
opacity: 1;
}
allow-discrete
を指定すると、離散的(即座に切り替わる)なプロパティの変更をトランジションの終了まで遅延させることが可能になる。そのため、高さのアニメーションが完了するまではcontent-visibility
が切り替わらず、コンテンツが描画ツリーから即座に除外されるのを防ぐことができる。その結果、閉じる際にもスムーズなアニメーションが実現可能になる。
calc-sizeについて
calc-size
は、CSSの比較的新しい関数であり、サイズ指定のトランジション時に使用される。従来のauto
値はトランジションの対象とできなかったが、calc-size(auto, size)
と記述することで、要素の本来のサイズ(auto
)と明示的なサイズ値の間をアニメーションさせることが可能になる。これにより、今回はdetails
要素の開閉時に高さがスムーズにアニメーションする。
例えば、以下のように指定することで、閉じた状態(height: 0
)から開いた状態(height: auto
)への変化をトランジションできる。ただし、calc-size
は執筆当時だと一部のブラウザのみで対応しており、すべての環境で動作するわけではない点に注意が必要である。
details[open]::details-content {
height: calc-size(auto, size);
}
Discussion