Stop the kitten measuring the size it shrank itself to

The mascot shrinks a step when it yields to a card, and the overlap test read
getBoundingClientRect, which reports the scaled box. So a card resting at the
mascot's edge drove a loop: overlap, shrink, no overlap, grow, overlap again,
every poll tick.

Measure the layout box instead — centre plus offsetWidth/offsetHeight, which
the yield transform doesn't move. Hysteresis would have hidden the pulsing;
this removes the path that caused it.

Claude-Session: https://claude.ai/code/session_01GJHNvirh7Hzhc9RL3HAvz7
This commit is contained in:
prosolis
2026-07-28 21:59:20 -07:00
parent f2dd30628a
commit 047f4ae67f
+16 -1
View File
@@ -24,6 +24,21 @@ export interface CardOverlap {
modal: boolean
}
// The mascot shrinks a step when it yields (.petal-companion-faded), and
// getBoundingClientRect reports the *scaled* box. Measuring that lets the
// mascot shrink out of its own overlap test: it yields, stops overlapping,
// wakes back up, overlaps again — pulsing forever against a card that sits just
// at its edge. The layout box doesn't move when it yields, so measure that. The
// transform origin is the centre, so it's the visual centre ± the layout size.
function layoutRect(el: HTMLElement) {
const r = el.getBoundingClientRect()
const halfW = el.offsetWidth / 2
const halfH = el.offsetHeight / 2
const cx = (r.left + r.right) / 2
const cy = (r.top + r.bottom) / 2
return { left: cx - halfW, right: cx + halfW, top: cy - halfH, bottom: cy + halfH }
}
// Reports what, if anything, the mascot should yield to at the given element.
// Used to fade the corner mascot out of the way when a card or panel reaches
// into its corner, so nothing is ever hidden (or made unclickable) by the
@@ -37,7 +52,7 @@ export function useCardOverlap(ref: RefObject<HTMLElement | null>): CardOverlap
const check = () => {
const el = ref.current
if (!el) return
const r = el.getBoundingClientRect()
const r = layoutRect(el)
const hits = (selector: string) => {
for (const other of document.querySelectorAll(selector)) {
const b = other.getBoundingClientRect()