20 CSS Tags & Chips Designs

Remove with Undo

Click × to remove a chip — but a small Undo toast appears for 4 seconds before deletion is final. The pattern Gmail and Linear made standard.

Light JS MIT licensed

Remove with Undo the 15th of 20 designs in the 20 CSS Tags & Chips Designs collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.

Live preview

Open in playground

The code

<div class="ctc-undo">
  <div class="ctc-undo-list">
    <span class="ctc-undo-chip" data-ctc-undo
      >Frontend<button type="button" aria-label="Remove Frontend">×</button></span
    >
    <span class="ctc-undo-chip" data-ctc-undo
      >Backend<button type="button" aria-label="Remove Backend">×</button></span
    >
    <span class="ctc-undo-chip" data-ctc-undo
      >DevOps<button type="button" aria-label="Remove DevOps">×</button></span
    >
  </div>
  <div class="ctc-undo-toast" role="status" aria-live="polite" hidden>
    <span class="ctc-undo-msg"></span>
    <button type="button" class="ctc-undo-btn">Undo</button>
  </div>
</div>
.ctc-undo {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 70px;
}

.ctc-undo-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.ctc-undo-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 12px;
  background: #1f1f2e;
  color: #f0eeff;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 999px;
  font:
    600 12px/1 system-ui,
    sans-serif;
  transition:
    opacity 0.25s,
    transform 0.25s;
}

.ctc-undo-chip.is-removing {
  opacity: 0;
  transform: scale(0.7);
  pointer-events: none;
}

.ctc-undo-chip button {
  width: 18px;
  height: 18px;
  background: transparent;
  border: 0;
  padding: 0;
  color: #b8b6d4;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  border-radius: 50%;
}

.ctc-undo-chip button:hover {
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
}

.ctc-undo-toast {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  background: #15151d;
  border: 1px solid rgba(46, 204, 138, 0.4);
  border-radius: 8px;
  font:
    500 12px/1.2 system-ui,
    sans-serif;
  color: #c4b5fd;
  align-self: flex-start;
  animation: ctc-undo-in 0.2s ease;
}

.ctc-undo-toast[hidden] {
  display: none;
}

.ctc-undo-btn {
  background: transparent;
  border: 0;
  color: #2ecc8a;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  padding: 0;
}

.ctc-undo-btn:hover {
  text-decoration: underline;
}

@keyframes ctc-undo-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }

@media (prefers-reduced-motion: reduce) {
  .ctc-undo-toast {
    animation: none !important;
  }
}
(function () {
  document.querySelectorAll(".ctc-undo").forEach(function (root) {
    var toast = root.querySelector(".ctc-undo-toast");
    var msg = root.querySelector(".ctc-undo-msg");
    var btn = root.querySelector(".ctc-undo-btn");
    var pendingChip = null;
    var timer = null;
    function commit() {
      if (pendingChip && pendingChip.parentNode) pendingChip.parentNode.removeChild(pendingChip);
      pendingChip = null;
      toast.setAttribute("hidden", "");
      timer = null;
    }
    btn.addEventListener("click", function () {
      if (!pendingChip) return;
      pendingChip.classList.remove("is-removing");
      clearTimeout(timer);
      timer = null;
      pendingChip = null;
      toast.setAttribute("hidden", "");
    });
    root.querySelectorAll("[data-ctc-undo] button").forEach(function (x) {
      x.addEventListener("click", function () {
        if (timer) commit();
        var chip = x.closest("[data-ctc-undo]");
        chip.classList.add("is-removing");
        pendingChip = chip;
        msg.textContent = 'Removed "' + chip.firstChild.textContent.trim() + '"';
        toast.removeAttribute("hidden");
        timer = setTimeout(commit, 4000);
      });
    });
  });
})();

Search CodeFronts

Loading…