← Easeout LabFree component
Ease-out vs linear
Same distance, same time. Ease-out starts fast and settles, so it feels responsive instead of robotic.
Linear
Ease-out
Needspnpm add motion
"use client"
import { useState } from "react"
import { motion, useReducedMotion } from "motion/react"
// Two dots cross the same track in the same time. Linear starts and stops abruptly;
// ease-out starts fast and settles, which reads as more natural.
const EASE_OUT = [0.16, 1, 0.3, 1] as const
const rows = [
{ label: "Linear", ease: "linear" as const, dot: "bg-current" },
{ label: "Ease-out", ease: EASE_OUT, dot: "bg-[#FF5A1F]" },
]
export function EaseVsLinear() {
const [end, setEnd] = useState(false)
const reduce = useReducedMotion()
return (
<div className="flex w-full max-w-xs flex-col gap-4 text-left">
{rows.map((r) => (
<div key={r.label} className="flex flex-col gap-1.5">
<span className="font-mono text-[11px] tracking-[0.06em] uppercase opacity-70">{r.label}</span>
<div className="relative h-4 rounded-full bg-current/10">
{/* the wrapper is the track minus one dot wide, so x: 100% lands the dot at the right edge */}
<motion.div
className="absolute inset-y-0 left-0 w-[calc(100%-1rem)]"
initial={false}
animate={{ x: end ? "100%" : "0%" }}
transition={reduce ? { duration: 0 } : { duration: 0.8, ease: r.ease }}
>
<span className={`block size-4 rounded-full ${r.dot}`} />
</motion.div>
</div>
</div>
))}
<button
type="button"
onClick={() => setEnd((v) => !v)}
className="mt-1 h-11 cursor-pointer self-center rounded-full border border-current/20 bg-transparent px-5 text-[14px] font-medium text-current focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
>
{end ? "Slide back" : "Slide"}
</button>
</div>
)
}