高さまでアニメーション化: auto;(および他の固有のサイズ設定キーワード)を CSS 内で指定

interpolate-size プロパティまたは calc-size() 関数を使用して、長さから組み込みサイズ設定キーワードへのスムーズな遷移とアニメーションを有効にします。

公開日: 2024 年 9 月 17 日

はじめに

よくリクエストされる CSS 機能として、height: auto にアニメーション化する機能があります。リクエストの若干のバリエーションとして、height ではなく width プロパティを移行するか、min-contentmax-contentfit-content などのキーワードで表される他の固有のサイズのいずれかに移行することがあります。

たとえば、次のデモでは、アイコンにカーソルを合わせたときにラベルが自然な幅にスムーズにアニメーション化されるといいでしょう。

使用される CSS は次のとおりです。

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease; /* 👈 Transition the width */

    &:hover,
    &:focus-visible {
        width: max-content; /* 👈 Doesn't work with transitions */
    }
}

width プロパティを移行するように transition を宣言し、:hoverwidth: auto を宣言しても、スムーズな移行は行われません。代わりに、急激な変化が起こります。

interpolate-size を使用して、組み込みサイズ設定キーワードとの間でアニメーション化する

対応ブラウザ

  • Chrome: 129。
  • Edge: サポートされていません。
  • Firefox: サポートされていません。
  • Safari: サポートされていません。

ソース

CSS の interpolate-size プロパティを使用すると、CSS 組み込みサイズ設定キーワードのアニメーションと遷移を許可するかどうかを制御できます。

デフォルト値は numeric-only で、補間は有効になりません。このプロパティを allow-keywords に設定すると、ブラウザがそれらのキーワードをアニメーション化できる場合に、長さから CSS 固有のサイズ設定キーワードへの補間が有効になります。

仕様どおり:

  • numeric-only: <intrinsic-size-keyword> は補間できません。
  • allow-keywords: 2 つの値を補間できます。一方が <intrinsic-size-keyword> で、もう一方が <length-percentage> の場合です。[…]

interpolate-size プロパティは継承されるため、:root で宣言して、ドキュメント全体で組み込みサイズ設定キーワードとの間で遷移を有効にできます。これはおすすめの方法です。

/* Opt-in the whole page to interpolate sizes to/from keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

次のデモでは、このルールがコードに追加されています。その結果、width: auto との間のアニメーションは正常に動作します(サポートされているブラウザの場合)。

セレクタを絞り込んでオプトインのリーチを制限する

allow-keywords オプトインをドキュメントのサブツリーに限定する場合は、セレクタを :root からターゲットとする要素のみに調整します。たとえば、ページの <header> がこのようなタイプの遷移に対応していない場合は、次のように <main> 要素とその子孫のみにオプトインを制限できます。

main { /* 👈 Scope the opt-in to only <main> and its descendants */
    interpolate-size: allow-keywords;
}

キーワードのサイズ設定との間でアニメーションをデフォルトで許可しないのはなぜですか?

このオプトイン メカニズムに関してよく寄せられるフィードバックとして、ブラウザでは本来備わっているサイズ設定キーワードから長さへの遷移やアニメーションをデフォルトで許可するべきである、というものがあります。

この動作を有効にするオプションは、この機能の開発中に検討されました。ワーキング グループは、多くのスタイルシートが、組み込みサイズ設定キーワード(automin-content など)をアニメーション化できないことを前提としているため、これをデフォルトで有効にすると下位互換性が失われることを発見しました。詳しくは、関連する CSS ワーキング グループの問題に関するこちらのコメントをご覧ください。

そのため、このプロパティはオプトインです。継承特性により、ドキュメント全体を有効にするには、前述のように :rootinterpolate-size: allow-sizes を宣言するだけです。

calc-size() を使用して、組み込みサイズ設定キーワードとの間でアニメーション化する

対応ブラウザ

  • Chrome: 129。
  • Edge: 129。
  • Firefox: サポートされていません。
  • Safari: サポートされていません。

ソース

固有のサイズ設定キーワードとの間で補間を有効にするもう一つの方法は、calc-size() 関数を使用することです。これにより、安全で明確に定義された方法で、組み込みサイズに対して数学演算を実行できます。

この関数は、次の 2 つの引数を順番に受け取ります。

  • calc-size ベース<intrinsic-size-keyword> でもネストされた calc-size() でもかまいません。
  • calc-size 計算: calc-size ベースを使用して計算を行うことができます。計算サイズベースを参照するには、size キーワードを使用します。

次に例を示します。

width: calc-size(auto, size);        // = the auto width, unaltered
width: calc-size(min-content, size); // = the min-content width, unaltered

元のデモに calc-size() を追加すると、コードは次のようになります。

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease;

    &:hover,
    &:focus-visible {
        width: calc-size(max-content, size); /* 👈 */
    }
}

視覚的には、interpolate-size を使用した場合とまったく同じ結果が得られます。したがって、この場合は interpolate-size を使用する必要があります。

calc-size() の強みは、計算を実行できることです。これは interpolate-size ではできないことです。

width: calc-size(auto, size - 10px); // = The auto width minus 10 pixels
width: calc-size(min-content, size + 1rem); // = The min-content width plus 1rem
width: calc-size(max-content, size * .5);   // = Half the max-content width

たとえば、ページ上のすべての段落のサイズを 50px の最も近い倍数にするには、次のようにします。

p {
    width: calc-size(fit-content, round(up, size, 50px));
    height: calc-size(auto, round(up, size, 50px));
}

calc-size() では、2 つの calc-size() の計算サイズのベースが同じである場合に、2 つの calc-size() の間で補間することもできます。これも interpolate-size では実現できません。

#element {
    width: min-content; /* 👈 */
    transition: width 0.35s ease;

    &:hover {
        width: calc-size(min-content, size + 10px); /* 👈 */
    }
}

calc()<intrinsic-size-keyword> を許可しない理由

calc-size() についてよく聞かれる質問は、CSS ワーキング グループが、組み込みサイズ設定キーワードをサポートするように calc() 関数を調整しなかった理由です。

その理由の一つは、計算時に組み込みサイズ設定キーワードを組み合わせて使用できないことです。たとえば、calc(max-content - min-content) と記述すると有効なように見えますが、実際には有効ではありません。calc-size() は、calc() とは異なり、最初の引数として 1 つの <intrinsic-size-keyword> のみを受け取るため、正確性を強制します。

コンテキスト認識もその理由の一つです。一部のレイアウト アルゴリズムには、特定の組み込みサイズ設定キーワードに対する特別な動作があります。calc-size() は、<length> ではなく、固有のサイズを表すように明示的に定義されています。これにより、これらのアルゴリズムは calc-size(<intrinsic-size-keyword>, …)<intrinsic-size-keyword> として扱い、そのキーワードに対する特別な動作を維持できます。

どの方法を使用すればよいですか。

ほとんどの場合、:rootinterpolate-size: allow-keywords を宣言します。基本的に 1 行で記述できるため、組み込みサイズ設定キーワードとの間でアニメーションを有効にする最も簡単な方法です。

/* Opt-in the whole page to animating to/from intrinsic sizing keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

このコードは、サポートしていないブラウザでは遷移なしにフォールバックするため、プログレッシブ エンハンスメントに適しています。

計算など、よりきめ細かい制御が必要な場合や、calc-size() でしか実行できない動作を使用する場合は、calc-size() を使用できます。

#specific-element {
    width: 50px;

    &:hover {
        width: calc-size(fit-content, size + 1em); /* 👈 Only calc-size() can do this */
    }
}

ただし、コード内で calc-size() を使用する場合、calc-size() をサポートしていないブラウザ用のフォールバックを組み込む必要があります。たとえば、追加のサイズ宣言を追加したり、@supports を使用して機能検出にフォールバックしたりします。

width: fit-content;
width: calc-size(fit-content, size + 1em);
       /* 👆 Browsers with no calc-size() support will ignore this second declaration,
             and therefore fall back to the one on the line before it. */

その他のデモ

interpolate-size: allow-keywords を活用したデモをいくつかご紹介します。

通知

次のデモは、こちらの @starting-style デモのフォークです。高さが異なるアイテムを追加できるようにコードを調整しました。

これを実現するには、ページ全体でサイズ キーワードの補間が有効になり、各 .item 要素の heightauto に設定されます。それ以外の場合、コードはフォーク前とまったく同じです。

:root {
    interpolate-size: allow-keywords; /* 👈 */
}

.item {
    height: auto; /* 👈 */

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

<details> 要素をアニメーション化する

このタイプの補間を使用する一般的なユースケースは、開くときに開示ウィジェットや排他的アコーディオンをアニメーション化することです。HTML では、<details> 要素を使用します。

interpolate-size: allow-keywords を使用すると、かなりのことができます。

@supports (interpolate-size: allow-keywords) {
    :root {
        interpolate-size: allow-keywords;
    }
    
    details {
        transition: height 0.5s ease;
        height: 2.5rem;
        
        &[open] {
            height: auto;
            overflow: clip; /* Clip off contents while animating */
        }
    }
}

ご覧のとおり、アニメーションは開示ウィジェットが開いているときにのみ実行されます。この問題に対処するため、Chrome では ::details-content 疑似要素の開発を進めており、今年後半に Chrome に搭載される予定です(今後の投稿で詳しく説明します)。interpolate-size: allow-keywords::details-content を組み合わせると、両方向のアニメーションを作成できます。