INTERACTION TO NEXT PAINT good is ≤ 200ms

Bhook.com · apply coupon

Start typing in the box. Keep typing, and hit the red button. Then try the same thing with the green button.

this spinner is pure CSS — if it freezes, the main thread is blocked

The only difference

// 🧊 BLOCKING — one 600ms task. Browser cannot paint. Cannot accept input.
function applyCouponBad() {
  calculateDiscount(everything);   // ~600ms of work, all at once
  showResult();
}

// 🌊 YIELDING — same 600ms of work, chopped into 6 pieces.
// After each piece we hand the mic back so the browser can breathe.
async function applyCouponGood() {
  showSpinner();                                // instant feedback, ~0ms
  for (const piece of split(everything, 6)) {
    calculateDiscount(piece);                   // ~100ms
    await new Promise(r => setTimeout(r, 0));   // 👈 the whole fix
  }
  showResult();
}
Same CPU work. Same answer. Completely different experience. In React, the equivalent tools are useMemo (don't redo work), useDeferredValue / startTransition (mark work as low-priority), a Web Worker (move work off the main thread entirely), or — most often and most boringly — asking the backend to do it.