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).
Use portals for overlays to avoid clipping and stacking bugs
Portals prevent modals/tooltips from being clipped by overflow and stacking contexts.
Bad example
| 1 | import { useState } from "react"; |
| 2 |
|
| 3 | export function Card() { |
| 4 | const [open, setOpen] = useState(false); |
| 5 |
|
| 6 | return ( |
| 7 | <div style={{ position: "relative", overflow: "hidden" }}> |
| 8 | <button onClick={() => setOpen(true)}>Open</button> |
| 9 |
|
| 10 | {open ? ( |
| 11 | <div style={{ position: "absolute", top: 40, right: 0, border: "1px solid #ccc" }}> |
| 12 | <button onClick={() => setOpen(false)}>Close</button> |
| 13 | <div>Popover content</div> |
| 14 | </div> |
| 15 | ) : null} |
| 16 | </div> |
| 17 | ); |
| 18 | } |
Explanation (EN)
Overlays rendered inside containers can be clipped by `overflow: hidden` or appear behind other stacking contexts. This produces flaky UI across layouts.
Objašnjenje (HR)
Overlayi renderirani unutar kontenjera mogu biti odrezani zbog `overflow: hidden` ili zavrsiti iza drugih stacking contexta. To uzrokuje flaky UI kroz razne layoute.
Good example
| 1 | import { createPortal } from "react-dom"; |
| 2 | import { useEffect, useState } from "react"; |
| 3 |
|
| 4 | function Popover({ onClose }: { onClose: () => void }) { |
| 5 | return ( |
| 6 | <div style={{ position: "fixed", top: 80, right: 20, border: "1px solid #ccc", background: "white" }}> |
| 7 | <button onClick={onClose}>Close</button> |
| 8 | <div>Popover content</div> |
| 9 | </div> |
| 10 | ); |
| 11 | } |
| 12 |
|
| 13 | export function Card() { |
| 14 | const [open, setOpen] = useState(false); |
| 15 | const [mounted, setMounted] = useState(false); |
| 16 |
|
| 17 | useEffect(function markMounted() { |
| 18 | setMounted(true); |
| 19 | }, []); |
| 20 |
|
| 21 | return ( |
| 22 | <div style={{ position: "relative", overflow: "hidden" }}> |
| 23 | <button onClick={() => setOpen(true)}>Open</button> |
| 24 |
|
| 25 | {mounted && open |
| 26 | ? createPortal(<Popover onClose={() => setOpen(false)} />, document.body) |
| 27 | : null} |
| 28 | </div> |
| 29 | ); |
| 30 | } |
Explanation (EN)
Portals render overlays outside clipping containers. The overlay becomes layout-independent and avoids stacking/overflow bugs across the app.
Objašnjenje (HR)
Portali renderiraju overlay izvan kontenjera koji reze sadrzaj. Overlay postaje neovisan o layoutu i izbjegavaju se stacking/overflow bugovi po cijeloj aplikaciji.
Notes (EN)
Portals keep React event bubbling intact. Still manage focus trapping and ESC/backdrop handling for accessibility (prefer headless UI libs for complex overlays).
Bilješke (HR)
Portali zadrzavaju React event bubbling. I dalje treba rijesiti focus trap i ESC/backdrop za accessibility (za kompleksne overlaye preferiraj headless UI libove).
Exceptions / Tradeoffs (EN)
Simple inline popovers inside unconstrained containers may not need portals, but verify overflow and z-index behavior across breakpoints.
Iznimke / Tradeoffi (HR)
Jednostavni inline popoveri u nekonteniranim kontenjerima mozda ne trebaju portale, ali provjeri overflow i z-index ponasanje kroz breakpointove.