mcpField
T
to-spring-or-not-to-spring

to-spring-or-not-to-spring

github.com/raphaelsalajaデザイン・UI42271 回インストール今日更新
リポジトリ

to-spring-or-not-to-spring について

操作の種類に応じたタイミング規則でアニメーションコードを確認するスキルです。ファイルの行番号とルール ID 付きで指摘を得たい開発者向けです。

主な機能

  1. 01ジェスチャー動作のスプリング確認
  2. 02用途に応じたイージングの確認
  3. 03時間範囲と上限のチェック
  4. 04アニメーションを省く場面の特定
  5. 05ファイル・行番号・ルール ID で報告

活用シーン

  1. 01ドラッグやスワイプのアニメーション確認
  2. 02モーダルや画面遷移の曲線確認
  3. 03UI コードのアニメーション時間監査
  4. 04入力やキーボード操作時の動きを検出

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/raphaelsalaja/skill --skill to-spring-or-not-to-spring

インストール後、Claude Code で/to-spring-or-not-to-springを入力して読み込まれたか確認します。

詳細

スプリングを使うべきか、使わないべきか

インタラクションの種類に応じて、アニメーションコードで適切なタイミング関数が選択されているかを確認します。

仕組み

  1. 指定されたファイルを読み込みます(またはユーザーにファイル/パターンを尋ねます)
  2. 以下のすべてのルールに照らして確認します
  3. file:line 形式で指摘事項を出力します

ルールのカテゴリ

優先度カテゴリプレフィックス
1スプリングの選択spring-
2イージングの選択easing-
3継続時間duration-
4アニメーションなしnone-

判断の枠組み

次の点を確認します。この動きはユーザーへの反応ですか、それともシステムからの働きかけですか?

動きの種類最適な選択理由
ユーザー主導(ドラッグ、フリック、ジェスチャー)スプリング中断されても自然に続き、速度を保ちます
システム主導(状態変化、フィードバック)イージング開始と終了が明確で、タイミングが予測しやすくなります
時間の表現(進捗、読み込み)リニア時間と進捗が1対1で対応します
高頻度(タイピング、高速な切り替え)なしアニメーションはノイズになり、動作が遅く感じられます

ルール

スプリングの選択ルール
spring-for-gestures

ジェスチャー主導の動き(ドラッグ、フリック、スワイプ)には、スプリングを使用する必要があります。

不合格:

<motion.div
  drag="x"
  transition={{ duration: 0.3, ease: "easeOut" }}
/>

合格:

<motion.div
  drag="x"
  transition={{ type: "spring", stiffness: 500, damping: 30 }}
/>
spring-for-interruptible

中断される可能性のある動きには、スプリングを使用する必要があります。

不合格:

// User can click again mid-animation
<motion.div
  animate={{ x: isOpen ? 200 : 0 }}
  transition={{ duration: 0.3 }}
/>

合格:

<motion.div
  animate={{ x: isOpen ? 200 : 0 }}
  transition={{ type: "spring", stiffness: 400, damping: 25 }}
/>
spring-preserves-velocity

速度が重要な場合は、入力のエネルギーを保つためにスプリングを使用します。

不合格:

// Fast flick and slow flick animate identically
onDragEnd={(e, info) => {
  animate(target, { x: 0 }, { duration: 0.3 });
}}

合格:

// Fast flick moves faster than slow flick
onDragEnd={(e, info) => {
  animate(target, { x: 0 }, {
    type: "spring",
    velocity: info.velocity.x,
  });
}}
spring-params-balanced

スプリングのパラメーターはバランスを取る必要があります。過剰な振動は避けてください。

不合格:

transition={{
  type: "spring",
  stiffness: 1000,
  damping: 5, // Too low - excessive bounce
}}

合格:

transition={{
  type: "spring",
  stiffness: 500,
  damping: 30, // Balanced - settles quickly
}}
イージングの選択ルール
easing-for-state-change

システムが開始する状態変化には、イージングカーブを使用します。

不合格:

// Toast notification using spring
<motion.div
  animate={{ y: 0 }}
  transition={{ type: "spring" }}
/>
// Feels restless for a simple announcement

合格:

<motion.div
  animate={{ y: 0 }}
  transition={{ duration: 0.2, ease: "easeOut" }}
/>
easing-entrance-ease-out

登場時にはイーズアウトを使用します(素早く現れ、ゆっくり落ち着きます)。

不合格:

.modal-enter {
  animation-timing-function: ease-in;
}

合格:

.modal-enter {
  animation-timing-function: ease-out;
}
easing-exit-ease-in

退場時にはイーズインを使用します(離れる前に勢いを増します)。

不合格:

.modal-exit {
  animation-timing-function: ease-out;
}

合格:

.modal-exit {
  animation-timing-function: ease-in;
}
easing-transition-ease-in-out

ビュー/モードの切り替えには、注意を中立的に引きつけるイーズインアウトを使用します。

合格:

.page-transition {
  animation-timing-function: ease-in-out;
}
easing-linear-only-progress

リニアイージングは、プログレスバーと時間の表現にのみ使用します。

不合格:

.card-slide {
  transition: transform 200ms linear; /* Mechanical feel */
}

合格:

.progress-bar {
  transition: width 100ms linear; /* Honest time representation */
}
継続時間のルール
duration-press-hover

押下およびホバーのインタラクション:120~180ms。

不合格:

.button:hover {
  transition: background-color 400ms;
}

合格:

.button:hover {
  transition: background-color 150ms;
}
duration-small-state

小さな状態変化:180~260ms。

合格:

.toggle {
  transition: transform 200ms ease;
}
duration-max-300ms

ユーザーが開始するアニメーションは、300msを超えてはいけません。

不合格:

<motion.div transition={{ duration: 0.5 }} />

合格:

<motion.div transition={{ duration: 0.25 }} />
duration-shorten-before-curve

アニメーションが遅く感じられる場合は、カーブを調整する前に継続時間を短くします。

不合格(よくある間違い):

/* Trying to fix slowness with sharper curve */
.element {
  transition: 400ms cubic-bezier(0, 0.9, 0.1, 1);
}

合格:

/* Fix slowness with shorter duration */
.element {
  transition: 200ms ease-out;
}
アニメーションなしのルール
none-high-frequency

高頻度のインタラクションには、アニメーションを使用しません。

不合格:

// Animated on every keystroke
function SearchInput() {
  return (
    <motion.div animate={{ scale: [1, 1.02, 1] }}>
      <input onChange={handleSearch} />
    </motion.div>
  );
}

合格:

function SearchInput() {
  return <input onChange={handleSearch} />;
}
none-keyboard-navigation

キーボード操作によるナビゲーションは、アニメーションなしで即座に行います。

不合格:

function Menu() {
  return items.map(item => (
    <motion.li
      whileFocus={{ scale: 1.05 }}
      transition={{ duration: 0.2 }}
    />
  ));
}

合格:

function Menu() {
  return items.map(item => (
    <li className={styles.menuItem} /> // CSS :focus-visible only
  ));
}
none-context-menu-entrance

コンテキストメニューの登場時にはアニメーションを使用しません(退場時のみ)。

不合格:

<motion.div
  initial={{ opacity: 0, scale: 0.95 }}
  animate={{ opacity: 1, scale: 1 }}
  exit={{ opacity: 0 }}
/>

合格:

<motion.div exit={{ opacity: 0, scale: 0.95 }} />

出力形式

ファイルを確認するときは、指摘事項を次の形式で出力します。

file:line - [rule-id] description of issue

Example:
components/drawer/index.tsx:45 - [spring-for-gestures] Drag interaction using easing instead of spring
components/modal/styles.module.css:23 - [easing-entrance-ease-out] Modal entrance using ease-in

まとめ表

指摘事項の後に、次のまとめを出力します。

ルール件数重大度
spring-for-gestures2HIGH
easing-entrance-ease-out1MEDIUM
duration-max-300ms3MEDIUM

クイックリファレンス

インタラクションタイミング種類
ドラッグのリリーススプリングstiffness: 500, damping: 30
ボタンの押下150msease
モーダルの登場200msease-out
モーダルの退場150msease-in
ページの切り替え250msease-in-out
プログレスバー可変linear
タイピングのフィードバック0msなし

参考資料

コメント