Pull-to-Refresh

Gesture to refresh content.

↓ pull 40px
Item one
Item two

html

<div class="rsp-pr-stage">
  <div class="rsp-pr-pull" style="height:40px;">
    
    <span class="rsp-pr-arrow">&darr; pull 40px</span>
  </div>
  <div class="rsp-pr-list"><div>Item one</div><div>Item two</div></div>
</div>
<style>
.rsp-pr-stage { width: 100%; max-width: 320px; background: #FAF7F2; border: 1px solid #24143022; border-radius: 12px; overflow: hidden; font: 12px system-ui, sans-serif; color: #241430aa; }
.rsp-pr-pull { display: flex; align-items: center; justify-content: center; background: #24143008; }
.rsp-pr-spin { width: 22px; height: 22px; border: 3px solid #24143022; border-radius: 999px; }
.rsp-pr-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; }
.rsp-pr-list div { padding: 12px; border-top: 1px solid #24143014; color: #241430; }
@media (prefers-reduced-motion: no-preference) {
  .rsp-pr-spin { animation: rsp-pr-spin 800ms linear infinite; }
  @keyframes rsp-pr-spin { to { transform: rotate(360deg); } }
}
</style>

tailwind

<div class="w-full max-w-[320px] bg-[#FAF7F2] border border-[#24143022] rounded-xl overflow-hidden font-sans text-xs text-[#241430]/70">
  <div class="flex items-center justify-center bg-[#24143008]" style="height:40px;">
    
    <span>&darr; pull 40px</span>
  </div>
  <div><div class="p-3 border-t border-[#24143014] text-[#241430]">Item one</div><div class="p-3 border-t border-[#24143014] text-[#241430]">Item two</div></div>
</div>
<style>
@media (prefers-reduced-motion: no-preference) {
  .tw-pr-spin { animation: rsp-pr-tw-spin 800ms linear infinite; }
  @keyframes rsp-pr-tw-spin { to { transform: rotate(360deg); } }
}
</style>

react

export function PullToRefresh({
  released = false,
  pullDistance = 40,
  duration = 800,
  accent = "#A8E6C1",
}) {
  return (
    <div style={{ width: "100%", maxWidth: 320, background: "#FAF7F2", border: "1px solid #24143022", borderRadius: 12, overflow: "hidden", fontFamily: "system-ui, sans-serif", fontSize: 12, color: "#241430aa" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "center", background: "#24143008", height: released ? 50 : pullDistance }}>
        {released ? (
          <span role="status" aria-live="polite" className="spin" style={{ width: 22, height: 22, border: "3px solid #24143022", borderTopColor: accent, borderRadius: 999 }}>
            <span style={{ position: "absolute", width: 1, height: 1, overflow: "hidden" }}>Refreshing&hellip;</span>
          </span>
        ) : (
          <span>&darr; pull {pullDistance}px</span>
        )}
      </div>
      <div>
        <div style={{ padding: 12, borderTop: "1px solid #24143014", color: "#241430" }}>Item one</div>
        <div style={{ padding: 12, borderTop: "1px solid #24143014", color: "#241430" }}>Item two</div>
      </div>
      <style>{`
        @media (prefers-reduced-motion: no-preference) {
          .spin { animation: rsp-pr-spin ${duration}ms linear infinite; }
          @keyframes rsp-pr-spin { to { transform: rotate(360deg); } }
        }
      `}</style>
    </div>
  );
}