Swipe
A row that slides aside to show what can be done to it.
On this page
A swipe is a row that slides aside to show what can be done to it. The row is a scroller with two places to rest: its content, as wide as the row, and the actions that wait behind its end edge. The browser's own scrolling does the work, so a finger, a trackpad and the Tab key all bring the actions in, and there is no script.
Swipe
<div class="sheet sheet-raised overflow-hidden w-full max-w(360)"> <div class="swipe"> <div class="swipe-content flex items-center gap-sm p(12)"> <span class="avatar">AL</span> <div class="min-w(0)"> <p class="m(0) fg-text-strong font-medium">Ada Lovelace</p> <p class="m(0) fg-text-muted">The notes on the engine are ready.</p> </div> </div> <div class="swipe-actions"> <button class="swipe-action" type="button">Archive</button> <button class="swipe-action swipe-action-danger" type="button">Delete</button> </div> </div> <div class="swipe bt(1) bt-solid b-line-hair"> <div class="swipe-content flex items-center gap-sm p(12)"> <span class="avatar">AT</span> <div class="min-w(0)"> <p class="m(0) fg-text-strong font-medium">Alan Turing</p> <p class="m(0) fg-text-muted">Can machines think? A short reply.</p> </div> </div> <div class="swipe-actions"> <button class="swipe-action" type="button">Archive</button> <button class="swipe-action swipe-action-danger" type="button">Delete</button> </div> </div> <div class="swipe bt(1) bt-solid b-line-hair"> <div class="swipe-content flex items-center gap-sm p(12)"> <span class="avatar">GH</span> <div class="min-w(0)"> <p class="m(0) fg-text-strong font-medium">Grace Hopper</p> <p class="m(0) fg-text-muted">The compiler passes every test.</p> </div> </div> <div class="swipe-actions"> <button class="swipe-action" type="button">Archive</button> <button class="swipe-action swipe-action-danger" type="button">Delete</button> </div> </div></div>#How it works
swipe is a horizontal scroller with no scrollbar and mandatory scroll snapping. swipe-content is as wide as the row and snaps at its start; swipe-actions follows it and snaps at its end. Letting go between the two settles on the nearer one, and scrolling back closes the row.
The content is opaque: it takes the color of the surface it lies on from --surface-color, which a sheet and a card publish, and casts a short shade on the well it uncovers. Where scroll-driven animations are supported, the actions fade in as they are uncovered; elsewhere they are simply there.
Nothing closes a row when another opens, and nothing removes a row when its action is pressed: both are the page's business. To close a row from script, set its scrollLeft to 0.
#Actions
An action is a button or a link: a word, or an icon over a word. Each cell is at least 72px wide and as tall as the row, so it is a full touch target at any row height. swipe-action-danger is the action that cannot be undone, in the danger ink on its own tint. Keep to one or two actions, with the destructive one last.
With icons
<div class="sheet sheet-raised overflow-hidden w-full max-w(360)"> <div class="swipe" id="swipe-open"> <div class="swipe-content flex items-center gap-sm p(16)"> <div class="min-w(0)"> <p class="m(0) fg-text-strong font-medium">Quarterly report.pdf</p> <p class="m(0) fg-text-muted">2.4 MB · Edited yesterday</p> </div> </div> <div class="swipe-actions"> <button class="swipe-action" type="button"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M12 4v11m0 0-4-4m4 4 4-4M5 19h14" /> </svg>Save</button> <button class="swipe-action swipe-action-danger" type="button"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M5 7h14M10 7V4h4v3M7 7l1 13h8l1-13" /> </svg>Delete</button> </div> </div></div><script> document.getElementById('swipe-open').scrollLeft = 999</script>#Accessibility
- Swiping is a shortcut, never the only way. A reader with a mouse and no trackpad cannot scroll a row sideways. Offer the same actions somewhere a pointer finds them: a menu on the row, or on the opened item.
- The actions are real buttons in the tab order. When one takes focus, the browser scrolls it into view, so the keyboard needs nothing else. The focus ring is drawn inside the cell, where the scroller does not cut it.
- An action that shows only an icon needs an accessible name. A word under the icon is better: these targets are found by reading.
- Confirm a destructive action, or offer to undo it in a toast.
#Classes
| Class | Description |
|---|---|
swipe | The row: a horizontal scroller with no scrollbar that rests on its content or on its actions |
swipe-content | What the row shows at rest, as wide as the row, on the color of the surface it lies on |
swipe-actions | The actions behind the end edge, in a well |
swipe-action | One action, on a button or a link: a word, or an icon over a word, in a cell at least 72px wide and as tall as the row |
swipe-action-danger | The action that cannot be undone, in the danger ink on its own tint. With swipe-action |