スワイプアクション Swipe Actions

リスト項目をスワイプして表示する操作ボタン。メールやチャットアプリで定番のインタラクション。

使用場面: メール一覧で左スワイプで削除、右スワイプでアーカイブなどの操作を行う場面 採用例: Apple Mail, Slack

ライブデモ

田中太郎
プロジェクトの進捗について
来週のミーティングまでに資料を準備お願いします...
佐藤花子
デザインレビューのお願い
添付のデザイン案についてご意見いただけますか...

← 左右にドラッグしてアクションを表示 →

ソースコード

<div class="demo-swipe-list">
  <div class="demo-swipe-item">
    <div class="demo-swipe-actions-behind demo-swipe-actions-behind--left">
      <button class="demo-swipe-action-btn demo-swipe-archive">アーカイブ</button>
    </div>
    <div class="demo-swipe-actions-behind demo-swipe-actions-behind--right">
      <button class="demo-swipe-action-btn demo-swipe-flag">フラグ</button>
      <button class="demo-swipe-action-btn demo-swipe-delete">削除</button>
    </div>
    <div class="demo-swipe-content" data-swipe>
      <div class="demo-swipe-sender">田中太郎</div>
      <div class="demo-swipe-subject">プロジェクトの進捗について</div>
      <div class="demo-swipe-preview">来週のミーティングまでに資料を準備お願いします...</div>
    </div>
  </div>
  <div class="demo-swipe-item">
    <div class="demo-swipe-actions-behind demo-swipe-actions-behind--left">
      <button class="demo-swipe-action-btn demo-swipe-archive">アーカイブ</button>
    </div>
    <div class="demo-swipe-actions-behind demo-swipe-actions-behind--right">
      <button class="demo-swipe-action-btn demo-swipe-flag">フラグ</button>
      <button class="demo-swipe-action-btn demo-swipe-delete">削除</button>
    </div>
    <div class="demo-swipe-content" data-swipe>
      <div class="demo-swipe-sender">佐藤花子</div>
      <div class="demo-swipe-subject">デザインレビューのお願い</div>
      <div class="demo-swipe-preview">添付のデザイン案についてご意見いただけますか...</div>
    </div>
  </div>
  <p class="demo-swipe-hint">← 左右にドラッグしてアクションを表示 →</p>
</div>
.demo-swipe-list { border: 1px solid #e5e7eb; border-radius: 10px; overflow: hidden; }
.demo-swipe-item { position: relative; border-bottom: 1px solid #e5e7eb; overflow: hidden; }
.demo-swipe-item:last-of-type { border-bottom: none; }
.demo-swipe-actions-behind {
  position: absolute; top: 0; bottom: 0; display: flex; align-items: stretch;
}
.demo-swipe-actions-behind--left { left: 0; }
.demo-swipe-actions-behind--right { right: 0; }
.demo-swipe-action-btn {
  border: none; color: #fff; font-size: 12px; font-weight: 600;
  padding: 0 20px; cursor: pointer; display: flex; align-items: center;
}
.demo-swipe-archive { background: var(--ui-primary); }
.demo-swipe-flag { background: #f59e0b; }
.demo-swipe-delete { background: #ef4444; }
.demo-swipe-content {
  position: relative; z-index: 2;
  padding: 12px 16px; background: var(--ui-bg-alt, #fff);
  transition: transform 0.3s ease;
  cursor: grab; user-select: none; -webkit-user-select: none;
}
.demo-swipe-content:active { cursor: grabbing; }
.demo-swipe-sender { font-size: 14px; font-weight: 600; color: var(--ui-text, #111); }
.demo-swipe-subject { font-size: 13px; color: var(--ui-text-sub, #444); margin-top: 2px; }
.demo-swipe-preview { font-size: 12px; color: var(--ui-text-muted, #999); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.demo-swipe-hint { text-align: center; font-size: 12px; color: var(--ui-text-muted, #aaa); padding: 10px; margin: 0; }
(function(){
  var THRESHOLD = 60, MAX_LEFT = 130, MAX_RIGHT = 80;
  document.querySelectorAll('[data-swipe]').forEach(function(el){
    var startX=0, dx=0, dragging=false, snapped=0;
    function start(x){ startX=x-snapped; dragging=true; el.style.transition='none'; }
    function move(x){
      if(!dragging) return;
      dx = x - startX;
      dx = Math.max(-MAX_LEFT, Math.min(MAX_RIGHT, dx));
      el.style.transform='translateX('+dx+'px)';
    }
    function end(){
      if(!dragging) return;
      dragging=false;
      el.style.transition='transform 0.3s ease';
      if(dx < -THRESHOLD){ snapped=-MAX_LEFT; }
      else if(dx > THRESHOLD){ snapped=MAX_RIGHT; }
      else { snapped=0; }
      el.style.transform='translateX('+snapped+'px)';
    }
    el.addEventListener('mousedown',function(e){e.preventDefault();start(e.clientX);});
    el.addEventListener('touchstart',function(e){start(e.touches[0].clientX);},{passive:true});
    document.addEventListener('mousemove',function(e){move(e.clientX);});
    document.addEventListener('touchmove',function(e){move(e.touches[0].clientX);},{passive:true});
    document.addEventListener('mouseup',end);
    document.addEventListener('touchend',end);
  });
  // アクションボタンのクリック処理
  document.querySelectorAll('.demo-swipe-action-btn').forEach(function(btn){
    btn.addEventListener('click', function(){
      var item = btn.closest('.demo-swipe-item');
      if(!item) return;
      if(btn.classList.contains('demo-swipe-delete')){
        item.style.transition='opacity 0.3s, max-height 0.3s';
        item.style.opacity='0';
        item.style.maxHeight='0';
        item.style.overflow='hidden';
        setTimeout(function(){ item.remove(); }, 300);
      } else if(btn.classList.contains('demo-swipe-archive')){
        var content = item.querySelector('[data-swipe]');
        if(content){ content.style.background='#e0f2fe'; content.style.transform='translateX(0)'; }
        setTimeout(function(){ item.style.opacity='0.5'; }, 200);
      } else if(btn.classList.contains('demo-swipe-flag')){
        var sender = item.querySelector('.demo-swipe-sender');
        if(sender) sender.style.color='var(--ui-primary)';
        var content2 = item.querySelector('[data-swipe]');
        if(content2) content2.style.transform='translateX(0)';
      }
    });
  });
})();

AIプロンプト

Basic
スワイプアクションをHTML/CSS/JSで作ってください。リスト項目を左右にスワイプすると、削除やアーカイブのボタンが表示されます。
Custom
以下の仕様でスワイプアクションを実装してください。
- メール一覧風のリストUI
- 左スワイプで「フラグ」「削除」ボタンを表示
- 右スワイプで「アーカイブ」ボタンを表示
- スワイプ量に応じた弾性アニメーション
- プライマリカラー: #2563eb
- タッチ・マウス両対応
Advanced
本格的なスワイプアクションを実装してください。
- PointerEvents APIで統一的なタッチ/マウス処理
- スワイプ速度の検知(velocity-based threshold)
- 一定以上スワイプしたら自動で全開
- 全開状態で自動実行(確認ダイアログ付き)
- 他の項目をスワイプしたら前の項目を閉じる
- 物理ベースのバウンスアニメーション
- requestAnimationFrameでの滑らかな描画