Scroll-Triggered Animation

Animation started by scroll position.

Progress fills as this section reaches the read position · fill · 1400ms

html

<div class="mot-sta-stage" data-property="fill">
  <div class="mot-sta-track"><span class="mot-sta-fill mot-sta-fill--loop" style="background:#E8A020;"></span></div>
  <p class="mot-sta-caption">Progress fills as this section reaches the read position &middot; fill &middot; 1400ms</p>
</div>
<style>
.mot-sta-stage { width: 100%; max-width: 320px; padding: 16px; background: #FAF7F2; border: 1px solid #24143022; border-radius: 12px; font: 12px system-ui, sans-serif; color: #241430aa; }
.mot-sta-track { height: 10px; background: #24143014; border-radius: 999px; overflow: hidden; }
.mot-sta-fill { display: block; height: 100%; width: 0%; border-radius: 999px; }
.mot-sta-caption { margin: 10px 0 0; }
@media (prefers-reduced-motion: no-preference) {
  [data-property="fill"] .mot-sta-fill { animation-name: mot-sta-fill; }
  [data-property="colour"] .mot-sta-fill { animation-name: mot-sta-colour; }
  [data-property="counter"] .mot-sta-fill { animation-name: mot-sta-fill; }
  .mot-sta-fill { animation-duration: 1400ms; animation-timing-function: ease-out; animation-fill-mode: both; }
  .mot-sta-fill--loop { animation-iteration-count: infinite; }
  @keyframes mot-sta-fill { from { width: 0%; } to { width: 100%; } }
  @keyframes mot-sta-colour { 0% { width: 100%; background: #241430; } 100% { width: 100%; background: #E8A020; } }
}
</style>

tailwind

<div class="w-full max-w-[320px] p-4 bg-[#FAF7F2] border border-[#24143022] rounded-xl font-sans text-xs text-[#241430]/70" data-property="fill">
  <div class="h-2.5 bg-[#24143014] rounded-full overflow-hidden"><span class="tw-sta-fill tw-sta-loop block h-full w-0 rounded-full" style="background:#E8A020;"></span></div>
  <p class="mt-2.5">Progress fills as this section reaches the read position &middot; fill &middot; 1400ms</p>
</div>
<style>
@media (prefers-reduced-motion: no-preference) {
  [data-property="fill"] .tw-sta-fill { animation-name: mot-sta-tw-fill; }
  [data-property="colour"] .tw-sta-fill { animation-name: mot-sta-tw-colour; }
  [data-property="counter"] .tw-sta-fill { animation-name: mot-sta-tw-fill; }
  .tw-sta-fill { animation-duration: 1400ms; animation-timing-function: ease-out; animation-fill-mode: both; }
  .tw-sta-loop { animation-iteration-count: infinite; }
  @keyframes mot-sta-tw-fill { from { width: 0%; } to { width: 100%; } }
  @keyframes mot-sta-tw-colour { 0% { width: 100%; background: #241430; } 100% { width: 100%; background: #E8A020; } }
}
</style>

react

export function ScrollTriggeredAnimation({
  property = "fill",
  duration = 1400,
  loop = true,
  accent = "#E8A020",
}) {
  const name = property === "colour" ? "mot-sta-colour" : "mot-sta-fill";
  return (
    <div style={{ width: "100%", maxWidth: 320, padding: 16, background: "#FAF7F2", border: "1px solid #24143022", borderRadius: 12, fontFamily: "system-ui, sans-serif", fontSize: 12, color: "#241430aa" }}>
      <div style={{ height: 10, background: "#24143014", borderRadius: 999, overflow: "hidden" }}>
        <span className="fill" style={{ display: "block", height: "100%", width: 0, borderRadius: 999, background: accent }} />
      </div>
      <p style={{ margin: "10px 0 0" }}>Progress fills as this section reaches the read position &middot; {property} &middot; {duration}ms</p>
      <style>{`
        @media (prefers-reduced-motion: no-preference) {
          .fill { animation: ${name} ${duration}ms ease-out ${loop ? "infinite" : 1} both; }
          @keyframes mot-sta-fill { from { width: 0%; } to { width: 100%; } }
          @keyframes mot-sta-colour { 0% { width: 100%; background: #241430; } 100% { width: 100%; background: #E8A020; } }
        }
      `}</style>
    </div>
  );
}