Drag arrange cards by the card, not a grip handle.

Reordering the battle line meant finding a small ⠿ handle beside each
card. Now the card itself is the drag surface, with the gesture split by
input type so it can't collide with the two things a press already means:

  * A mouse lifts the card once the cursor travels 5px. Shorter presses
    stay clicks.
  * A finger has to hold still for 220ms first. Arrange is the one screen
    tall enough to scroll, and a finger swiping a card almost always
    means scrolling — so the card deliberately does NOT set
    `touch-action: none`. The hold is what distinguishes a drag, and once
    it lands the scroll is cancelled by preventing `touchmove`, which
    works precisely because a still finger hasn't started one.

That also fixes the phone's tap-to-magnify fighting the drag: only a
press that never became a drag counts as a tap, so hauling a card up the
list no longer pops the expanded card view open under your finger. A
picked-up card swells and casts a shadow, which on touch is the only
confirmation the hold registered.

The up/down arrows stay as the precise alternative and grow to 46px
(40px on phones) now that they aren't sharing space with the handle.
This commit is contained in:
Greyson Parrelli
2026-07-27 16:25:15 -04:00
parent a2024229ff
commit 8383f0f718
2 changed files with 224 additions and 101 deletions
+45 -35
View File
@@ -1876,6 +1876,15 @@ h3 {
gap: 20px;
}
/* How-to-reorder line, tucked under the ordering rule a shade quieter — the
drag gesture has no visible handle to advertise it. */
.arrange-tip {
display: block;
margin-top: 4px;
font-size: 0.85rem;
opacity: 0.78;
}
/* A vertical battle line laid into a felt tray: card 0 (fights first) sits at
the top and later pets flow downward. */
.arrange-col {
@@ -1916,9 +1925,7 @@ h3 {
justify-content: center;
}
/* The lifted card floats above its neighbors while being dragged. The z-index
sits on the inner wrapper (which carries the follow transform) so it wins
over sibling rows without disturbing the measured `.arrange-card` box. */
/* The lifted card floats above its neighbors while being dragged. */
.arrange-card.is-dragging {
z-index: 5;
}
@@ -1936,8 +1943,9 @@ h3 {
transition: transform 0.18s ease-out;
}
/* Reorder controls: the grip handle on top, the up/down arrows below. Hung to
the right of the card so the card itself stays centered under the marker. */
/* The up/down arrows, hung to the right of the card so the card itself stays
centered under the marker. They're the precise alternative to dragging, so
they're sized as comfortable targets rather than tucked away. */
.arrange-controls {
position: absolute;
left: 100%;
@@ -1947,40 +1955,41 @@ h3 {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
gap: 10px;
}
/* Grab this to drag-reorder. `touch-action: none` keeps the browser from
scrolling the page while a touch drag is in progress; the pointer handlers
in ArrangePhase do the reordering. */
.arrange-handle {
touch-action: none;
.arrange-arrow {
display: grid;
place-items: center;
width: 46px;
height: 46px;
padding: 0;
font-size: 1.15rem;
line-height: 1;
border-radius: 12px;
}
/* The card is its own drag surface — press it and move (see ArrangePhase for
how a mouse and a finger each start a drag). Deliberately no
`touch-action: none`: a finger has to hold still to pick a card up, so plain
swipes are left to scroll the page. Selection and the long-press callout are
off so the hold reads as a grab, not as text selection. */
.arrange-grab {
display: flex;
cursor: grab;
user-select: none;
-webkit-user-select: none;
display: flex;
align-items: center;
justify-content: center;
min-width: 40px;
min-height: 40px;
font-size: 1.5rem;
line-height: 1;
color: var(--gold);
opacity: 0.75;
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 10px;
background: rgba(0, 0, 0, 0.2);
-webkit-touch-callout: none;
border-radius: var(--card-radius);
transition: transform 140ms ease, box-shadow 140ms ease;
}
.arrange-handle:active {
/* Picked up: the card swells off the tray and casts a shadow over the rows
below — on touch that lift is the only confirmation the hold landed. */
.arrange-card.is-dragging .arrange-grab {
cursor: grabbing;
opacity: 1;
}
.arrange-arrows {
display: flex;
flex-direction: column;
gap: 6px;
transform: scale(1.05);
box-shadow: 0 14px 24px rgba(0, 0, 0, 0.45);
}
/* ---------- battle ---------- */
@@ -3223,11 +3232,12 @@ h3 {
}
.arrange-controls {
margin-left: 8px;
gap: 8px;
}
.arrange-handle {
min-width: 34px;
min-height: 34px;
font-size: 1.25rem;
.arrange-arrow {
width: 40px;
height: 40px;
font-size: 1rem;
}
/* --- battle: fit both sides and the clash between the rails --- */