playful 250ms · premium 550ms · energetic 180ms

Motion Personality

同じ「下から登場」でも duration と overshoot の違いだけでブランドの温度感が変わることを 4 アーキタイプで比較する。

playful 250ms · premium 550ms · energetic 180ms
Playful250ms

fun · whimsical · bouncy

Hello

ease: 0.17, 0.89, 0.32, 1.27

Premium550ms

elegant · minimal · luxury

Hello

ease: 0.40, 0.00, 0.20, 1.00

Corporate300ms

clean · professional · dashboard

Hello

ease: 0.20, 0.00, 0.00, 1.00

Energetic180ms

dynamic · bold · exciting

Hello

ease: 0.05, 0.70, 0.10, 1.00

/**
 * Motion Personality — 4つの archetype(Playful / Premium / Corporate / Energetic)を
 * 同じカードで対比する。
 * 移植元: motion-playground app/_sections/PersonalityArchetypes.tsx(dark 変換 M-B-2)
 */
import { motion } from 'motion/react';
import {
  personalities,
  type PersonalityKey,
} from '../../../lib/motion-tokens/playground';
import { useLabReplay } from './use-lab-replay';

const order: PersonalityKey[] = [
  'playful',
  'premium',
  'corporate',
  'energetic',
];

function PersonalityCard({
  personalityKey,
  playKey,
}: {
  personalityKey: PersonalityKey;
  playKey: number;
}) {
  const p = personalities[personalityKey];
  const overshootScale = 1 + p.overshoot;

  return (
    <div className="rounded-xl border border-[var(--border-subtle)] bg-[var(--bg-surface)] p-5">
      <div className="mb-4 flex items-center justify-between">
        <span
          className={`inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium ring-1 ${p.accent}`}
        >
          {p.label}
        </span>
        <span className="font-mono text-xs tabular-nums text-[var(--text-muted)]">
          {Math.round(p.duration * 1000)}ms
        </span>
      </div>
      <p className="mb-4 text-xs text-[var(--text-muted)]">{p.description}</p>

      <div className="relative h-32 overflow-hidden rounded-md bg-[var(--bg-elevated)]">
        <motion.div
          key={`${personalityKey}-${playKey}`}
          initial={{ y: 40, opacity: 0, scale: 0.92 }}
          animate={{
            y: [40, 0, 0],
            opacity: [0, 1, 1],
            scale: [0.92, overshootScale, 1],
          }}
          transition={{
            duration: p.duration,
            ease: p.ease,
            times: p.overshoot > 0 ? [0, 0.7, 1] : [0, 1, 1],
          }}
          className="absolute inset-x-4 bottom-4 rounded-md bg-[var(--text-primary)] px-4 py-3 text-[var(--text-inverted)]"
        >
          <p className="text-sm font-medium">Hello</p>
          <p className="mt-0.5 text-[11px] text-[rgba(10,10,11,0.6)]">
            ease: {p.ease.map((n) => n.toFixed(2)).join(', ')}
          </p>
        </motion.div>
      </div>
    </div>
  );
}

export default function PersonalityArchetypes() {
  const { rootRef, playKey } = useLabReplay();

  return (
    <div
      ref={rootRef}
      data-lab-island
      className="grid w-full gap-4 p-4 sm:grid-cols-2 lg:grid-cols-4 lg:p-6"
    >
      {order.map((key) => (
        <PersonalityCard key={key} personalityKey={key} playKey={playKey} />
      ))}
    </div>
  );
}