animate-ui について
React と Vue のプロジェクト向けに、CLI やインポートの置き換えで既存の UI プリミティブに Motion を適用するスキルです。アニメーションの選択、移行、レビュー、操作効果の再現も案内します。
主な機能
- 01UI プリミティブへの Motion 適用
- 02shadcn/ui コンポーネント向け CLI 変換
- 03React と Vue に対応
- 04移行とアニメーションレビューの指針
- 05操作や動きのスタイルに応じた選択
活用シーン
- 01shadcn/ui コンポーネントへのアニメーション追加
- 02Radix UI などのプリミティブへの動きの適用
- 03Animate UI v1 プロジェクトの移行
- 04ぎこちない動きの確認や参考効果の再現
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ imskyleen/ animate-ui --skill animate-uiインストール後、Claude Code で/animate-uiを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
NOTICE.mdreferences/base-libraries.mdcatalog.mdfeel.mdicons.mdmigrate.mdreferences/interactions/banner.mdcount-badge.mdexpanding-card.mdfield-error.mdfilter-grid.mdform-steps.mdlist.mdREADME.mdreorder.mdskeleton.mdstatus-button.mdswipe-row.mdreferences/recipes/card.mdcss-driven.mddisclosure.mdindicator.mdmenu.mdmodal.mdoverlay.mdpage.mdpalette.mdpress.mdREADME.mdroute.md詳細
Animate UI
Animate UI は、コンポーネントの基盤となるプリミティブにアニメーションを加えます。パッケージの @animate-ui/react には、Radix UI、Base UI、React Aria の各プリミティブが、それぞれのライブラリの API をそのまま 保った状態で Motion とともに含まれているため、インポートを差し替えるだけで使えます。Vue では、@animate-ui/vue が reka-ui(shadcn-vue のプリミティブ)に対して同じことを行い、Motion for Vue(motion-v)を使います。
動きは弾むように生き生きとしていますが、一貫性もあります。ほぼすべての動きにスプリングを使い、動きの役割ごとに値は1つ(トークン)だけを定め、トゥイーンは要素の動作に応じてカーブを選びます。Emil Kowalski の実践的なルール(トリガーを起点とする変形原点、入場・退場には ease-out、押下時のフィードバック、切り替えをつなぐ軽いぼかし。NOTICE.md を参照)を取り入れつつ、厳密さは少し緩めています。スプリングは少しオーバーシュートしてもよく、ポップアップは 0.7 から開始でき、キーボード操作もポインター操作と同じようにアニメーションします。
1. 方法を選ぶ
| 依頼の内容… | 実行すること |
|---|---|
Vue または Nuxt のプロジェクト(shadcn-vue、reka-ui、.vue ファイル) | references/vue.md、次にその手順 |
shadcn/ui コンポーネント(プロジェクトに components.json がある) | A. CLI を実行する |
| Radix UI、Base UI、React Aria を使って独自に作成したコンポーネント | B. インポートを差し替える |
| カスタムのプレーンなマークアップ、別のライブラリ、または Animate UI が扱わないプリミティブ | C. ルールに沿ってアニメーションを付ける |
Animate UI v1 のプロジェクト(components/animate-ui/... ファイル) | references/migrate.md |
| すでに Animate UI を使っているアプリで、動きを速く、遅く、または穏やかにしたい | § Pace |
| 既存の動きをレビューし、遅いアニメーションやカクつくアニメーションを見つけたい | references/review.md |
| 既存の動きにある1つのルール(動きを減らす設定、ループ)を修正したい | C の手順8と該当するルールのみ |
| 再現したいアニメーション(リンク、動画、GIF、CSS または JS のスニペット) | references/reproduce.md |
| 言葉で表した動きの印象(「もっとキビキビ」「もっと柔らかく」「弾むように」)や、トークン用の CSS | references/feel.md |
| 名前で指定したインタラクション(ローディングボタン、リスト、スワイプ、バナーなど) | C、references/interactions/ から |
| 任意のライブラリまたは SVG のアイコンにアニメーションを付けたい | references/icons.md |
手作業で動きを記述する前に、references/catalog.md を確認してください。入場アニメーション、スクロール時の表示、カウンター、テキスト効果、ボタン、背景、アイコンはすでに用意されており、作り直すよりも既存のものを使うほうが効果的です。
2. インストール
パス A、B、C では @animate-ui/react と motion をインポートします(C は Motion のコードを記述する場合のみ。CSS または Tailwind のみを使うリクエストではパッケージを追加しません)。プロジェクトのロックファイルからパッケージマネージャーを判別し、それを使って追加します。pnpm-lock.yaml → pnpm add、yarn.lock → yarn add、bun.lock/bun.lockb → bun add、それ以外は npm install です。CLI も同じ方法で実行します(pnpm dlx、yarn dlx、bun x、npx。以下のコマンドでは pnpm を使います)。コンポーネントがすでに使用しているプリミティブライブラリ(radix-ui、@base-ui/react、react-aria-components)はインストールしたままにします。@animate-ui/react はその上に構築されています。CLI もインポートの置き換えも、パッケージをインストールしません。package.json にも node_modules にも @animate-ui/react がない場合、animate は警告を表示します。必要なパッケージ(@base-ui/react)が package.json にない場合、migrate は警告を表示します。インストールできない場合は、package.json の dependencies に項目を手動で追加し、出力にインストールコマンドを記載します(その他のピア依存関係はオプションです)。"@animate-ui/react": "^2.0.0" と "motion": "^14.0.0" を追加します(@animate-ui/react が必要とするバージョンです。既存の motion の指定が 14 を許容する場合は、そのままにします。"latest" と "*" も含みます)。
Tailwind のバージョンは、Motion の transform の隣でクラスを中央揃えする方法と、hover: が細かいポインターに限定されるかどうかの 2 点で重要です。package.json の tailwindcss が 4、または CSS に @import "tailwindcss" があれば v4 です。tailwind.config.* があり、tailwindcss が 3 なら v3 です。v4 でのみ使えるクラス構文(data-open: の単独バリアントや、h-(--x) のような (--var) 値)があれば、同様に v4 です。判断できない場合は、どちらでも動作する形式を使います。中央揃えにはマージンを使い(inset-0 m-auto h-fit。すでに中央揃えになっているラッパーがある場合はそのままにします)、ホバーには [@media(hover:hover)_and_(pointer:fine)]:hover: を使い、ホバー時の境界線には色を明示します(v3 では単独の border はグレー、v4 ではテキスト色になります)。
3. ゲート:動きの度合い
ゲート(C の § Gate)は、コンポーネントをどの程度動かすか、いつ止めるかを判断します。
- 明示的なリクエストには必ず応えます。 何かをアニメーションさせるよう頼まれたら、アニメーションさせます。ゲートが選ぶのは形式だけです。そのコンポーネントが一日中使われる場合は、最も短いものを選びます(⌘K で開くコマンドパレットなら、すばやいフェードとわずかな拡大にし、待たせません)。また、その理由を出力で一行にまとめます。明示的なリクエストに対して、動きを一切加えずに回答してはいけません。
- 自由度のあるリクエスト(「ページに活気を感じさせて」「動きについてどう思う?」など)では、動きを抑えたり、動きを加えることで待ち時間が増えるだけなら、動きをなくしたりしてもかまいません。すでに動いているものに対する「レビューしてから修正して」は明示的なリクエストです。適切な形式で動きを保ちます。
- レベルが指定されていない場合は、レベルを尋ねます。 A、B、移行、§ Pace、レビューでの修正では尋ねません。差し替えそのものが回答であり(リクエストで enter と exit が指定されている場合も同様です)、レビューでの修正では要素の動きを適切な形式に保ちます。リクエストのどこかにレベルや雰囲気を表す言葉があれば、褒め言葉や特定のインタラクションと一緒に出てきた場合も、それが回答になります。「Cycle」「fade」「drag to close」は効果を指定しているので、控えめな動きにし、その効果を中心にします。それ以外で、「anime ma liste」「add an animation to this button」のように、レベル、雰囲気、参考例を指定せずに何かのアニメーションを求めるリクエストには、コードを出す前に一度だけ尋ねます。どのレベルにするか、subtle、more、expressive(
references/feel.mdの § Levels)から選んでもらい、それぞれをこのコンポーネントに適用するとどうなるかを数語で説明します。ゲートが推奨するものには「推奨」と明記します。リンク、動画、「Animate UI の sheet のように」という指定も回答になります。回答できる人がいない場合(対話のない実行時)は、subtleを選び、その旨を出力で伝えます。「vivant」「calm」「snappy」のような雰囲気を表す言葉があれば、動きの度合いを表す言葉(「plus animé」「bouncy」「expressive」など)でない限り、subtle レベルでその雰囲気を設定します。レシピとカタログのデフォルトは subtle レベルで書かれています。「Nice」「nicely」「cool」だけでは褒め言葉であり、雰囲気の指定にはなりません。また、インタラクションだけ(スワイプ、並べ替え、スケルトンなど)を指定してもレベルはわからないため、尋ねます。 - 複数の経路を組み合わせたリクエスト(shadcn/ui のダイアログと手作りのリストなど)では、各コンポーネントにそれぞれの経路を適用します。レベルを尋ねる必要がある場合、質問の対象は C のコンポーネントだけです。
- A ではリクエストでコンポーネントを指定します(「all」ならすべてを実行し、
--excludeで一部を除外します)。B では依頼されたものだけが対象です。どちらも動きを短くするわけではありません。プリミティブには、それぞれのトークンが用意されています。移行(references/migrate.md)と**§ Pace**では、この手順を省略します。
A. shadcn/ui コンポーネント:CLI を実行する
pnpm add @animate-ui/react motion
pnpm dlx @animate-ui/cli@latest animate <component> --dry-run # preview
pnpm dlx @animate-ui/cli@latest animate <component> # or: animate all
<component> は ui フォルダー内のファイル名です(dropdown-menu。大文字・小文字は問わず、.tsx の有無も問いません)。複数のコンポーネントを続けて指定でき、all --exclude sidebar,calendar で一部を除外できます。CLI は components.json を使ってコンポーネントを検出し、プリミティブのインポート(radix-ui、@radix-ui/react-*、@base-ui/react[/…]、react-aria-components、vaul、input-otp、@shadcn/react/questionnaire)を @animate-ui/react に置き換え、Motion が代わりに処理する CSS アニメーション用クラスを削除します(tw-animate ユーティリティ、Base UI の data-starting-style/data-ending-style トランジション、React Aria の data-entering/data-exiting、Base UI の CSS 変数による高さ。Radix の h-(--radix-accordion-content-height) は無害なので残ります)。Animate UI がアニメーション化しない同じインポート内のパーツ(Tabs のルートや Slot など)も、ほかのパーツと一緒に移動します。@animate-ui/react がそれらを変更せずに再エクスポートします。すべての shadcn のベースとスタイルで動作し、繰り返し実行しても結果は変わらず、現時点でアニメーション化しないプリミティブも一覧表示します。CLI が行う変更を手作業で編集しないでください。出力に問題がある場合は報告してください。
Tailwind 3 では、Motion の transform によって打ち消される translate クラスの代わりに、マージンを使ってモーダルを中央に配置します(inset-0 m-auto h-fit)。バージョンは package.json、components.json、設定ファイル、またはファイル自体のクラス構文から読み取ります。意図的に残すものは、animate-none、animate-none!、origin-(--transform-origin) などです(Motion はインラインスタイルを使ってアニメーション化するため、どちらも Motion と競合せず、origin 変数は Motion が拡大する起点になります)。また、transition-colors やトリガーの transition-all といった通常の Tailwind トランジションも残します(色やホバー時のフィードバック用であり、Motion が新たに再生する入場・退場アニメーションではありません。移動するインジケーターは、選択状態の見た目を自身で複製して非表示にします)。
B. Radix UI、Base UI、React Aria を使用している場合:インポートを差し替えます
パッケージ(§ Install)を追加してから、次のように差し替えます。
import { Dialog, Slot } from 'radix-ui'; // before
import { Dialog, Slot } from '@animate-ui/react/primitives/radix-ui'; // after (re-exports all of radix-ui)
import * as DialogPrimitive from '@radix-ui/react-dialog'; // before
import * as DialogPrimitive from '@animate-ui/react/primitives/radix-ui/dialog'; // after
import { Dialog } from '@base-ui/react/dialog'; // before
import { Dialog } from '@animate-ui/react/primitives/base-ui/dialog'; // after
import { Dialog, Field } from '@base-ui/react'; // before
import { Dialog, Field } from '@animate-ui/react/primitives/base-ui'; // after (re-exports all of Base UI)
import { Popover, Button } from 'react-aria-components'; // before
import { Popover, Button } from '@animate-ui/react/primitives/react-aria'; // after (re-exports all of React Aria)
ライブラリのインポートスタイルに合わせます。包括的なインポート(radix-ui、@base-ui/react、react-aria-components)では、指定子はそのままにしてソースだけを変更します。パッケージ単位またはサブパスのインポート(@radix-ui/react-*、@base-ui/react/*、vaul → radix-ui/drawer、input-otp、@shadcn/react/questionnaire → @animate-ui/react/primitives/questionnaire)では、プリミティブ単位のパスを使います。変更を求められたのが一部分だけでも、インポート全体を移します。Animate UI がアニメーション化しないパーツは、そのまま再エクスポートされます。Animate UI がアニメーション化しない Base UI のサブパス(@base-ui/react/field)は、Base UI のままにします。
次に、コンポーネント独自の CSS アニメーションクラスを削除し、Motion と重ねて実行されないようにします。対象は、tw-animate ユーティリティ(animate-in、animate-out、fade-*、zoom-*、slide-in-from-*、slide-out-to-*、およびそれらと併用される duration-*)、Base UI の data-starting-style:*/data-ending-style:* トランジション、React Aria の data-entering:*/data-exiting:*(および entering:/exiting:)、高さのキーフレーム(animate-accordion-down/up、animate-collapsible-*。Base UI の h-(--accordion-panel-height) も対象ですが、Radix の h-(--radix-accordion-content-height) はそのまま使えます。アニメーション化されたコンテンツがこの変数を auto に設定するためです)です。それ以外はすべてそのままにします。パーツ、props、データ属性、forceMount/keepMounted、レンダープロップ、配置クラス、項目独自のハイライトまたはフォーカス時の背景(data-highlighted:bg-*、focus:bg-accent)はそのままです。滑らかに移動するハイライトがこれを担います。-translate-x-1/2 -translate-y-1/2 で中央に配置されたモーダルは、Tailwind v4 ではそのまま使えます(translate プロパティは Motion の transform と合成されます)。Tailwind v3 の場合、またはバージョンを特定できない場合(§ Install)は競合するため、transform ではなくマージンで中央に配置します。top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 を inset-0 m-auto h-fit に置き換えます(幅のクラスはそのままです)。入退場するアニメーション付きパーツ(コンテンツ、オーバーレイ、インジケーター)は、任意の props transition と exitTransition(Motion の Transition)を受け取ります。移動のみを行うパーツ(スイッチのつまみ、スライダー、プログレスバー)は transition を受け取ります。例外は Animate UI 独自のドロップイン(input-otp、@shadcn/react/questionnaire)です。これらは各パッケージの props のみを受け取り、どちらの prop も受け取りません(ただし、AnimateUIProvider の tempo と、質問票のチェック用の checkAnimation は引き続き適用されます)。QuestionnaireItem では、shadcn/ui の Animated Items の例にある tw-animate クラス(data-active:animate-in、data-active:fade-in-0、data-active:slide-in-from-bottom-2、data-active:duration-300)を削除します。これらはステップの入場アニメーションに重ねて実行されます。
ペース
すべてのアニメーションを一度に速くしたり遅くしたりするには、アプリを AnimateUIProvider(@animate-ui/react/provider。クライアントコンポーネントです。Next.js のルートレイアウトでは、<body> 内で {children} を囲むように配置します)で囲み、tempo を設定します。持続時間と遅延にはその値が乗算され、スプリングは新しい時間スケールでも形状を保ちます。モーション低減時のフェードも同様です。「少し」または程度を指定しない場合(「速くして」「遅く感じる」)は 1 段階、遅くするなら 1.15、速くするなら 0.85 です。「かなり」の場合は 1.3 または 0.7 です。再度求められた場合は、現在の値からもう 1 段階進めます(1.15 → 1.3)。0.5~2 の範囲に収めてください。プロバイダーがない場合はデフォルト(tempo 1)が適用されます。アプリ全体について「速く」または「遅く感じる」と言われた場合はこれを使います。特定のコンポーネントについての場合は feel.md を参照してください。これはすべての Animate UI プリミティブと、useTempoTransition() または useMotionTokens() を通じてトークンを読み取るコードに適用されます。手入力した値を使うアプリ独自のモーション(Motion の props、Tailwind の duration-*、CSS トランジション)には適用されません。出力では該当するファイルを挙げ、トークンを使う形に移行することを提案してください。
C. それ以外の場合:ルールに従ってアニメーションを追加します
- テクニックを把握します。 Motion のドキュメントとサンプル(motion.dev)には、ほとんどのインタラクションの構築方法が示されています。ツールで参照できる場合は、まずそこでテクニックを学び、Animate UI のトークンとルールを使って実装してください。成果物は、誰かのサンプルではなく、ユーザー自身のアニメーションとして提示します。
- まずカタログを確認します:
references/catalog.mdに該当するもの(カウンター、登場アニメーション、スクロール時の表示、テキストエフェクト、ボタンエフェクト)があれば、それを使います。Animate UI のコンポーネントで実現できる場合(単語の切り替え、タブ、コピー用ボタン、押下状態のボタン、カウンターなど)は、モーションを手作業で書くのではなく、そのコンポーネントを使い、アニメーション部分も含めて全体を使用してください。Tabs では、リストにTabsHighlightを、切り替わるコンテンツには値ごとにTabsContentを含むTabsContentsを使用します。手作業でパネル上のリストを切り替える方法は使いません。コンポーネントのレイアウトが合わない場合(Rotating Text はブロック要素です)は、置き換えるのではなく、レイアウトや周囲の props(align、inline-blockスタイルなど)を変更してください。 - ゲート(以下)を確認してから、コンポーネントを分類し、該当するレシピから始めます。オーバーレイ、メニュー、コントロール、カード、トースト、ページ、ページ遷移には、下の表にある
references/recipes/<kind>.mdを使用します。名前を指定されたインタラクション(ステータスボタン、項目を追加・削除するリスト、ドラッグによる並べ替え、ビューへ開くカード、スケルトン、カウントバッジ、エラー状態のフィールド、スワイプで削除する項目、フィルター付きグリッド、フォームのステップ、バナー)には、references/interactions/を使用します。 - Radix UI、Base UI、React Aria のオーバーレイを描画する場合は、presence を自分で再配線するより、Animate UI のプリミティブを組み合わせて使うことを優先してください。手作りのオーバーレイは、ユーザーが求めない限り手作りのままにします。アニメーションを加えることは、ライブラリに移行することを意味しません(フォーカス、モダリティ、閉じる操作が変わるためです)。
references/base-libraries.mdにあるプレーンなマークアップの例のように、そのまま Motion を組み込み、プリミティブを使うと何が追加されるかを出力で説明してください。手作りのシートに対してドラッグやスワイプを求められた場合は、移行を求められたものとして扱います。@animate-ui/react/primitives/radix-ui/drawerに移行し(Drawerをインポートし、direction、Drawer.Portal、Drawer.Overlay、Drawer.Content、Drawer.Handle、Drawer.Title、Drawer.Descriptionを備えたDrawer.Rootを使用)、不足していればradix-uiを追加し、変更点(フォーカスが内部に閉じ込められる、Escape キーで閉じる、背後のページがスクロールしなくなる)を出力で説明してください。 - Motion はクライアントコンポーネントで実行されます。Motion コードを書くファイルに
'use client'を指定し、アニメーション部分を独立したクライアントコンポーネントに移すことで、サーバーページはサーバー側のままにしてください。マウント時に表示されるコンテンツはサーバー HTML では非表示になります。この方法は、ファーストビューより下にあるコンテンツや装飾要素に限り、初回表示時のページの主要テキストには使わないでください。ファーストビューに表示される可能性の高いコンテンツをアニメーション化する明示的な依頼があった場合は、そのとおりに実装し、ハイドレーションまで非表示になることを出力で伝えてください。モーションの軽減設定や画面サイズによって異なるマークアップを使う場合は、ハイドレーション中にサーバーの値を返すフック(useShouldReduceMotion()、サーバースナップショット付きのuseSyncExternalStoreなど)で値を読み取ります。Motion のuseReducedMotion()や、レンダー中のwindow.matchMediaは使わないでください。使うと React はサーバー HTML を破棄し、ページを再レンダーします。 - ユーザーが動きの印象(「キビキビ」「落ち着いた」「もっと弾む」など)を表現している場合は、値を選ぶ前に
references/feel.mdを確認してください。Motion を使わない CSS(スタイルシート、Tailwind、サーバーコンポーネントなど)では、プロジェクトのフォルダーからnode <skill>/scripts/css-timing.mjs <token>を実行して、トークンのタイミングを出力します(このスクリプトはこのスキルに含まれており、プロジェクトの@animate-ui/reactを読み込みます)。パッケージがない場合は、tokens.mdに記載された tween トークンの値をcubic-bezierとして記述してください。 - すべての値はトークン(
references/tokens.md)から取得してください。'@animate-ui/react/utils/motion'からspring, transition, scale, blur, shift, ease, useTempoTransition, ReducedMotionConfig, useShouldReduceMotionをインポートします。useTempoTransition()を通す(またはuseMotionTokens()から読み取る)ことで、AnimateUIProviderのtempoが適用されるようにしてください。トークンが対象の役割をカバーしている場合、スプリングの値を手入力してはいけません(レシピ独自の距離とスケールはそのまま使用します)。ease-in は使わないでください。ただし、閉じたダイアログのtransition.dismissと、メニューバーで次のコンテンツがフェードインするtransition.handoffInは例外です。登場アニメーションに ease-in-out を使わないでください。アニメーション付きアイコンは例外で、値はアイコンファイル(references/icons.md)に記載されています。 references/rules.mdのすべてのルールを確認し(品質ルールは必須、スタイルルールはデフォルトです)、検証(§ Verify)を行ってください。
ゲート:アニメーションさせるべきか?
| 使用頻度 | 判断 |
|---|---|
| 常に使われる(押下、ツールチップ、メニュー、タブ、切り替え、ホバー時のハイライト) | 素早くアニメーションさせます:spring.press、transition.menu、spring.pop、spring.selection、spring.highlight。ホバー時のスケール変更はしません。 |
| ときどき使われる(ポップオーバー、選択メニュー、ダイアログ、シート、トースト、開閉パネル) | 役割に応じたトークンを使います。サーフェスには時間をかけてもかまいません(表示には spring.dialog、transition.drawer、transition.sheet、非表示には transition.dismiss、transition.drawerOut、画面端のトーストには spring.toast)。 |
| まれに使われる(エフェクト、テキスト、数値、アイコン、オンボーディング、成功) | 独自のモーションを使います。独自のスプリング、より強いバウンス、長めのリズムを使います(アイコン:icons.md)。 |
| 一日中キーボードから切り替える(コマンドパレット、グローバルショートカット) | 最短の形式にします:transition.menu に scale.menu による素早いフェードとわずかなスケール変更を組み合わせます。閉じるときも遅くせず、スタッガーも使いません。開く要求を受けたときは、即時表示のままでもかまいません。 |
キーボード操作のナビゲーションも、ポインター操作と同じようにアニメーションさせます。目的を一言で表します。フィードバック(操作への応答)、空間(どこから来て、どこへ行くか)、状態(何が変わったか)、連続性(同じものが移動したこと)、喜び(ページの表示や成功など、華やかに演出する価値のあるまれな瞬間)です。開く要求を受けたとき、当てはまる言葉がなければ、アニメーションさせないでください。
コンポーネントの種類
レシピとゲートテーブルは C 用です。B では、プリミティブにモーションが組み込まれています。
| 種類 | 例 | レシピ |
|---|---|---|
| アンカー付きオーバーレイ | ポップオーバー、ツールチップ、ホバーカード、プレビューカード | recipes/overlay.md |
| アンカー付きリスト | ドロップダウンメニュー、コンテキストメニュー、メニューバー、セレクト、コンボボックス、ナビゲーションメニュー | recipes/menu.md |
| 中央配置オーバーレイ | ダイアログ、アラートダイアログ | recipes/modal.md |
| 画面端オーバーレイ | シート、ドロワー | recipes/sheet.md |
| 開閉可能なコンテンツ | アコーディオン、折りたたみ | recipes/disclosure.md |
| 移動するインジケーター | タブ、トグルグループ、ページネーション、ナビゲーションメニューのインジケーター | recipes/indicator.md |
| フローのステップ | アンケート | recipes/steps.md |
| 押下可能な要素 | ボタン、トグル、チェックボックス、スイッチ、ラジオボタン | recipes/press.md |
| 押下可能なサーフェス | クリック可能なカード、タイル、リスト行 | recipes/card.md |
| ショートカットで開く中央配置のリスト | コマンドパレット、スポットライト検索 | recipes/palette.md |
| 画面端に積み重ねて表示する手作りのメッセージ | トースト、スナックバー、画面端に固定される通知 | recipes/toast.md |
| ページコンテンツ | ヒーロー、セクション、機能グリッド、統計情報 | recipes/page.md |
| ページまたはルートの切り替え | ページ遷移(Next.js、React Router、TanStack Router) | recipes/route.md |
| その場で切り替わる 2 つの状態 | コピー → コピー済み、太陽 → 月、変化するラベル(ルール B9) | recipes/swap.md |
| ライブラリが CSS 変数で制御するジェスチャーまたはスタック(Base UI Drawer、Toast) | recipes/css-driven.md |
ルールの概要
- モーションツリーを囲む
ReducedMotionConfig、transform文字列を無効化するuseShouldReduceMotion()、マウント時に再生するアニメーション用の<ReducedMotionConfig remount>で、アニメーションに動きの軽減設定を組み込みます。フェードやぼかしは残し、移動はなくします。 transform、不透明度、フィルターを使います(下のコンテンツを押し下げる必要がある場合に限り高さも使います)。アンカー付きポップアップは、種類に応じて0.7/0.8/0.9からトリガーを起点に広がります(ライブラリの原点変数を使用)。モーダルは中央にとどまります。- 押下時はスプリングで0.97倍に縮小します(カードや行全体の場合は0.985倍)。押下された要素はホバー時に縮小しません。ホバーでは色、背景、枠線、または影を変えます。クリップされたカードの内部では、画像をズームしたり、カードを数ピクセル浮かせたりできます。
- トゥイーンでは、入るものと出るものにイーズアウト、目で変化を追うもの(開いていくディスクロージャー、オーバーレイ、開くメニュー、スライドインするシート)にはより柔らかなイーズアウト、画面上を移動するものにはイーズインアウト、色には
easeを使います。イーズインは使いません。ただし、閉じるドロワーは短いtransition.drawerOutで端から素早く移動し、閉じるダイアログやシートは一定の押し出しでtransition.dismissに従って素早く離れ、メニューバーの次のコンテンツは、離れていくコンテンツの背後に遅い立ち上がりのtransition.handoffInでフェードインします。切り替えを軽いぼかしでつなぎます。 - 何も引き伸ばしません。
layoutでサイズ変更されるボックスでは、子要素にもlayoutを設定するか、高さ自体をアニメーションさせ、テキスト、アイコン、画像の比率を保ちます。変化するテキストはフェード、ぼかし、またはスライドで切り替え、拡大縮小はしません。表現力を高める場合は、Animate UIのテキスト(Rotating、Morphing、Rolling Text、Sliding Number)を使います。 - 退出時には入場時のアニメーションを再生します(閉じるダイアログ、ドロワー、シートはすぐに、より速く退出し、離れていきます)。そのトランジションは
exitターゲット内に設定します(退出する要素は最後のプロパティを保持します)。最初から開いている場合も、アニメーションします。 - ライブラリの要素をアニメーション対象の要素として維持します(Radixの
asChild、Base UIのrender、React Ariaのrender/motion.create)。これにより、data-*属性やスタイルが引き続き適用されます。 - 操作に慣れたユーザーには、ツールチップを即座に表示します(置き換えられるツールチップも即座に消します)。
- メニューバーのポップオーバーは、閉じて開き直すのではなく、次のメニューへ移動してサイズを変更します(
spring.morphは穏やかに始まり、行き過ぎることなくイーズインし、メニュー間で速度を保ちます)。そのコンテンツはその場で切り替わります。ポップオーバーが上をスライドする間、コンテンツは画面上の同じ位置にとどまります。前の項目は移動先からshift.handoff(6px)離れた位置へ退出し、transition.handoffOutでフェードアウトします。次の項目はそこからspring.handoffで現れ、背後でtransition.handoffInによりフェードインします。FileからEditへ移る場合、Fileの項目は左へ退出し、Editの項目は右から入ります。どのフレームでも画面上のポップオーバーは1つだけで、各メニューのコンテンツは1回だけ表示されます(メニューに戻ると、退出した項目が現在の位置から戻ります)。トリガー間を滑らかに移動するハイライトは1つだけです(internal/menu-handoff.ts、internal/item-highlight.tsx)。 - キーボード操作もポインター操作と同様にアニメーションします。ホバーは、ホバーに対応した高精度ポインターでのみ有効にします。再トリガー時はターゲットを変更し、再スタートはしません。ループは画面外では一時停止します。
検証
ブラウザーで各アニメーションの途中のフレームも確認し、停止時だけで判断しないでください。本来ラベルの上を何も通らないこと、変化の前後で周囲の単語が動かないこと、テキストのベースラインが保たれること、状態が変わるすべての部分がアニメーションすることを確認します(シェブロンが一瞬で反転するのは不合格です)。あるレイヤーから別のレイヤーへ引き渡される要素(イントロからヘッダーへ滑り出す共有のlayoutId)は、まだフェード中の要素より手前に保ちます。新しいレイヤーをフェード中のレイヤーより上にするか、そうしないとベール越しに透けて見えます(白いロゴが滑っている途中でグレーに変わるなど)。
- 1行の置き換えでも、何かに手を加える前に型チェックを実行します。その出力が変更前の状態です。
references/rules.mdのチェックリストにある品質項目をすべて満たし、スタイルの逸脱があれば明記します。- モーションを減らす設定(OS の設定、または Playwright の
emulateMedia({ reducedMotion: 'reduce' }))では、要素は引き続き表示・非表示になりますが、動きません。 - 素早く2回トリガーすると、アニメーションの目標が更新されます。キーボード操作でもポインター操作と同様にアニメーションします。位置指定と
classNameも引き続き機能します。 - DevTools の Animations を10%にして速度を落とし、開始位置、オーバーシュート、カーブを確認します。
ブラウザーを使わない場合(エージェントのシェルや CI)は、読み取れる範囲を確認します。A と B では、型チェックに新たなエラーがなく、変更前後の出力を比較し、差分がインポートの置き換え、アニメーションクラスの削除、§ B の指定に沿ったモーダルの translate による中央配置の置き換え、パッケージエントリーの追加だけであることを確認します。プリミティブには、オーバーレイを含め、表示時と非表示時のアニメーションが組み込まれています。C では、型チェックに新たなエラーがないこと、すべての値がトークンまたはレシピに由来すること、動く要素すべてにモーションを減らす設定への対応があること(その要素をReducedMotionConfigで囲み、transform の文字列を無効化する)、すべてのexitにその内部のtransitionと、その上位のAnimatePresenceがあること、width、height(開閉式 UI を除く)、top、left、marginをアニメーションさせていないことを確認します。また、ブラウザーでは実行していないことを出力に記載します。Motion を使わない CSS の場合は、feel.md末尾のチェックを行います。
出力
変更内容(CLI の実行、インポートの置き換え、またはコード)に続けて、数行で次の内容を示します。
- A, B:CLI または置き換えで変更した内容、追加するパッケージ、意図的に残したもの、変更前後の型チェック。削除対象がないことも完全な回答です。
- C:指定されたレベル(指定がなければデフォルトの subtle)、ゲートの結果(モーションが短縮またはスキップされた場合はその理由)、使用したカタログ項目またはレシピ、トークン、変更したスタイルのデフォルト値とその理由。
- Feel:選んだ言葉、適用したステップまたは行、設定したトークンまたはスプリング、維持した役割。
- Migrate:実施した手動の手順と残した手順、削除または維持したファイル。
- Reproduce:読み取った内容または測定した内容、それを置き換えたトークンまたは維持した値、参照からは分からなかったこと。
- Icon:ファイルと配置先、アイコンの機能とその意味の理由、トリガーとその理由、レベル、置き換えた使用箇所、残した元ファイル、
icons.mdの手法またはルールで適用・除外したもの。 - Review:検出事項(ファイル:行、理由、修正)、却下した指摘とその理由、修正した場合は監査の変更前後。
- Pace:設定したテンポとその場所。