Tailwind active:scale on elements creates click dead zone on edges
Tailwind active:scale on buttons/cards creates an edge-click dead zone: hover works, click handler never fires.
Any press-scale pattern (active:scale-[0.97..0.99] on buttons, or a card recipe with :active { transform: scale(0.99) }) intermittently ignores clicks: the user sees the hover effect, presses, and nothing happens. Clicks near the center work; clicks near the border fail. No error, no event.
Mechanism: on mousedown the :active transform shrinks the element under the cursor (scale 0.98 on a 350px-wide button pulls each edge in ~3.5px; 0.99 on a 900px card, ~4.5px). By mouseup the pointer is outside the shrunken element, so the browser dispatches click on the nearest common ancestor of the mousedown/mouseup targets, and the element's handler never fires. Worst variant: 'stretched link' cards (an anchor with an ::after { inset: 0 } overlay making the whole card clickable) lose the navigation entirely, because mousedown targets the anchor and mouseup targets the card.
Reproduced with an A/B test in a real browser: identical press-and-hold click 2px inside the border fails with the scale rule injected and fires with it removed.
Two fixes, depending on whether the press-scale look is part of the design language.
A. If the scale is expendable: replace it with a press state that doesn't change geometry (background/border tint):
- active:scale-[0.98]
+ active:border-cerulean-400 active:bg-cerulean-100B. If the scale is a designed pattern (preferred): keep the transform and add a hit-area guard pseudo-element that only exists while :active, covering the pre-scale footprint so mouseup can't escape. z-index: -1 + isolate keeps the guard under nested interactive children so it never steals their clicks:
.press-scale { position: relative; isolation: isolate; }
.press-scale:active { transform: scale(var(--press-scale, 0.98)); }
.press-scale:active::after {
content: '';
position: absolute;
inset: -3%; /* covers scales down to ~0.95: need p >= 50(1-s)/s per side */
z-index: -1;
}Tune per site with [--press-scale:0.97] (Tailwind arbitrary property). The guard appears only during the press, so it can't overlap adjacent controls at rest.
Stretched-link cards need the guard on the ANCHOR's stretch, not the card — a card-level guard makes mouseup target the card and the click still retargets away from the anchor (navigation dead). Grow the stretch itself while pressed:
.card-link::after { content: ''; position: absolute; inset: 0; z-index: 1; }
.card-link:active::after { inset: -1%; } /* covers scale(0.99) */Both mousedown and mouseup then resolve to the anchor and navigation fires.
Audit tip: grep for active:scale and :active + transform. Dead-zone width per edge is dimension * (1 - factor) / 2; wide, short elements (chat chips, list rows, full-width cards) are the worst offenders.
Verification pattern (puppeteer): mouse.move(box.x + 2, box.y + box.height / 2); mouse.down(); wait 200ms; mouse.up() and assert the handler's observable effect; also assert getComputedStyle(el).transform is the scale matrix during the press to prove the design is preserved.