Add invisible hover buffer around the before/after demo

Ported the homepage's padding-margin trick: .split-comparison now has
20px of padding around the visible box, with a matching negative
top/bottom margin so the padding does not affect layout flow. The
pointer event listeners move from .split-container to .split-comparison
so the hover tracking engages inside the buffer and only resets when
the mouse leaves the full padded area. Percentage math still reads
.split-container.getBoundingClientRect() so the divider position stays
aligned with the visible box.

This matches how the landing-page split demo feels: graze the edge
and the divider holds; leave the box entirely and it eases back.
This commit is contained in:
Paul Bakaus
2026-04-08 11:09:05 -07:00
parent ac710d5ec1
commit 71f46ef20f
2 changed files with 28 additions and 17 deletions
+7 -2
View File
@@ -997,8 +997,13 @@ main#main {
.split-comparison {
position: relative;
width: 100%;
max-width: 500px;
margin: 0 0 clamp(2rem, 4vw, 3rem);
max-width: 540px;
/* 20px padding acts as an invisible hover buffer so the divider does
not immediately snap back when the pointer grazes the box edge.
Negative top/bottom margins collapse the padding out of layout flow
so the demo still sits at its intended rhythm in the page. */
padding: 20px;
margin: -20px 0 calc(clamp(2rem, 4vw, 3rem) - 20px);
}
.split-container {
+21 -15
View File
@@ -116,17 +116,20 @@ ${bodyHtml}
});
// Before/after split-compare: drag on touch, hover OR drag on mouse.
// On mouse-leave the divider eases back to the default position.
// Pointer events attach to the padded .split-comparison wrapper so
// there is a ~20px invisible buffer around the visible box. The
// divider only snaps back when the pointer leaves that outer buffer.
(function initSplitCompare() {
const containers = document.querySelectorAll('.split-container');
if (containers.length === 0) return;
const wrappers = document.querySelectorAll('.split-comparison');
if (wrappers.length === 0) return;
const hasHover = matchMedia('(hover: hover)').matches;
const DEFAULT_POSITION = 50;
for (const container of containers) {
const splitAfter = container.querySelector('.split-after');
const splitDivider = container.querySelector('.split-divider');
if (!splitAfter || !splitDivider) continue;
for (const wrapper of wrappers) {
const container = wrapper.querySelector('.split-container');
const splitAfter = wrapper.querySelector('.split-after');
const splitDivider = wrapper.querySelector('.split-divider');
if (!container || !splitAfter || !splitDivider) continue;
const tanAngle = Math.tan(10 * Math.PI / 180);
let skewOffset = 8;
@@ -168,6 +171,9 @@ ${bodyHtml}
paint(DEFAULT_POSITION);
// Percentage is always relative to the VISIBLE .split-container,
// not the padded .split-comparison wrapper. The pointer event
// target is the wrapper but the clip-path math uses the inner box.
const pctFromClientX = (clientX) => {
const rect = container.getBoundingClientRect();
return ((clientX - rect.left) / rect.width) * 100;
@@ -176,19 +182,19 @@ ${bodyHtml}
let hovering = false;
let dragging = false;
container.addEventListener('pointerenter', (e) => {
wrapper.addEventListener('pointerenter', (e) => {
if (hasHover && e.pointerType === 'mouse') {
hovering = true;
}
});
container.addEventListener('pointerdown', (e) => {
wrapper.addEventListener('pointerdown', (e) => {
dragging = true;
container.setPointerCapture(e.pointerId);
wrapper.setPointerCapture(e.pointerId);
setTarget(pctFromClientX(e.clientX));
});
container.addEventListener('pointermove', (e) => {
wrapper.addEventListener('pointermove', (e) => {
if (dragging || hovering) {
setTarget(pctFromClientX(e.clientX));
}
@@ -197,14 +203,14 @@ ${bodyHtml}
const endDrag = (e) => {
if (dragging) {
dragging = false;
try { container.releasePointerCapture(e.pointerId); } catch {}
try { wrapper.releasePointerCapture(e.pointerId); } catch {}
}
};
container.addEventListener('pointerup', endDrag);
container.addEventListener('pointercancel', endDrag);
wrapper.addEventListener('pointerup', endDrag);
wrapper.addEventListener('pointercancel', endDrag);
container.addEventListener('pointerleave', (e) => {
wrapper.addEventListener('pointerleave', (e) => {
endDrag(e);
if (hovering) {
hovering = false;