静的に書き出したマーケサイトでも、アコーディオンや開閉パネル、フィルタードロワーは「ゼロから自然な高さへ」を滑らかに見せたい。長年の答えは「JavaScript で scrollHeight を測ってピクセル指定」でした。height: auto は計算値の段階で補間不能なキーワード扱いだったからです。2026 年、interpolate-size: allow-keywords と calc-size() の組み合わせは、内在サイズとトランジションの数式のあいだを宣言的につなぎます。ただし前提として、カスケード、flex/grid の既定の最小サイズ、WebKit のリフロー統合を理解する必要があります。本文では痛み、プリミティブ、@supports による段階的強化、パフォーマンス規律、アクセシビリティ、判断用のマトリクスを扱います。文書間の View Transition とページ内の高さトゥイーンを比較したい場合は 静的MPA向け View Transitions の記事 を、開閉領域に伸縮するフォームがある場合は field-sizing とフォーム を併読し、高さアニメと textarea の内在幅が衝突しないようにしてください。
検証の経済性も現実的です。顧客と同じGPU上でサブピクセルを再現するには、壊れたアニメを本番に出すより Mac mini を借りる方が安い。MacHTML のクラウド Apple Silicon はおよそ $16.9/日 で、以下ではその数字をリハーサル予算の目安にします。
height:auto アニメがつらい理由
通常の transition は長さ・パーセント・一部の transform を補間しますが、height: auto は特別です。used value は最初のレイアウト後の内容計測に依存します。transition: height 240ms ease で 0 と auto を切り替えると、多くのエンジンは数値のペアが無いため区間末でパキッと変わります。max-height: 9999px のハックは一見滑らかですが、翻訳で本文が伸びると体感距離とイージングの時間が合わなくなったり、レイアウトツリーが巨大な箱を想定してGPU負荷が膨らんだりします。scrollHeight を読むJSは有効ですがメインスレッドへの結合を戻し、静的ホストのCSPを複雑にし、ウェブフォントがアニメ途中で到着するとちらつきの原因にもなります。
新しいモデルは計測をスタイル系に閉じ込めます。内在のまま考えつつ、エンジンが補間可能とみなせる数値の廊下を与えます。Reactの状態機械を禁じた静的書き出しや main.js のバイトに敏感な案件では構造的なメリットです。Safari優先チームは、flexアイテム内でパーセント高さと内在キーワードが混ざるとWebKitが循環解決に厳しかったことも覚えておくと、「ブラグ」と誤報しないで済みます。
角丸の overflow: hidden との共存も地味に痛いです。max-height頼みだと影やアウトラインの描画が内容の露出とズレ、Retinaで「二枚扉」のように見えることがあります。内在補間は measured content box に used height を寄せられますが、無関係なプロパティを同時に動かさないことが条件です。
interpolate-size: allow-keywords の実務
interpolate-size はサブツリーに「キーワードも補間してよい」と宣言します。開閉のルートに interpolate-size: allow-keywords を置くと、内在キーワードと長さのあいだの遷移を測定可能な端点として扱えます。通常は .disclosure や details の外枠に限定し、グローバルな body には広げません。補間面が広がると無関係なルールまで高くつきます。
.disclosure {
interpolate-size: allow-keywords;
overflow: clip;
transition: height 260ms cubic-bezier(.2,.8,.2,1);
}
明示的な height 状態とセットにします。折りたたみは 0、展開は依然 auto でも、エンジンは数値軌道を計算できます。クラスで height:0 と height:auto を切り替えるときはマージンの相殺に注意し、謎の隙間が出たらアニメ要素の margin ではなく内側の padding で調整します。interpolate-size は継承するためネストしたアコーディオンには便利ですが、子カードが誤って継承し flex-basis も動かすと Safari で多パスレイアウトが起き得ます。ユーティリティクラスでスコープを絞り、動かない静的シェルには残さないのが無難です。
calc-size(fit-content) を測定の橋にする
calc-size() は内在キーワードを長さに落とし、transition がサンプリングできるようにします。展開側を height: calc-size(fit-content, size) にすると、height:auto と同様に内容ボックスを測った値が補間に入り、折りたたみ側が 0 なら両端が長さになります。
.panel[data-open="true"] .panel-body {
height: calc-size(fit-content, size);
}
.panel[data-open="false"] .panel-body {
height: 0;
}
max-height から移行するときは体感速度を比較してください。calc-size() は実際の深さに追従するため短いFAQと長いポリシー本文でイージングが破綻しにくい反面、ライブリージョンが遷移中に文字を足すと終点が動きます。静的HTMLでは transitionend までDOM更新を止めるのが無難です。
box-sizing: border-box を全体に敷いている場合、測った高さが意図した border box を含むか確認してください。そうでないと Safari が内容高さだけを動かし枠線が一フレームで飛び出すことがあります。
@supports での段階的強化
calc-size() が無いときは瞬時切り替えか max-height クリップにフォールバックします。機能クエリは関数トークン自体をテストし、プロパティ名だけを見ないでください。パースはできるが補間できない、という中途半端な実装を避けられます。
@supports (height: calc-size(fit-content, size)) {
.panel-body { transition: height 240ms ease; }
}
@supports not (height: calc-size(fit-content, size)) {
.panel-body { transition: max-height 320ms ease; max-height: 0; }
.panel[data-open="true"] .panel-body { max-height: 80vh; }
}
ビルド時に @supports の結果をHTMLコメントに出すのはサポート向けには便利ですが、モダンCSSだけで本文をゲートしないでください。CSS無しでも見出しとアンカーは残すべきです。動きは details/summary や aria-expanded 付きボタンの上に載せるオプション扱いにします。
Grid・flex・min-height:auto
flex子は既定で min-height:auto により内容の最小寄与より縮みません。0 から内在高さへアニメーションするなら min-height:0 が必要な場面があります。grid でも min-size:auto とトラック寸法の組み合わせで行が縮まないことがあります。
align-self:stretch のグリッド領域ではブロックサイズが確定しており、calc-size() の解決が変わります。aspect-ratio と喧嘩することもあるため、本文は単列グリッドにし、動くパネルは独立した整形コンテキストに隔離し、contain:layout はフォーカスリングが切れないことを確認してから使います。
gap と高さを同時に動かすと Safari は依存レイアウトを複数回走らせることがあります。高さトゥイーン中は兄弟の margin は固定し、必要なら opacity で柔らかく見せます。
Safari/WebKit の注意点
メインスレッドが忙しいと WebKit はスタイル更新をまとめ、高さの transition が飛ぶことがあります。静的HTMLでも解析タグは遅延させてください。macOSのアップグレード期は安定版SafariとSTPの両方でピクセル差分を取ると安心です。
height は合成レイヤに逃げません。transform と二重にかけるとレイアウトとペイントが倍加するので、ジェスチャごとにどちらか一方に絞ります。
展開時のサブピクセル文字は -webkit-font-smoothing の差でシマーに見えることがあるので、折りたたみと展開で同じ設定に揃えます。
パフォーマンス:レイアウトスラッシング
高さのサンプルごとにレイアウトが走ります。五枚同時オープンは負荷が乗ります。同時実行数を抑え、ビューポート外は content-visibility:auto(支援技術への影響を確認のうえ)などで間引きます。WebKitのタイムラインで紫のレイアウトバーが1フレームを超えていないか見ます。
JSフォールバックで ResizeObserver を使う場合、CSS transition と同時に同期的に読むとフィードバックループになります。requestAnimationFrame で間引き、document.hidden では書き込みを止めます。
アクセシビリティと prefers-reduced-motion
大きな領域の動きは前庭に負担をかけます。prefers-reduced-motion: reduce では時間をほぼゼロにするか opacity のみのヒントに切り替えます。DOM順序やフォーカス順は変えません。
@media (prefers-reduced-motion: reduce) {
.panel-body { transition: none !important; }
}
フォーカスリングがクリップされる場合は overflow:clip と内側の余白、または内側ラッパーへのアウトライン移動を検討します。aria-expanded は属性変更と同時に支援技術へ伝わるようにします。
静的HTMLのアコーディオンパターン
コンテナにクラスを一つ置き、最小限のJSで data-open を切り替えるか、ネイティブ details をCSSでミラーします。JS無しでも details は開閉できますが、アニメは長らくハック頼みでした。interpolate-size と calc-size() で意味と動きが揃います。二重に高さを所有させないでください。
CSSはタイポ→レイアウト→モーションの順で重ね、リリース直前のマーケ用 !important でトランジションが死ぬのを防ぎます。S3/CloudFront のような静的ホストでは motion.css?v= でキャッシュ破棄を管理します。
マトリクス:内在高さを動かすか
| シナリオ | 内在高さを動かす? | メモ |
|---|---|---|
| 長文の法務アコーディオン | はい | calc-size は実深さに追従。 |
| 無限フィード | めったに無し | 仮想化を優先。 |
| モーダル | 場合による | 出入りは transform を優先。 |
| スティッキーナビの露出 | 注意 | sticky 合成と干渉するので実測。 |
リリース前モーションQAチェックリスト
- 320/390/834px で最長ロケール文字列を使い折りたたみ高さを確認する。
- macOSのアクセシビリティで prefers-reduced-motion を切り替え状態が伝わるか見る。
- パネルを五枚連打しベースラインM2でCPUとファンを見る。
- 本番同等のサードパーティ込みでWebKitタイムラインを録る。
- 遷移中点でクリップされたコンテナ内のフォーカス順と可視性を確認する。
- macOSリリース窓なら安定版SafariとSTPを比較する。
- webfont preload を変えたら内在計測を再テストする。
- ダークモードと高コントラストでピクセル差分を取る。
よくある質問
max-height はまだ使える?
フォールバックとしては可。calc-size が使えるなら長文でのタイミングずれを避けられます。
スクロールドリブンアニメの代替?
いいえ。別問題です。併用するなら相互作用を実測してください。
width:auto も同様?
概念は近いですが行ボックスの再レイアウトが増えるので計測が必須です。
信頼できるモーションは構文暗記より、本番に近いフォント・拡張・スケーリングで実機Safariを回すことです。MacHTML の Mac mini(およそ $16.9/日)なら常時接続のターゲットになり、SSHでピクセルスナップ、VNCでデザインレビューができます。
クラウド Mac mini で内在高さアニメをリハーサル
Apple Silicon Safari で静的バンドルを開き、アコーディオン嵐のレイアウトをプロファイルしてから本番CSSへモーショントークンをマージしましょう。