1. プロンプトとコンテキスト
ダイアログがdisplay: noneで非表示になっています。プロダクト側はフェードイン・フェードアウトさせ、アニメーション完了後にのみレイアウトから除外することを求めています。開発チームはopacityのみを遷移させているため、表示時のアニメーションが見えず、非表示時は瞬時に消えてしまいます。離散プロパティについて説明し、CSSを設計した上で、ブラウザのフォールバック、フォーカス管理、prefers-reduced-motionについて網羅してください。
2. 面接官がテストしていること
displayやcontent-visibilityなどの離散プロパティには連続した中間値が存在しないことを理解しているか。transition-behavior: allow-discreteまたはそのショートハンドを、duration(時間)として扱うのではなく正しく使用できるか。- 切り替えのタイミングが表示時と非表示時で異なること、および
@starting-styleが初期スタイルを確立することを理解しているか。 - 互換性、アクセシビリティ、フォーカス、視覚効果の抑制(reduced motion)、および実際のレンダリングテストを考慮しているか。
3. 回答前の明確化のための質問
- これは通常の要素ですか、ネイティブのdialogですか、それともtop-layerのpopoverですか?
- レイアウトから除外してフォーカス不可にする必要がありますか、それとも描画(paint)をスキップするだけで十分ですか?
- どのブラウザバージョンがサポート対象であり、離散遷移が使えない場合のフォールバックはどうしますか?
- アニメーション中のフォーカス、Escapeキー、バックドロップクリック、およびreduced-motionの設定はどのように動作すべきですか?
4. 30秒の回答フレームワーク
displayは離散プロパティであるため、デフォルトでは補間されません。transition-behavior: allow-discreteを使用して遷移を有効にし、opacityまたはtransformに0以外のdurationを指定します。表示時はアニメーション全体のために十分早い段階で可視値に切り替え、非表示時は終了時まで要素を表示状態に保ちます。初回の挿入フレームには@starting-styleを使用します。古いブラウザは連続プロパティのフォールバックを維持しながら拡張機能を無視できます。フォーカス、非表示セマンティクス、reduced-motionは引き続き明示的な処理が必要です。
5. ステップバイステップの詳細な回答
ステップ 1: 連続プロパティと離散プロパティを分離する
opacityやtransformは連続した中間値を生成できますが、displayは値の切り替えのみを行います。allow-discreteがない場合、ブラウザは即座にdisplayを変更するため、閉じる要素はopacityの遷移が見える前に削除され、開く要素には可視の開始フレームがありません。transition-behaviorは離散遷移を許可するだけであり、transition-propertyと0以外のtransition-durationが依然として必要です。
ステップ 2: 双方向のパネル遷移を構築する
まず連続的なフォールバックを宣言し、次にallow-discreteを使用した拡張宣言を行います。非表示時、コンテンツが表示され続けるようにdisplayは100%付近で切り替わる必要があります。表示時は早い段階で可視値に切り替わる必要があります。transition-behaviorのみを宣言して、対象のopacityやtransformの状態を忘れないようにしてください。
.panel {
opacity: 0;
transform: translateY(-0.5rem);
display: none;
transition: opacity 180ms ease, transform 180ms ease;
transition: opacity 180ms ease, transform 180ms ease, display 180ms allow-discrete;
}
.panel[data-open="true"] {
opacity: 1;
transform: translateY(0);
display: block;
}ステップ 3: 初回表示とpopoverの処理
要素が動的に挿入されたり、popoverが初めて開かれたりする場合、比較対象となる直前の可視スタイルが存在しないことがあります。@starting-styleは初期のopacityとtransformを提供します。ネイティブのpopoverはtop layerにも入るため、displayとoverlayを離散遷移に含める必要があり、そうしないと非表示時に要素が即座に削除されてしまいます。対象ブラウザのバージョンでこの組み合わせをテストしてください。
ステップ 4: フォールバックと機能検出の設計
古いブラウザは未知のallow-discrete値を無視しますが、ベースとなるopacity/transformの遷移は維持すべきです。必要に応じて、ベーススタイル全体を隠すのではなく、@supports (transition-behavior: allow-discrete)の配下に拡張ルールを配置します。フォールバックでも、開閉、フォーカス、および正しい非表示セマンティクスを公開する必要があります。
ステップ 5: アクセシビリティ、パフォーマンス、検証
アニメーションはアクセシビリティの状態そのものではありません。閉じたときは、hidden、aria-hidden、またはネイティブdialogの状態を同期し、トリガーにフォーカスを戻します。開いたときは、フォーカスを内部に移動し、Escapeキーをサポートします。prefers-reduced-motion: reduceのもとではdurationを短縮または削除します。レイアウト、描画、キーボードフォーカス、スクリーンリーダー、素早いトグル切り替え、中断された遷移、および低スペック端末でのレイアウト/コンポジットのコストをテストします。
6. 質の高い回答の例
displayは離散プロパティであり、opacityのような連続した中間値はありません。私ならベースのopacity/transform遷移を維持した上で、transition-behavior: allow-discreteを使ってdisplayを拡張パスに組み込みます。表示時は早い段階で可視に切り替わり、非表示時は終了まで可視状態を維持します。@starting-styleは最初のフレームを提供し、popoverではtop-layerのdisplayおよびoverlayの処理も必要です。古いブラウザは拡張機能を無視しますが、ベースのアニメーションは維持されます。また、フォーカス、Escapeキー、非表示セマンティクス、reduced motionを同期し、ブラウザ間やキーボード操作フロー全体で動作を検証します。
7. よくある間違い
- opacityのみを遷移させる → displayが即座に変更される → 連続プロパティを維持しながらallow-discreteを追加する。
transition-behaviorをdurationとして扱う → 離散プロパティが依然として遷移しない → プロパティと0以外のdurationも宣言する。- 非表示の開始時にdisplayをnoneに切り替える → フェードが見えなくなる → 離散値の切り替えを非表示durationの終了時に合わせる。
- 初回挿入を無視する → 初回オープン時に表示アニメーションが発生しない →
@starting-styleで初期フレームを定義する。 - マウスとモダンブラウザのみでテストする → キーボード、スクリーンリーダー、またはフォールバック状態が機能しない → supportsのフォールバック、フォーカス、reduced-motionのチェックを含める。
8. フォローアップ質問と回答
なぜ非表示処理の終了時にdisplayを切り替える必要があるのですか?
即座にnoneに切り替えると、要素はレイアウトとレンダリングから外れるため、opacityのdurationが残っていても表示されるコンテンツがなくなります。100%付近まで表示を維持し、その後に非表示状態に移行します。
allow-discreteは毎フレームdisplayを補間しますか?
いいえ。離散値が遷移に参加することを許可するだけであり、値自体は決められた時点で切り替わります。連続的な視覚的変化は、opacityやtransform、またはその他の補間可能なプロパティからもたらされます。
古いブラウザのユーザビリティをどのように維持しますか?
まずallow-discreteを含まないベース遷移を宣言し、その後@supportsまたは後続の宣言で拡張します。拡張が利用できない場合は、ボタンの動作、フォーカス、非表示セマンティクスを維持しながら、displayの即時切り替えを許容します。
reduced motion(視覚効果の抑制)にはどのように対応しますか?
prefers-reduced-motion: reduceのもとでは、状態同期、フォーカス移動、閉じるコントロールを維持したまま、durationを大幅に短縮するかゼロに設定します。単にアニメーションを非表示にして、見えないフォーカス可能な要素を残すようなことは避けてください。