80 · 200 · 350 · 600 · 1200ms · md3 standard
Duration Lab
80ms から 1200ms まで duration を並べ、要素の種類ごとの体感差を比較する。tooltip は 80–120ms、page transition は 400–600ms が目安。
80ms
tooltip
micro feedback
200ms
icon transition
clear state change
350ms
card enter
spatial awareness
600ms
page transition
context switch
1200ms
dramatic reveal
theatrical
/**
* Duration Lab — 80ms から 1200ms まで、体感の違いを比較する。
* 移植元: motion-playground app/_sections/DurationLab.tsx(dark 変換 M-B-2)
*/
import { motion } from 'motion/react';
import { easings } from '../../../lib/motion-tokens/playground';
import { useLabReplay } from './use-lab-replay';
import { useTrackWidth } from './use-track-width';
const durations = [
{ ms: 80, label: 'tooltip', note: 'micro feedback' },
{ ms: 200, label: 'icon transition', note: 'clear state change' },
{ ms: 350, label: 'card enter', note: 'spatial awareness' },
{ ms: 600, label: 'page transition', note: 'context switch' },
{ ms: 1200, label: 'dramatic reveal', note: 'theatrical' },
];
function Lane({
ms,
label,
note,
playKey,
}: {
ms: number;
label: string;
note: string;
playKey: number;
}) {
const [trackRef, travel] = useTrackWidth(28, 12);
return (
<div className="flex items-center gap-4 border-b border-[var(--border-subtle)] py-3 last:border-none">
<div className="w-40 shrink-0">
<p className="font-mono text-sm font-medium text-[var(--text-primary)]">
{ms}ms
</p>
<p className="mt-0.5 text-xs text-[var(--text-muted)]">{label}</p>
<p className="text-[11px] text-[var(--text-muted)]">{note}</p>
</div>
<div
ref={trackRef}
className="relative h-10 flex-1 overflow-hidden rounded-md bg-[var(--bg-elevated)]"
>
<motion.div
key={playKey}
initial={{ x: 0 }}
animate={{ x: travel }}
transition={{ duration: ms / 1000, ease: easings.md3Standard }}
className="absolute top-1.5 left-1.5 h-7 w-7 rounded-md bg-[var(--text-primary)]"
/>
</div>
</div>
);
}
export default function DurationLab() {
const { rootRef, playKey } = useLabReplay();
return (
<div ref={rootRef} data-lab-island className="w-full p-4 lg:p-6">
<div className="rounded-lg border border-[var(--border-subtle)] bg-[var(--bg-surface)] px-4 py-2">
{durations.map((d) => (
<Lane
key={d.ms}
ms={d.ms}
label={d.label}
note={d.note}
playKey={playKey}
/>
))}
</div>
</div>
);
} .duration-lane { transition: transform var(--ms) cubic-bezier(0.2, 0, 0, 1); }
.duration-lane.play { transform: translateX(var(--travel)); }
.duration-80 { --ms: 80ms; }
.duration-200 { --ms: 200ms; }
.duration-350 { --ms: 350ms; }
.duration-600 { --ms: 600ms; }
.duration-1200 { --ms: 1200ms; } Build a duration comparison with 5 lanes:
- 80ms (tooltip) / 200ms (icon) / 350ms (card enter) / 600ms (page transition) / 1200ms (dramatic reveal)
- all lanes share md3-standard easing cubic-bezier(0.2, 0, 0, 1)
- each lane: ms value in monospace tabular-nums + label + a track with a moving block
- replay restarts all lanes simultaneously via a shared key
- simultaneous playback makes the duration differences legible at a glance