グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



【CSS】display:none に対してアニメーションを付けてみた


CSSを熟知している皆さんにとって、CSSでアニメーションを作成することは簡単なことかもしれません。
しかし、私のようなまだまだ経験の浅いコーダーにとっては、慣れない作業に感じることがあります。

そこで、最近私がコーディングしたアニメーションについて、備忘録として記録しておこうと思います。

実装概要

実装概要としては至ってシンプルでです。

コード(TypeScript × React)は下記のようになります。

import { useState, type FC, type PropsWithChildren } from "react";
import { BsArrowsAngleExpand } from "react-icons/bs";

const Content: FC<PropsWithChildren> = ({ children }) => {
  return <div className="content">{children}</div>;
};

export const Example: FC = () => {
  const [isEnabled, setIsEnabled] = useState(false);

  return (
    <div className="example">
      <Content>block</Content>
      <Content>
        <button
          className="button"
          onClick={() => setIsEnabled((prev) => !prev)}
        >
          <BsArrowsAngleExpand size="20px" />
        </button>
        {isEnabled && (
          <div className="block">
            <span>block</span>
          </div>
        )}
      </Content>
      <Content>block</Content>
    </div>
  );
};

実際の挙動は、ボタンを押すと下記のようにコンテンツが表示される挙動に、アニメーションを実装するといったものです。

さて、実際にアニメーションを追加していきます。

シンプルにコーディング

とりあえず、heightopacityだけのシンプルなアニメーションを付けるとして、

{isEnabled && (
  <div className="block">
    <span>block</span>
  </div>
)}

上記のコードのisEnabledで表示・非表示を分けている部分を、CSS で置き換えるために

<div className="block" data-isenabled={isEnabled}>
  <span>block</span>
</div>

データ属性として<div>isEnabledを渡すように変更し、

.block {
  --block-height: 200px;

  padding-top: 10px;
  height: var(--block-height);
  opacity: 1;
  transition: all 1s;
}

.block[data-isenabled="false"] {
  padding-top: 0px;
  height: 0px;
  opacity: 0;
}

とすると、

アニメーションが動いてますね。

完成!となればいいんですが… 実は、要素としては残ってしまっているんです。

アクセシビリティツリー的に言えば、表示されていないテキストが確認できてしまう状態です。

ということで、機能に寄った実装をしていきます。

display:none を追加

そういった場合は、display:noneを使っていきます。

.block {
  --block-height: 200px;

+ display: block;
  padding-top: 10px;
  height: var(--block-height);
  opacity: 1;
  transition: all 1s;
}

.block[data-isenabled="false"] {
+ display: none;
  padding-top: 0px;
  height: 0px;
  opacity: 0;
}

実装に加えてあげますと、

アクセシビリティツリー上では表示されなくなりましたが、 見ての通り、追加されていたアニメーションは表示されなくなってしまいました。

困ったときはドキュメント確認ということで、ドキュメントを確認してみると、

display を CSS トランジションでアニメーションさせる場合、 2 つの追加の機能が必要になります。

  • @starting-style は、アニメーションする要素が最初に表示されたときからトランジションさせたいプロパティの開始値を提供します。これは予期しない動作を避けるために必要です。既定では、 CSS トランジションは要素の最初のスタイル更新時や、 display の型が none から他の型へ変更された時には発生しません。
  • transition-behavior: allow-discrete は、 transition-property 宣言(または一括指定の transition)で display のトランジションを有効にするために設定する必要があります。

しっかりと書いてありました。ということで、実装していきましょう。

@starting-style + transition-behavior + transition-property

ドキュメント通りに実装すると下記のようになるでしょうか。

.block {
  --block-height: 200px;

  display: block;
  padding-top: 10px;
  height: var(--block-height);
  opacity: 1;
- transition: all 1s;

+ transition-property: display, opacity, height, padding-top;
+ transition-behavior: allow-discrete;
+ transition-duration: 1s;

+ @starting-style {
+   height: 0px;
+   opacity: 0;
+ }
}

.block[data-isenabled="false"] {
  display: none;
  padding-top: 0px;
  height: 0px;
  opacity: 0;
}

実装を確認してみると、

アニメーションも実装できて、且つアクセシビリティツリー上でも表示されなくなりましたね。

まとめ

元々、display:noneはアニメーションを付けづらいイメージを持っていて、アクセシビリティツリーを考慮せず、visibility:hiddenで対応していた部分もあったのですが、より「CSS 完全に理解した」コーディングができるようになり、少し感慨深いものがあります。

補足として@starting-styleは firefox では互換性が(2024年7月22日 現在)なかったのですが、 改めて互換性を確認してみると、2024年8月6日 リリースの 129 で対応されるということで気兼ねなく実装できますので、是非参考にしていただければと思います。

この記事を書いた人

2G
2G
システムエンジニアへの夢をあきらめきれず、建築業界からIT業界へ転職。
アーティス入社後はフロントエンドエンジニアとして、webアプリケーションサービスの開発に従事している。趣味は、ラーメン屋巡り。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします