Easing

Rate-of-change curve of an animation.

ease-in-out

html

<div class="mot-ease-stage" data-curve="ease-in-out">
  <svg class="mot-ease-svg" viewBox="0 0 60 60" width="60" height="60" aria-hidden="true">
    <path class="mot-ease-axis" d="M4,4 L4,56 L56,56" fill="none" stroke="#24143033" stroke-width="1.5"/>
    <path class="mot-ease-curve mot-ease-curve--linear" d="M4,56 L56,4" fill="none" stroke="#D9C8F0" stroke-width="3"/>
    <path class="mot-ease-curve mot-ease-curve--ease-in" d="M4,56 C30,56 40,20 56,4" fill="none" stroke="#D9C8F0" stroke-width="3"/>
    <path class="mot-ease-curve mot-ease-curve--ease-out" d="M4,56 C4,20 30,4 56,4" fill="none" stroke="#D9C8F0" stroke-width="3"/>
    <path class="mot-ease-curve mot-ease-curve--ease-in-out" d="M4,56 C24,56 36,4 56,4" fill="none" stroke="#D9C8F0" stroke-width="3"/>
  </svg>
  <div class="mot-ease-track">
    <span class="mot-ease-dot mot-ease-dot--loop" style="background:#D9C8F0;"></span>
  </div>
  <p class="mot-ease-caption">ease-in-out</p>
</div>
<style>
.mot-ease-stage { width: 100%; max-width: 320px; padding: 16px; background: #FAF7F2; border: 1px solid #24143022; border-radius: 12px; font: 13px system-ui, sans-serif; color: #241430aa; }
.mot-ease-curve { display: none; }
[data-curve="linear"] .mot-ease-curve--linear,
[data-curve="ease-in"] .mot-ease-curve--ease-in,
[data-curve="ease-out"] .mot-ease-curve--ease-out,
[data-curve="ease-in-out"] .mot-ease-curve--ease-in-out { display: block; }
.mot-ease-track { position: relative; height: 24px; background: #24143014; border-radius: 999px; margin-top: 10px; }
.mot-ease-dot { position: absolute; top: 1px; left: 1px; width: 22px; height: 22px; border-radius: 999px; }
.mot-ease-caption { margin: 8px 0 0; text-transform: capitalize; }
@media (prefers-reduced-motion: no-preference) {
  .mot-ease-dot { animation-duration: 1400ms; animation-name: mot-ease-slide; }
  [data-curve="linear"] .mot-ease-dot { animation-timing-function: linear; }
  [data-curve="ease-in"] .mot-ease-dot { animation-timing-function: ease-in; }
  [data-curve="ease-out"] .mot-ease-dot { animation-timing-function: ease-out; }
  [data-curve="ease-in-out"] .mot-ease-dot { animation-timing-function: ease-in-out; }
  .mot-ease-dot--loop { animation-iteration-count: infinite; }
  @keyframes mot-ease-slide { 0%,100% { left: 1px; } 50% { left: calc(100% - 23px); } }
}
</style>

tailwind

<div class="w-full max-w-[320px] p-4 bg-[#FAF7F2] border border-[#24143022] rounded-xl font-sans text-sm text-[#241430]/70" data-curve="ease-in-out">
  <svg viewBox="0 0 60 60" width="60" height="60" aria-hidden="true">
    <path d="M4,4 L4,56 L56,56" fill="none" stroke="#24143033" stroke-width="1.5"/>
    <path class="tw-curve tw-curve--linear" d="M4,56 L56,4" fill="none" stroke="#D9C8F0" stroke-width="3"/>
    <path class="tw-curve tw-curve--ease-in" d="M4,56 C30,56 40,20 56,4" fill="none" stroke="#D9C8F0" stroke-width="3"/>
    <path class="tw-curve tw-curve--ease-out" d="M4,56 C4,20 30,4 56,4" fill="none" stroke="#D9C8F0" stroke-width="3"/>
    <path class="tw-curve tw-curve--ease-in-out" d="M4,56 C24,56 36,4 56,4" fill="none" stroke="#D9C8F0" stroke-width="3"/>
  </svg>
  <div class="relative h-6 bg-[#24143014] rounded-full mt-2.5">
    <span class="absolute top-px left-px w-[22px] h-[22px] rounded-full tw-ease-loop" style="background:#D9C8F0;"></span>
  </div>
  <p class="mt-2 capitalize">ease-in-out</p>
</div>
<style>
.tw-curve { display: none; }
[data-curve="linear"] .tw-curve--linear,
[data-curve="ease-in"] .tw-curve--ease-in,
[data-curve="ease-out"] .tw-curve--ease-out,
[data-curve="ease-in-out"] .tw-curve--ease-in-out { display: block; }
@media (prefers-reduced-motion: no-preference) {
  [data-curve="linear"] span { animation-timing-function: linear; }
  [data-curve="ease-in"] span { animation-timing-function: ease-in; }
  [data-curve="ease-out"] span { animation-timing-function: ease-out; }
  [data-curve="ease-in-out"] span { animation-timing-function: ease-in-out; }
  span { animation-name: mot-ease-tw-slide; animation-duration: 1400ms; }
  .tw-ease-loop { animation-iteration-count: infinite; }
  @keyframes mot-ease-tw-slide { 0%,100% { left: 1px; } 50% { left: calc(100% - 23px); } }
}
</style>

react

export function Easing({
  curve = "ease-in-out",
  duration = 1400,
  loop = true,
  accent = "#D9C8F0",
}) {
  const paths = {
    linear: "M4,56 L56,4",
    "ease-in": "M4,56 C30,56 40,20 56,4",
    "ease-out": "M4,56 C4,20 30,4 56,4",
    "ease-in-out": "M4,56 C24,56 36,4 56,4",
  };
  return (
    <div data-curve={curve} style={{ width: "100%", maxWidth: 320, padding: 16, background: "#FAF7F2", border: "1px solid #24143022", borderRadius: 12, fontFamily: "system-ui, sans-serif", fontSize: 13, color: "#241430aa" }}>
      <svg viewBox="0 0 60 60" width="60" height="60" aria-hidden="true">
        <path d="M4,4 L4,56 L56,56" fill="none" stroke="#24143033" strokeWidth="1.5" />
        <path d={paths[curve]} fill="none" stroke={accent} strokeWidth="3" />
      </svg>
      <div style={{ position: "relative", height: 24, background: "#24143014", borderRadius: 999, marginTop: 10 }}>
        <span className={loop ? "dot dot--loop" : "dot"} style={{ position: "absolute", top: 1, left: 1, width: 22, height: 22, borderRadius: 999, background: accent }} />
      </div>
      <p style={{ margin: "8px 0 0", textTransform: "capitalize" }}>{curve}</p>
      <style>{`
        @media (prefers-reduced-motion: no-preference) {
          .dot { animation: mot-ease-slide ${duration}ms ${curve} 1; }
          .dot--loop { animation-iteration-count: infinite; }
          @keyframes mot-ease-slide { 0%,100% { left: 1px; } 50% { left: calc(100% - 23px); } }
        }
      `}</style>
    </div>
  );
}