fix: mobile/touch UX for toolbar buttons and ghost tool placement

- Switch ghost tracking from mousemove to pointermove (fires for touch/pen/mouse)
- Add cancel button (✕) on ghost outline for mobile (no ESC key available)
- Center ghost on viewport for touch devices instead of (0,0)
- Add touch-action: manipulation to all toolbar buttons (eliminates 300ms tap delay)
- Bump mobile touch targets to 44px min-height with larger padding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-02 12:18:07 -08:00
parent 74b15ba1b7
commit c8e63b5c9f
1 changed files with 36 additions and 6 deletions

View File

@ -62,6 +62,7 @@
transition: background 0.2s; transition: background 0.2s;
white-space: nowrap; white-space: nowrap;
text-align: left; text-align: left;
touch-action: manipulation;
} }
.toolbar-group-toggle:hover { .toolbar-group-toggle:hover {
@ -102,6 +103,7 @@
text-align: left; text-align: left;
white-space: nowrap; white-space: nowrap;
transition: background 0.15s; transition: background 0.15s;
touch-action: manipulation;
} }
.toolbar-dropdown button:hover { .toolbar-dropdown button:hover {
@ -160,6 +162,7 @@
text-align: left; text-align: left;
white-space: nowrap; white-space: nowrap;
transition: background 0.15s; transition: background 0.15s;
touch-action: manipulation;
} }
#toolbar-panel-body button:hover { #toolbar-panel-body button:hover {
@ -196,6 +199,7 @@
transition: background 0.2s; transition: background 0.2s;
white-space: nowrap; white-space: nowrap;
text-align: left; text-align: left;
touch-action: manipulation;
} }
#toolbar > button:hover { #toolbar > button:hover {
@ -844,8 +848,9 @@
#toolbar .toolbar-group-toggle { #toolbar .toolbar-group-toggle {
width: 100%; width: 100%;
text-align: left; text-align: left;
padding: 10px 12px; padding: 12px 14px;
font-size: 13px; font-size: 14px;
min-height: 44px;
} }
#toolbar .toolbar-dropdown { #toolbar .toolbar-dropdown {
@ -856,14 +861,16 @@
} }
#toolbar .toolbar-dropdown button { #toolbar .toolbar-dropdown button {
padding: 10px 12px; padding: 12px 14px;
font-size: 13px; font-size: 14px;
min-height: 44px;
} }
#toolbar > button { #toolbar > button {
width: 100%; width: 100%;
text-align: left; text-align: left;
padding: 10px 12px; padding: 12px 14px;
min-height: 44px;
} }
#toolbar .toolbar-sep { #toolbar .toolbar-sep {
@ -2183,7 +2190,30 @@
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
transition: width 0.1s, height 0.1s; transition: width 0.1s, height 0.1s;
`; `;
const cancelBtn = document.createElement("button");
cancelBtn.textContent = "✕";
cancelBtn.style.cssText = `
position: absolute; top: -16px; right: -16px;
width: 32px; height: 32px; border-radius: 50%;
border: 2px solid #14b8a6; background: white; color: #14b8a6;
font-size: 16px; font-weight: bold; cursor: pointer;
pointer-events: auto; z-index: 10000;
display: flex; align-items: center; justify-content: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
`;
cancelBtn.addEventListener("pointerdown", (e) => {
e.preventDefault();
e.stopPropagation();
clearPendingTool();
});
ghostEl.appendChild(cancelBtn);
document.body.appendChild(ghostEl); document.body.appendChild(ghostEl);
// Center ghost for touch devices (no mousemove fires after toolbar tap)
if (window.matchMedia("(pointer: coarse)").matches) {
ghostEl.style.left = (window.innerWidth / 2) + "px";
ghostEl.style.top = (window.innerHeight / 2) + "px";
}
} }
function clearPendingTool() { function clearPendingTool() {
@ -2193,7 +2223,7 @@
} }
// Track ghost position // Track ghost position
document.addEventListener("mousemove", (e) => { document.addEventListener("pointermove", (e) => {
if (ghostEl) { if (ghostEl) {
ghostEl.style.left = e.clientX + "px"; ghostEl.style.left = e.clientX + "px";
ghostEl.style.top = e.clientY + "px"; ghostEl.style.top = e.clientY + "px";