Build Swipeable To-Do Items Like Gmail — A Beautiful JavaScript UI Touch

Introduction

There’s something magical about swiping. It feels natural. Smooth. Personal.

In Gmail, you swipe to archive or delete emails with satisfying motion and color. And now, you’ll learn to recreate that same interaction for your to-do app—step by step—using vanilla JavaScript and CSS only.

Let’s make your app feel alive.

Final result

What Are We Building?

We’re going to build:

  • Swipeable items (left or right)
  • A delete action with a red background
  • A check/done action with a green background
  • Mobile + desktop support
  • A reusable class you can drop into any project

No libraries. Just clean HTML, CSS, and JavaScript.

Step 1: Basic Styles for Swipeable Items

Let’s start with the visual foundation. Add this to your SwipeHandler.css file or inside <style> in your page:

.swipe-item {
  position: relative;
  background: #fff;
  padding: 20px;
  margin: 10px 0;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  transition: transform 0.3s ease;
}

This styles each swipeable item with padding, shadow, and a subtle animation for smooth swipes.

Now let’s define the backgrounds that appear when swiping.

.swipe-bg {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 60px;
  display: flex;
  align-items: center;
  padding: 0 20px;
  z-index: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease;
  color: white;
  font-size: 24px;
}

This part defines the hidden color blocks (with icons) that will appear when swiping.

.swipe-bg.visible {
  opacity: 1;
  visibility: visible;
}

Makes the background visible when swiping.

.bg-right {
  background-color: #d93025; /* Gmail red */
  justify-content: flex-start;
  left: 0;
}

.bg-left {
  background-color: #188038; /* Gmail green */
  justify-content: flex-end;
  right: 0;
}

🟥 Red background (delete) shows on the right, and 🟩 green background (done) shows on the left.

Step 2: The JavaScript Class — SwipeHandler

Now let’s create our swipe system in JavaScript. Create a file SwipeHandler.js and paste the code in chunks.

Step 2.1 – Setup and Constructor

export class SwipeHandler {
  constructor(element, config) {
    this.el = element;
    this.onSwipeLeft = config.onSwipeLeft || (() => {});
    this.onSwipeRight = config.onSwipeRight || (() => {});
    this.threshold = config.threshold || 100;

    this._addIndicators();
    this._initSwipeListeners();
  }

xplanation:
We create a class that accepts:

  • an element (.swipe-item)
  • config for actions on left/right swipe
  • a swipe threshold

Then we set up indicators and listeners.


Step 2.2 – Add Left/Right Icons

  _addIndicators() {
    this.leftBg = document.createElement("div");
    this.leftBg.className = "swipe-bg bg-left";
    this.leftBg.innerHTML = `<svg style="fill: white;" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" /></svg>`;
    this.el.appendChild(this.leftBg);

    this.rightBg = document.createElement("div");
    this.rightBg.className = "swipe-bg bg-right";
    this.rightBg.innerHTML = `<svg style="fill:white;" viewBox="0 0 24 24"><path d="M20.37,8.91L19.37,10.64L7.24,3.64L8.24,1.91L11.28,3.66L12.64,3.29L16.97,5.79L17.34,7.16L20.37,8.91M6,19V7H11.07L18,11V19A2,2 0 0,1 16,21H8A2,2 0 0,1 6,19Z" /></svg>`;
    this.el.appendChild(this.rightBg);
  }

We dynamically insert background elements with SVG icons (check & delete).

Step 2.3 – Swipe Detection

  _initSwipeListeners() {
    let startX = 0, currentX = 0, swiping = false;

    const handleStart = (e) => {
      swiping = true;
      startX = e.touches ? e.touches[0].clientX : e.clientX;
      currentX = startX;
      this.el.style.transition = "none";
      this._hideBoth();
    };

Starts the swipe, records the initial X position.

    const handleMove = (e) => {
      if (!swiping) return;
      currentX = e.touches ? e.touches[0].clientX : e.clientX;
      const diffX = currentX - startX;
      this.el.style.transform = `translateX(${diffX}px)`;

      if (diffX > 10) {
        this._showRight();
      } else if (diffX < -10) {
        this._showLeft();
      } else {
        this._hideBoth();
      }
    };

This handles the dragging motion and shows/hides background actions.

    const handleEnd = () => {
      swiping = false;

      const diffX = currentX - startX;
      this.el.style.transition = "transform 0.3s ease";
      this._hideBoth();

      if (diffX > this.threshold) {
        this._handleSwipeRight();
      } else if (diffX < -this.threshold) {
        this._handleSwipeLeft();
      } else {
        this.el.style.transform = "translateX(0)";
      }
    };
    this.el.addEventListener("touchstart", handleStart);
    this.el.addEventListener("touchmove", handleMove);
    this.el.addEventListener("touchend", handleEnd);
    this.el.addEventListener("mousedown", handleStart);
    this.el.addEventListener("mousemove", handleMove);
    this.el.addEventListener("mouseup", handleEnd);

On release, this part decides what to do: reset, delete, or mark as done.

Step 2.4 – Swipe Logic

  _handleSwipeRight() {
    this.el.style.transform = "translateX(100%)";
    this.onSwipeRight(this.el);
  }

  _handleSwipeLeft() {
    this.el.style.transform = "translateX(0)";
    this.onSwipeLeft(this.el);
  }

Custom actions are called based on swipe direction.

Step 2.5 – Visibility Control

  _hideBoth() {
    this.leftBg.classList.remove("visible");
    this.rightBg.classList.remove("visible");
  }

  _showLeft() {
    this.leftBg.classList.add("visible");
    this.rightBg.classList.remove("visible");
  }

  _showRight() {
    this.rightBg.classList.add("visible");
    this.leftBg.classList.remove("visible");
  }
}

Shows/hides backgrounds and icons based on swipe direction.

Step 3: Attach to Your To-Do Items

Assume you have this HTML structure:

<li class="swipe-item" id="todo-1">Buy groceries</li>
<li class="swipe-item" id="todo-2">Finish Techlino post</li>

And this JavaScript:

document.querySelectorAll(".swipe-item").forEach((item) => {
  new SwipeHandler(item, {
    onSwipeLeft: () => {
      const id = item.getAttribute('id');
      handleDoneTodo(id); // Your logic here
    },
    onSwipeRight: () => {
      const id = item.getAttribute('id');
      handleDeleteTodo(id); // Your logic here
    }
  });
});

Recap

  • We created swipeable items with smooth motion
  • Added action backgrounds and icons
  • Hooked into custom logic (delete/done)
  • No libraries, works on mobile and desktop

What’s Next?

This class is reusable and extendable. What would you like to build with it?

  • Swipeable product cards?
  • Swipe-to-dismiss alerts?
  • Even swipe-based navigation?

Tell me your idea in the comments below 👇
I’ll help you build it or write the next guide around it.

Leave a Comment