Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Drive transient UI animations with CSS, not a web of global listeners
Use a CSS animation plus onAnimationEnd (or a single scoped observer) to end transient effects instead of registering many global window listeners.
Bad example
| 1 | useEffect(() => { |
| 2 | if (!isNew) return; |
| 3 | const stop = () => document.querySelectorAll('.blink').forEach(el => el.classList.remove('blink')); |
| 4 | const events = ['mousemove', 'mousedown', 'keydown', 'scroll'] as const; |
| 5 | events.forEach(e => window.addEventListener(e, stop)); |
| 6 | return () => events.forEach(e => window.removeEventListener(e, stop)); |
| 7 | }, [isNew]); |
Explanation (EN)
Listening to four global events and mutating the DOM by class name is fragile, leaks intent across the whole window, and is hard to reason about.
Objašnjenje (HR)
Slušanje četiri globalna eventa i mijenjanje DOM-a po imenu klase je krhko, širi namjeru na cijeli prozor i teško ga je pratiti.
Good example
| 1 | // CSS |
| 2 | // .blink { animation: blink 1.4s ease-in-out 3; } |
| 3 |
|
| 4 | const [isNew, setIsNew] = useState(true); |
| 5 | return ( |
| 6 | <a |
| 7 | className={cx(styles.row, { [styles.blink]: isNew })} |
| 8 | onAnimationEnd={() => setIsNew(false)} |
| 9 | > |
| 10 | {children} |
| 11 | </a> |
| 12 | ); |
Explanation (EN)
A finite CSS animation ends itself; onAnimationEnd clears the state locally with no global listeners and no manual DOM mutation.
Objašnjenje (HR)
Konacna CSS animacija sama zavrsi; onAnimationEnd lokalno cisti stanje bez globalnih slusaca i bez rucnog mijenjanja DOM-a.
Exceptions / Tradeoffs (EN)
If the animation must persist until the user provably sees it (e.g. element enters viewport), a single scoped IntersectionObserver on the element is appropriate instead of global listeners.
Iznimke / Tradeoffi (HR)
Ako animacija mora trajati dok je korisnik dokazano ne vidi (npr. element ude u vidno polje), prikladniji je jedan IntersectionObserver vezan na element nego globalni slusaci.