From d5f8c2e4803b52116508c27cc8e9034d489b9de9 Mon Sep 17 00:00:00 2001 From: cottongin Date: Wed, 26 Aug 2026 17:26:12 -0400 Subject: [PATCH] fix: stabilize popover positioning and prevent viewport overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Switch popover from position:absolute to position:fixed to avoid scroll-offset math and simplify viewport clamping. - Add max-height + overflow-y:auto so the popover scrolls internally instead of overflowing the viewport. - Remove the reposition call after picker opens — this was causing the popover to flash and jump when the picker changed its height. - Improve edge clamping: if neither above nor below fits, clamp to viewport bottom rather than flipping unpredictably. - Remove the CSS arrow (::before) which was mispositioned when the popover appeared above the badge or near edges. Co-authored-by: Cursor --- content/styles/badges.css | 26 ++++---------------------- content/tweaks/color-popover.js | 23 +++++++++++++++-------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/content/styles/badges.css b/content/styles/badges.css index 7a15f61..4d689f6 100644 --- a/content/styles/badges.css +++ b/content/styles/badges.css @@ -86,14 +86,16 @@ html.rt-enabled .thing.link .flat-list li:has(> .comments) { /* --- Color popover --- */ .rt-color-popover { - position: absolute; + position: fixed; z-index: 10000; background: #fff; border: 1px solid #ccc; border-radius: 6px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18); padding: 12px; width: 260px; + max-height: calc(100vh - 20px); + overflow-y: auto; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 13px; color: #1a1a1a; @@ -104,20 +106,6 @@ html.rt-enabled .thing.link .flat-list li:has(> .comments) { display: block; } -/* Popover arrow */ -.rt-color-popover::before { - content: ""; - position: absolute; - top: -6px; - left: 16px; - width: 10px; - height: 10px; - background: #fff; - border-left: 1px solid #ccc; - border-top: 1px solid #ccc; - transform: rotate(45deg); -} - .rt-popover-header { display: flex; justify-content: space-between; @@ -275,12 +263,6 @@ html.rt-enabled .thing.link .flat-list li:has(> .comments) { color: #e0e0e0; } -.rt-color-popover.rt-dark::before { - background: #2a2a2a; - border-left-color: #555; - border-top-color: #555; -} - .rt-color-popover.rt-dark .rt-popover-header { border-bottom-color: #444; } diff --git a/content/tweaks/color-popover.js b/content/tweaks/color-popover.js index 87837b6..862e452 100644 --- a/content/tweaks/color-popover.js +++ b/content/tweaks/color-popover.js @@ -161,7 +161,6 @@ row.hex.classList.remove("rt-input-error"); updatePreview(); }); - if (currentAnchor) positionPopover(currentAnchor); } function closePicker() { @@ -238,21 +237,29 @@ function positionPopover(anchorEl) { var rect = anchorEl.getBoundingClientRect(); var gap = 6; + var pad = 10; popoverEl.style.visibility = "hidden"; popoverEl.classList.add("rt-popover-visible"); var popRect = popoverEl.getBoundingClientRect(); - var top = rect.bottom + window.scrollY + gap; - var left = rect.left + window.scrollX; - if (rect.bottom + gap + popRect.height > window.innerHeight) { - top = rect.top + window.scrollY - popRect.height - gap; + var top = rect.bottom + gap; + var left = rect.left; + + if (top + popRect.height > window.innerHeight - pad) { + var aboveTop = rect.top - popRect.height - gap; + if (aboveTop >= pad) { + top = aboveTop; + } else { + top = Math.max(pad, window.innerHeight - popRect.height - pad); + } } - var maxLeft = document.documentElement.scrollWidth - popRect.width - 8; - if (left > maxLeft) left = maxLeft; - if (left < 8) left = 8; + if (left + popRect.width > window.innerWidth - pad) { + left = window.innerWidth - popRect.width - pad; + } + if (left < pad) left = pad; popoverEl.style.top = top + "px"; popoverEl.style.left = left + "px";