👐

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の活用

解決するには、以下のようにtransitioncontent-visibilityallow-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が切り替わらず、コンテンツが描画ツリーから即座に除外されるのを防ぐことができる。その結果、閉じる際にもスムーズなアニメーションが実現可能になる。

https://developer.mozilla.org/ja/docs/Web/CSS/transition-behavior

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);
}

デモ

株式会社ZOZO

Discussion