Swipeable row Gagan
Transaction row with swipe-left gesture to reveal edit and delete actions. Uses pointer events for cross-platform gesture handling โ works on touch and mouse. Reveals 128px of action buttons. Used in the expense list to give power users a fast edit/delete path without tapping into the detail view.
Default โ closed
A
๐
Farzi Cafe โ dinner
โน2,400
Swipe left to reveal actions. Actions are hidden behind the surface until triggered.
Revealed โ swiped left
A
๐
Farzi Cafe โ dinner
โน2,400
Surface translateX(-128px). Edit action (butter) on left, delete action (coral) on right. Tapping anywhere on the surface snaps it back to closed.
In a list
A๐
Farzi Cafe โ dinner
โน2,400
R๐
Flat rent โ June
โน18,000
K๐
Swiggy order โ Kabir
โน840
Usage
Do
Only one row can be revealed at a time. When a user starts swiping a new row, close any previously revealed row before revealing the new one. This prevents the list from becoming a mess of half-swiped rows.
Don't
Don't use swipeable row in contexts where the user might be scrolling horizontally (e.g. inside a horizontal scroll container). The swipe gesture will conflict with scroll. In those contexts, use a long-press context menu instead.
Do
Use pointer events, not touch events. Pointer events fire on both touch and mouse, enabling mouse users to drag-reveal on desktop. Set
touch-action: pan-y on the swipe surface so vertical scrolling still works while the gesture handler captures horizontal movement.
Don't
Don't perform delete directly when the delete button is tapped โ show a confirmation (a bottom sheet or inline undo toast). Deletion without undo is a UX violation, especially for settled expenses that can't be recovered.
Spec
| Property | Value | Notes |
|---|---|---|
| Reveal width | 128px | Total โ 64px edit + 64px delete |
| Reveal threshold | 45% of reveal width (~58px) | Snap open if swipe exceeds threshold; snap closed if below |
| Gesture type | Pointer events | Not touch events โ works on desktop + touch |
| Surface animation | 240ms ease-out | --d-med, --ease-out |
| Direction | Left only | Right swipe has no action |
| Edit action color | --butter | Amber โ non-destructive |
| Delete action color | --coral | Red โ destructive, requires confirm |
| Close on outside tap | Yes | Tap anywhere on the surface to snap closed |
| Close on scroll | Yes | Vertical scroll should close any open row |
Accessibility
Swipe gesture alternative
Swipe is a gesture-only interaction โ not accessible to keyboard or switch users. Provide an accessible alternative: a context menu triggered by long-press or a three-dot button within the row. The swipe actions (edit, delete) must be reachable without gesture.
Action button labels
Action buttons say "edit" and "delete" โ that's not enough context when multiple rows exist. Add
aria-label: aria-label="Edit Farzi Cafe dinner" and aria-label="Delete Farzi Cafe dinner".
Code
HTML
<div class="swipe-container"> <!-- Actions revealed on swipe --> <div class="swipe-actions" aria-hidden="true"> <button class="swipe-action-edit" aria-label="Edit Farzi Cafe dinner">edit</button> <button class="swipe-action-delete" aria-label="Delete Farzi Cafe dinner">delete</button> </div> <!-- Swipe surface --> <div class="swipe-surface" style="touch-action: pan-y;"> <!-- avatar, body, price cells ... --> </div> </div>
Design tokens used
| Token | Value | Role |
|---|---|---|
--butter | #F4C94E | Edit action background |
--coral | #E76F51 | Delete action background |
--s-paper | #FBF8F0 | Row surface background |
--ink-line | #DDD2B8 | Container border |
--teal | #0E7C66 | Positive share amount |
--d-med | 240ms | Snap animation duration |
--ease-out | cubic-bezier(0.23,1,0.32,1) | Snap easing |
--r-md | 14px | Container border radius |
See it in context
Interactive prototype โ this component is live below. Tap, swipe, and drag to explore.
Swipe any transaction row to the left to reveal the edit/delete action buttons. The row rubber-bands if you release before the threshold.