Mobile long-press UX: making “hold” interactions feel right
2025-12-19 ・ ~4 min read
Problem: long-press shows text selection UI
- Implementing “hold” on a
buttoncan trigger selection/callout menus on mobile- You want hold to control the game, but the OS/browser shows selection UI
- This interrupts gameplay and feels broken
Fix: `user-select: none`
- Apply plain CSS
user-select: noneto controls- Prevents long-press selection menus from appearing
- Lets players focus on the game controls
Implementation Example
When using Tailwind CSS, add the select-none class.
// IconButton component example
export function IconButton({ children, ...props }: IconButtonProps) {
return (
<button
{...props}
className={`
bg-black/50 hover:bg-black/70
text-white font-bold
transition-all
select-none // ← Prevents selection menu on long-press
${className}
`}
>
{children}
</button>
);
}
When using plain CSS:
.game-button {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
Long-press Implementation Example
When implementing long-press, use onPointerDown and onPointerUp.
export function HoldIconButton({ onHoldStart, onHoldEnd, children }: Props) {
return (
<IconButton
onPointerDown={(e) => {
e.currentTarget.setPointerCapture(e.pointerId);
onHoldStart();
}}
onPointerUp={onHoldEnd}
onPointerCancel={onHoldEnd}
className="select-none" // Prevents selection on long-press
>
{children}
</IconButton>
);
}