モーション階層とホバーレシピ
14以上のプロトタイプアプリで実証された2つのアイデア:
- 継続時間はコンポーネントごとの数値ではなく、規模である。 目は共通のテンポを一つの製品として読み取る。小さいものほど速く動く。
- ホバーは幾何学を、穏やかに動かす。 行の中に成長するハイライト、到着する矢印、行き先へ傾くシェブロン、次の状態へ転がるラベル。色だけのホバーはスイッチを切り替えたようにしか読まれない。
ファイル
references/recipes.css: Tailwind v4@themeトークンと@utilityレシピ。references/timing.ts: MotionとWAAPI向けの同じ階層。references/swap.tsx:IconSwapとLabelSwap(React)。
階層
| 階層 | ms | 用途 |
|---|---|---|
tap | 100 | アイコンの切り替え、プレス |
pop | 150 | メニュー、ポップオーバー、ツールチップの開く |
hover | 200 | ポインターに応答する行やコントロール |
card | 300 | カードの浮き上がり、リストの整列、ページの切り替え |
layout | 500 | パネルの折りたたみ、ページのリフロー |
- 出現はイーズアウト(
cubic-bezier(0.22,1,0.36,1))。退出はイーズインで、1階層短く実行する。 - 2つのものが場所を入れ替える場合、退出するものは即座に去り、入ってくるものは
delay-handoff(50ms)後に開始する。 - 値のトゥイーン(カウンター、バーの充填、チャート線の描画)はより長く実行してもよい: 目はデータを追っている。
- 意図的なスプリングは維持すること。
移行: すべてのアドホックな継続時間を最も近い階層にマップする。
| アドホック値 | 階層 |
|---|---|
| 0.12 | tap |
| 0.15–0.18 | pop |
| 0.2–0.24 | hover |
| 0.28–0.35 | card |
| 0.4–0.5 | layout |
画面のタイミングはほとんどの場合20ms未満しか変化しないと予想される。
レシピ(要素ごとに一つ)
hover-grow(ナビゲーション行、リンク行):- ハイライトは、最初はインセット(3px×6px)で透明、その後行全体に膨らみながらフェードインする
::before。 :focus-visible、data-state=open、aria-current=pageでも表示される。- スクエアまたはブルータリストのデザイン(Carbon風)では、代わりにフラットなハイライトを維持すること。
- ハイライトは、最初はインセット(3px×6px)で透明、その後行全体に膨らみながらフェードインする
trail(リンク行、「プランを見る」):- 親の
.groupがホバーまたはフォーカスされるまでは半透明かつ半分のスケールの後続グリフが、そのリーディングエッジから成長する。 trail-out(左下から成長) とarrow-up-rightを組み合わせて、アプリを離れるリンクを示す。
- 親の
nudge/nudge-back(シェブロン、「すべて表示」、戻るリンク): 目的地に向かって2px傾く。lift(クリック可能なカード):card階層にわたって、表面は一段明るく、影は一段深く。- 決してtranslateを使わない: カーソルの下でカードが跳ねると、照準対象が動いてしまう。
scroll-fade(スクロールするペイン):- マスクベースの端フェード。スクロール可能な側にのみ適用。コンテナ自身のスクロールタイムラインで駆動され、背景色は不要。
- 非対応ブラウザでは単にフェードが表示されない。
IconSwap(メニューを閉じる、コピーからチェック、太陽から月):- 出ていくグリフは縮小し、−22°傾き、イーズインでフェードアウトする。
- 入ってくるグリフはハンドオフ遅延の後に続く。
- 両方ともマウントされたままなので、途中で逆転してもきれいに戻る。
LabelSwap(「リンクをコピー」から「コピー済み」、「折りたたむ」から「展開する」):- 両方のラベルが一つのグリッドセルに配置されるため、幅のジャンプが発生しない。
- 隠れたラベルは
transition-discreteによりinvisibleになり、支援技術は現在のラベルのみを読み上げる。
トラップ
- 自身をアニメーションさせる要素での
scroll-fade(Radixメニュー、ポップオーバー、セレクトコンテンツ) は、その要素の退出アニメーションを置き換える。Radixは終了しない終了を待ち、メニューが開いたままになる。フェードは内部のスクロールする子要素に配置すること。 scroll-fadeは境界線と影もマスクする。 境界線のあるカードに配置せず、内部のスクローラーに配置すること。- Tailwind v4のカスタム
duration-*ユーティリティは、--tw-durationも設定する必要がある。そうしないと、同じ要素のtransition-[…]ユーティリティがソート順に応じて優先される。 - ライン高を伴う名前付きタイプステップ (
--text-meta--line-height) は、親のリーディングを継承していた任意のサイズを置き換える際、変換された各行を1ピクセルずらす。中間ステップには独自のライン高を持たせないこと。 - ホバーは
@media (hover: hover)の背後にゲートすること。:focus-visibleで同じ状態を表示し、prefers-reduced-motionを尊重すること(Motion:<MotionConfig reducedMotion="user">)。